whet 0.0.32 → 0.0.33
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/bin/haxe/Log.d.ts +33 -0
- package/bin/haxe/Log.js +61 -0
- package/bin/whet/Source.d.ts +10 -1
- package/bin/whet/Source.js +25 -2
- package/bin/whet/SourceHash.js +2 -9
- package/bin/whet/Stone.d.ts +27 -0
- package/bin/whet/Stone.js +13 -0
- package/bin/whet/Whet.js +1 -1
- package/bin/whet/cache/FileCache.d.ts +3 -1
- package/bin/whet/cache/FileCache.js +32 -12
- package/bin/whet/cache/HashCache.d.ts +36 -0
- package/bin/whet/cache/HashCache.js +79 -0
- package/bin/whet/route/OutputFilterMatcher.d.ts +32 -0
- package/bin/whet/route/OutputFilterMatcher.js +83 -0
- package/bin/whet/route/Router.d.ts +7 -1
- package/bin/whet/route/Router.js +73 -0
- package/bin/whet/stones/Files.d.ts +1 -0
- package/bin/whet/stones/Files.js +31 -0
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {PosInfos} from "./PosInfos"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
Log primarily provides the `trace()` method, which is invoked upon a call to
|
|
5
|
+
`trace()` in Haxe code.
|
|
6
|
+
*/
|
|
7
|
+
export declare class Log {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
Format the output of `trace` before printing it.
|
|
11
|
+
*/
|
|
12
|
+
static formatOutput(v: any, infos: PosInfos): string
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
Outputs `v` in a platform-dependent way.
|
|
16
|
+
|
|
17
|
+
The second parameter `infos` is injected by the compiler and contains
|
|
18
|
+
information about the position where the `trace()` call was made.
|
|
19
|
+
|
|
20
|
+
This method can be rebound to a custom function:
|
|
21
|
+
|
|
22
|
+
var oldTrace = haxe.Log.trace; // store old function
|
|
23
|
+
haxe.Log.trace = function(v, ?infos) {
|
|
24
|
+
// handle trace
|
|
25
|
+
}
|
|
26
|
+
...
|
|
27
|
+
haxe.Log.trace = oldTrace;
|
|
28
|
+
|
|
29
|
+
If it is bound to null, subsequent calls to `trace()` will cause an
|
|
30
|
+
exception.
|
|
31
|
+
*/
|
|
32
|
+
static trace(v: any, infos?: null | PosInfos): void
|
|
33
|
+
}
|
package/bin/haxe/Log.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {Register} from "../genes/Register.js"
|
|
2
|
+
import {Std} from "../Std.js"
|
|
3
|
+
|
|
4
|
+
const $global = Register.$global
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
Log primarily provides the `trace()` method, which is invoked upon a call to
|
|
8
|
+
`trace()` in Haxe code.
|
|
9
|
+
*/
|
|
10
|
+
export const Log = Register.global("$hxClasses")["haxe.Log"] =
|
|
11
|
+
class Log {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
Format the output of `trace` before printing it.
|
|
15
|
+
*/
|
|
16
|
+
static formatOutput(v, infos) {
|
|
17
|
+
let str = Std.string(v);
|
|
18
|
+
if (infos == null) {
|
|
19
|
+
return str;
|
|
20
|
+
};
|
|
21
|
+
let pstr = infos.fileName + ":" + infos.lineNumber;
|
|
22
|
+
if (infos.customParams != null) {
|
|
23
|
+
let _g = 0;
|
|
24
|
+
let _g1 = infos.customParams;
|
|
25
|
+
while (_g < _g1.length) str += ", " + Std.string(_g1[_g++]);
|
|
26
|
+
};
|
|
27
|
+
return pstr + ": " + str;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
Outputs `v` in a platform-dependent way.
|
|
32
|
+
|
|
33
|
+
The second parameter `infos` is injected by the compiler and contains
|
|
34
|
+
information about the position where the `trace()` call was made.
|
|
35
|
+
|
|
36
|
+
This method can be rebound to a custom function:
|
|
37
|
+
|
|
38
|
+
var oldTrace = haxe.Log.trace; // store old function
|
|
39
|
+
haxe.Log.trace = function(v, ?infos) {
|
|
40
|
+
// handle trace
|
|
41
|
+
}
|
|
42
|
+
...
|
|
43
|
+
haxe.Log.trace = oldTrace;
|
|
44
|
+
|
|
45
|
+
If it is bound to null, subsequent calls to `trace()` will cause an
|
|
46
|
+
exception.
|
|
47
|
+
*/
|
|
48
|
+
static trace(v, infos) {
|
|
49
|
+
let str = Log.formatOutput(v, infos);
|
|
50
|
+
if (typeof(console) != "undefined" && console.log != null) {
|
|
51
|
+
console.log(str);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
static get __name__() {
|
|
55
|
+
return "haxe.Log"
|
|
56
|
+
}
|
|
57
|
+
get __class__() {
|
|
58
|
+
return Log
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
package/bin/whet/Source.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare class Source {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export declare class SourceData {
|
|
28
|
-
protected constructor(id: string, data: Buffer)
|
|
28
|
+
protected constructor(id: string, data: Buffer, knownHash?: null | SourceHash)
|
|
29
29
|
data: Buffer
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -56,6 +56,15 @@ export declare class SourceData {
|
|
|
56
56
|
* @return Promise<SourceData>
|
|
57
57
|
*/
|
|
58
58
|
static fromFile(id: string, path: string, pathId: string): Promise<SourceData>
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Read file without recomputing hash. Used when mtime validation passed.
|
|
62
|
+
* @param id Path id relative to stone that generates it.
|
|
63
|
+
* @param path Actual path relative to CWD.
|
|
64
|
+
* @param pathId Path Id relative to project.
|
|
65
|
+
* @param knownHash The hash from cache (already validated via mtime).
|
|
66
|
+
*/
|
|
67
|
+
static fromFileSkipHash(id: string, path: string, pathId: string, knownHash: SourceHash): Promise<SourceData>
|
|
59
68
|
static fromString(id: string, s: string): SourceData
|
|
60
69
|
static fromBytes(id: string, data: Buffer): SourceData
|
|
61
70
|
}
|
package/bin/whet/Source.js
CHANGED
|
@@ -61,12 +61,12 @@ class Source extends Register.inherits() {
|
|
|
61
61
|
|
|
62
62
|
export const SourceData = Register.global("$hxClasses")["whet.SourceData"] =
|
|
63
63
|
class SourceData extends Register.inherits() {
|
|
64
|
-
new(id, data) {
|
|
64
|
+
new(id, data, knownHash) {
|
|
65
65
|
this.filePath = null;
|
|
66
66
|
this.filePathId = null;
|
|
67
67
|
this.data = data;
|
|
68
68
|
this.id = id;
|
|
69
|
-
this.hash = SourceHash.fromBytes(data);
|
|
69
|
+
this.hash = (knownHash != null) ? knownHash : SourceHash.fromBytes(data);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -130,6 +130,29 @@ class SourceData extends Register.inherits() {
|
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Read file without recomputing hash. Used when mtime validation passed.
|
|
136
|
+
* @param id Path id relative to stone that generates it.
|
|
137
|
+
* @param path Actual path relative to CWD.
|
|
138
|
+
* @param pathId Path Id relative to project.
|
|
139
|
+
* @param knownHash The hash from cache (already validated via mtime).
|
|
140
|
+
*/
|
|
141
|
+
static fromFileSkipHash(id, path, pathId, knownHash) {
|
|
142
|
+
return new Promise(function (res, rej) {
|
|
143
|
+
Fs.readFile(path, function (err, buffer) {
|
|
144
|
+
if (err != null) {
|
|
145
|
+
Log.log(50, ...["File does not exist.", {"id": id, "path": path, "error": err}]);
|
|
146
|
+
rej(err);
|
|
147
|
+
} else {
|
|
148
|
+
let source = new SourceData(id, buffer, knownHash);
|
|
149
|
+
source.filePath = path;
|
|
150
|
+
source.filePathId = pathId;
|
|
151
|
+
res(source);
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
133
156
|
static fromString(id, s) {
|
|
134
157
|
return SourceData.fromBytes(id, Buffer.from(s, "utf-8"));
|
|
135
158
|
}
|
package/bin/whet/SourceHash.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {Router} from "./route/Router.js"
|
|
2
2
|
import {MaybeArray_Fields_} from "./magic/MaybeArray.js"
|
|
3
|
+
import {HashCache} from "./cache/HashCache.js"
|
|
3
4
|
import {Utils} from "./Utils.js"
|
|
4
5
|
import {Stone} from "./Stone.js"
|
|
5
6
|
import {Register} from "../genes/Register.js"
|
|
@@ -25,15 +26,7 @@ class SourceHash extends Register.inherits() {
|
|
|
25
26
|
return this.bytes.toString("hex");
|
|
26
27
|
}
|
|
27
28
|
static fromFile(path) {
|
|
28
|
-
return
|
|
29
|
-
Fs.readFile(path, function (err, bytes) {
|
|
30
|
-
if (err != null) {
|
|
31
|
-
rej(err);
|
|
32
|
-
} else {
|
|
33
|
-
res(SourceHash.fromBytes(bytes));
|
|
34
|
-
};
|
|
35
|
-
});
|
|
36
|
-
});
|
|
29
|
+
return HashCache.get().getFileHash(path);
|
|
37
30
|
}
|
|
38
31
|
|
|
39
32
|
/**
|
package/bin/whet/Stone.d.ts
CHANGED
|
@@ -104,6 +104,17 @@ export declare class Stone<T extends StoneConfig> {
|
|
|
104
104
|
*/
|
|
105
105
|
list(): Promise<string[]>
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Optional filter describing what this Stone can produce.
|
|
109
|
+
* Used by Router to skip Stones that can't match a query.
|
|
110
|
+
* If null, Stone is assumed to potentially produce anything.
|
|
111
|
+
*
|
|
112
|
+
* This is a function (not a static value) because Stone outputs
|
|
113
|
+
* may depend on runtime config which can change externally.
|
|
114
|
+
* Override in subclasses to provide filtering optimization.
|
|
115
|
+
*/
|
|
116
|
+
getOutputFilter(): null | OutputFilter
|
|
117
|
+
|
|
107
118
|
/**
|
|
108
119
|
* Caches this resource under supplied `path` as a single copy.
|
|
109
120
|
* @param path
|
|
@@ -159,3 +170,19 @@ export type StoneConfig = {
|
|
|
159
170
|
}
|
|
160
171
|
|
|
161
172
|
export type AnyStone = Stone<any>
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Describes what file types/patterns a Stone can produce.
|
|
176
|
+
* Used by Router to skip Stones that can't match a query.
|
|
177
|
+
*/
|
|
178
|
+
export type OutputFilter = {
|
|
179
|
+
/**
|
|
180
|
+
* File extensions this Stone can produce (without dot). Null = any.
|
|
181
|
+
* Supports compound extensions like "png.meta.json" for metadata files.
|
|
182
|
+
*/
|
|
183
|
+
extensions?: null | string[],
|
|
184
|
+
/**
|
|
185
|
+
Glob patterns for output files. Null = matches any file with valid extension.
|
|
186
|
+
*/
|
|
187
|
+
patterns?: null | string[]
|
|
188
|
+
}
|
package/bin/whet/Stone.js
CHANGED
|
@@ -241,6 +241,19 @@ class Stone extends Register.inherits() {
|
|
|
241
241
|
});
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Optional filter describing what this Stone can produce.
|
|
246
|
+
* Used by Router to skip Stones that can't match a query.
|
|
247
|
+
* If null, Stone is assumed to potentially produce anything.
|
|
248
|
+
*
|
|
249
|
+
* This is a function (not a static value) because Stone outputs
|
|
250
|
+
* may depend on runtime config which can change externally.
|
|
251
|
+
* Override in subclasses to provide filtering optimization.
|
|
252
|
+
*/
|
|
253
|
+
getOutputFilter() {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
|
|
244
257
|
/**
|
|
245
258
|
* Caches this resource under supplied `path` as a single copy.
|
|
246
259
|
* @param path
|
package/bin/whet/Whet.js
CHANGED
|
@@ -12,7 +12,7 @@ const $global = Register.$global
|
|
|
12
12
|
export const Whet_Fields_ = Register.global("$hxClasses")["whet._Whet.Whet_Fields_"] =
|
|
13
13
|
class Whet_Fields_ {
|
|
14
14
|
static main() {
|
|
15
|
-
Whet_Fields_.program.enablePositionalOptions().passThroughOptions().description("Project tooling.").usage("[options] [command] [+ [command]...]").version("0.0.
|
|
15
|
+
Whet_Fields_.program.enablePositionalOptions().passThroughOptions().description("Project tooling.").usage("[options] [command] [+ [command]...]").version("0.0.33", "-v, --version").allowUnknownOption(true).showSuggestionAfterError(true).option("-p, --project <file>", "project to run", "Project.mjs").option("-l, --log-level <level>", "log level, a string/number", "info").option("--no-pretty", "disable pretty logging").exitOverride();
|
|
16
16
|
try {
|
|
17
17
|
Whet_Fields_.program.parse();
|
|
18
18
|
}catch (_g) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {HashCache} from "./HashCache.js"
|
|
1
2
|
import {BaseCache} from "./BaseCache.js"
|
|
2
3
|
import {Utils} from "../Utils.js"
|
|
3
4
|
import {SourceHash} from "../SourceHash.js"
|
|
@@ -41,7 +42,7 @@ class FileCache extends Register.inherits(() => BaseCache, true) {
|
|
|
41
42
|
while (_g3 < _g4.length) {
|
|
42
43
|
let file = _g4[_g3];
|
|
43
44
|
++_g3;
|
|
44
|
-
_g2.push({"fileHash": SourceHash.fromHex(file.fileHash), "filePath": file.filePath, "id": file.id});
|
|
45
|
+
_g2.push({"fileHash": SourceHash.fromHex(file.fileHash), "filePath": file.filePath, "id": file.id, "mtime": file.mtime, "size": file.size});
|
|
45
46
|
};
|
|
46
47
|
_g.push({"hash": tmp, "ctime": val1, "baseDir": val2, "files": _g2});
|
|
47
48
|
};
|
|
@@ -62,6 +63,7 @@ class FileCache extends Register.inherits(() => BaseCache, true) {
|
|
|
62
63
|
} else {
|
|
63
64
|
idOverride = null;
|
|
64
65
|
};
|
|
66
|
+
let _gthis = this;
|
|
65
67
|
let _g1 = [];
|
|
66
68
|
let _g2 = 0;
|
|
67
69
|
let _g3 = source.data;
|
|
@@ -69,7 +71,10 @@ class FileCache extends Register.inherits(() => BaseCache, true) {
|
|
|
69
71
|
let data = _g3[_g2];
|
|
70
72
|
++_g2;
|
|
71
73
|
_g1.push(data.getFilePathId(idOverride).then(function (filePath) {
|
|
72
|
-
|
|
74
|
+
let cwdPath = Path.posix.join(".", _gthis.rootDir, ".", filePath);
|
|
75
|
+
return HashCache.getStats(cwdPath).then(function (stats) {
|
|
76
|
+
return {"fileHash": SourceHash.fromBytes(data.data), "filePath": filePath, "id": data.id, "mtime": stats.mtime, "size": stats.size};
|
|
77
|
+
});
|
|
73
78
|
}));
|
|
74
79
|
};
|
|
75
80
|
return Promise.all(_g1).then(function (files) {
|
|
@@ -100,17 +105,32 @@ class FileCache extends Register.inherits(() => BaseCache, true) {
|
|
|
100
105
|
++_g2;
|
|
101
106
|
_g1.push(new Promise(function (res, rej) {
|
|
102
107
|
let path = Path.posix.join(".", _gthis.rootDir, ".", file.filePath);
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
Fs.stat(path, function (statErr, stats) {
|
|
109
|
+
if (statErr != null) {
|
|
110
|
+
if (((statErr) instanceof Error) && statErr.code == "ENOENT") {
|
|
111
|
+
rej("Invalid.");
|
|
112
|
+
} else {
|
|
113
|
+
rej(statErr);
|
|
114
|
+
};
|
|
115
|
+
return;
|
|
108
116
|
};
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
117
|
+
let mtimeMatch = file.mtime != null && stats.mtimeMs == file.mtime && (stats.size | 0) == file.size;
|
|
118
|
+
if (mtimeMatch && !stone.ignoreFileHash) {
|
|
119
|
+
SourceData.fromFileSkipHash(file.id, path, file.filePath, file.fileHash).then(res, rej);
|
|
112
120
|
} else {
|
|
113
|
-
|
|
121
|
+
SourceData.fromFile(file.id, path, file.filePath).then(function (sourceData) {
|
|
122
|
+
if (sourceData == null || !stone.ignoreFileHash && !SourceHash.equals(sourceData.hash, file.fileHash)) {
|
|
123
|
+
rej("Invalid.");
|
|
124
|
+
} else {
|
|
125
|
+
res(sourceData);
|
|
126
|
+
};
|
|
127
|
+
}, function (err) {
|
|
128
|
+
if (((err) instanceof Error) && err.code == "ENOENT") {
|
|
129
|
+
rej("Invalid.");
|
|
130
|
+
} else {
|
|
131
|
+
rej(err);
|
|
132
|
+
};
|
|
133
|
+
});
|
|
114
134
|
};
|
|
115
135
|
});
|
|
116
136
|
}));
|
|
@@ -236,7 +256,7 @@ class FileCache extends Register.inherits(() => BaseCache, true) {
|
|
|
236
256
|
while (_g3 < _g4.length) {
|
|
237
257
|
let file = _g4[_g3];
|
|
238
258
|
++_g3;
|
|
239
|
-
_g2.push({"fileHash": SourceHash.toHex(file.fileHash), "filePath": Path.posix.join(".", "./", ".", file.filePath), "id": Path.posix.join(".", "./", ".", file.id)});
|
|
259
|
+
_g2.push({"fileHash": SourceHash.toHex(file.fileHash), "filePath": Path.posix.join(".", "./", ".", file.filePath), "id": Path.posix.join(".", "./", ".", file.id), "mtime": file.mtime, "size": file.size});
|
|
240
260
|
};
|
|
241
261
|
_g.push({"hash": tmp, "ctime": val1, "ctimePretty": tmp1, "baseDir": tmp2, "files": _g2});
|
|
242
262
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {SourceHash} from "../SourceHash"
|
|
2
|
+
import {Map as Map__1} from "../../Map"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* In-memory cache for file hashes based on mtime+size.
|
|
6
|
+
* Avoids re-reading and re-hashing file contents when files haven't changed.
|
|
7
|
+
*/
|
|
8
|
+
export declare class HashCache {
|
|
9
|
+
protected constructor()
|
|
10
|
+
protected cache: Map__1<string, CachedHash>
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get hash for file, using cache if mtime/size match.
|
|
14
|
+
* Returns cached hash or computes new one.
|
|
15
|
+
*/
|
|
16
|
+
getFileHash(path: string): Promise<SourceHash>
|
|
17
|
+
protected static instance: HashCache
|
|
18
|
+
static get(): HashCache
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get file stats (mtime and size) for a path.
|
|
22
|
+
* Useful for storing alongside cache entries.
|
|
23
|
+
*/
|
|
24
|
+
static getStats(path: string): Promise<FileStats>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type CachedHash = {
|
|
28
|
+
hash: string,
|
|
29
|
+
mtime: number,
|
|
30
|
+
size: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type FileStats = {
|
|
34
|
+
mtime: number,
|
|
35
|
+
size: number
|
|
36
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {SourceHash} from "../SourceHash.js"
|
|
2
|
+
import {StringMap} from "../../haxe/ds/StringMap.js"
|
|
3
|
+
import {Register} from "../../genes/Register.js"
|
|
4
|
+
import * as Fs from "fs"
|
|
5
|
+
|
|
6
|
+
const $global = Register.$global
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* In-memory cache for file hashes based on mtime+size.
|
|
10
|
+
* Avoids re-reading and re-hashing file contents when files haven't changed.
|
|
11
|
+
*/
|
|
12
|
+
export const HashCache = Register.global("$hxClasses")["whet.cache.HashCache"] =
|
|
13
|
+
class HashCache extends Register.inherits() {
|
|
14
|
+
new() {
|
|
15
|
+
this.cache = new StringMap();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get hash for file, using cache if mtime/size match.
|
|
20
|
+
* Returns cached hash or computes new one.
|
|
21
|
+
*/
|
|
22
|
+
getFileHash(path) {
|
|
23
|
+
let _gthis = this;
|
|
24
|
+
return new Promise(function (res, rej) {
|
|
25
|
+
Fs.stat(path, function (err, stats) {
|
|
26
|
+
if (err != null) {
|
|
27
|
+
rej(err);
|
|
28
|
+
return;
|
|
29
|
+
};
|
|
30
|
+
let cached = _gthis.cache.inst.get(path);
|
|
31
|
+
let mtimeMs = stats.mtimeMs;
|
|
32
|
+
if (cached != null && cached.mtime == mtimeMs && cached.size == (stats.size | 0)) {
|
|
33
|
+
res(SourceHash.fromHex(cached.hash));
|
|
34
|
+
return;
|
|
35
|
+
};
|
|
36
|
+
Fs.readFile(path, function (err, data) {
|
|
37
|
+
if (err != null) {
|
|
38
|
+
rej(err);
|
|
39
|
+
return;
|
|
40
|
+
};
|
|
41
|
+
let hash = SourceHash.fromBytes(data);
|
|
42
|
+
let this1 = _gthis.cache;
|
|
43
|
+
let value = {"mtime": mtimeMs, "size": stats.size | 0, "hash": SourceHash.toHex(hash)};
|
|
44
|
+
this1.inst.set(path, value);
|
|
45
|
+
res(hash);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
static get() {
|
|
51
|
+
if (HashCache.instance == null) {
|
|
52
|
+
HashCache.instance = new HashCache();
|
|
53
|
+
};
|
|
54
|
+
return HashCache.instance;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get file stats (mtime and size) for a path.
|
|
59
|
+
* Useful for storing alongside cache entries.
|
|
60
|
+
*/
|
|
61
|
+
static getStats(path) {
|
|
62
|
+
return new Promise(function (res, rej) {
|
|
63
|
+
Fs.stat(path, function (err, stats) {
|
|
64
|
+
if (err != null) {
|
|
65
|
+
rej(err);
|
|
66
|
+
} else {
|
|
67
|
+
res({"mtime": stats.mtimeMs, "size": stats.size | 0});
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
static get __name__() {
|
|
73
|
+
return "whet.cache.HashCache"
|
|
74
|
+
}
|
|
75
|
+
get __class__() {
|
|
76
|
+
return HashCache
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {OutputFilter} from "../Stone"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Utility for matching queries against Stone output filters.
|
|
5
|
+
* Enables skipping entire Stone subtrees when their outputs can't match a query.
|
|
6
|
+
*/
|
|
7
|
+
export declare class OutputFilterMatcher {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check if a query could possibly match a Stone's output filter.
|
|
11
|
+
* Returns true if the Stone should be enumerated, false if it can be skipped.
|
|
12
|
+
*
|
|
13
|
+
* @param query The search pattern (often a specific file path)
|
|
14
|
+
* @param filter The Stone's output filter (null = matches anything)
|
|
15
|
+
* @param queryIsPattern True if query contains wildcards
|
|
16
|
+
*/
|
|
17
|
+
static couldMatch(query: string, filter: null | OutputFilter, queryIsPattern: boolean): boolean
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Extract extension from path, supporting compound extensions.
|
|
21
|
+
* "title.png" → "png"
|
|
22
|
+
* "title.png.meta.json" → "png.meta.json"
|
|
23
|
+
* Returns null if no extension or contains wildcard.
|
|
24
|
+
*/
|
|
25
|
+
protected static getExtension(path: string): null | string
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if query extension matches any filter extension.
|
|
29
|
+
* Handles compound extensions: query "png.meta.json" matches filter "json" or "meta.json" or "png.meta.json"
|
|
30
|
+
*/
|
|
31
|
+
protected static extensionMatches(queryExt: string, filterExts: string[]): boolean
|
|
32
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import Minimatch from "minimatch"
|
|
2
|
+
import {Register} from "../../genes/Register.js"
|
|
3
|
+
|
|
4
|
+
const $global = Register.$global
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Utility for matching queries against Stone output filters.
|
|
8
|
+
* Enables skipping entire Stone subtrees when their outputs can't match a query.
|
|
9
|
+
*/
|
|
10
|
+
export const OutputFilterMatcher = Register.global("$hxClasses")["whet.route.OutputFilterMatcher"] =
|
|
11
|
+
class OutputFilterMatcher {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if a query could possibly match a Stone's output filter.
|
|
15
|
+
* Returns true if the Stone should be enumerated, false if it can be skipped.
|
|
16
|
+
*
|
|
17
|
+
* @param query The search pattern (often a specific file path)
|
|
18
|
+
* @param filter The Stone's output filter (null = matches anything)
|
|
19
|
+
* @param queryIsPattern True if query contains wildcards
|
|
20
|
+
*/
|
|
21
|
+
static couldMatch(query, filter, queryIsPattern) {
|
|
22
|
+
if (filter == null) {
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
if (filter.extensions != null) {
|
|
26
|
+
let queryExt = OutputFilterMatcher.getExtension(query);
|
|
27
|
+
if (queryExt != null && !OutputFilterMatcher.extensionMatches(queryExt, filter.extensions)) {
|
|
28
|
+
return false;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
if (!queryIsPattern && filter.patterns != null) {
|
|
32
|
+
let _g = 0;
|
|
33
|
+
let _g1 = filter.patterns;
|
|
34
|
+
while (_g < _g1.length) if (new Minimatch.Minimatch("**/" + _g1[_g++], null).match(query)) {
|
|
35
|
+
return true;
|
|
36
|
+
};
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract extension from path, supporting compound extensions.
|
|
44
|
+
* "title.png" → "png"
|
|
45
|
+
* "title.png.meta.json" → "png.meta.json"
|
|
46
|
+
* Returns null if no extension or contains wildcard.
|
|
47
|
+
*/
|
|
48
|
+
static getExtension(path) {
|
|
49
|
+
let name = path.substring(path.lastIndexOf("/") + 1);
|
|
50
|
+
let firstDot = name.indexOf(".");
|
|
51
|
+
if (firstDot == -1 || firstDot == name.length - 1) {
|
|
52
|
+
return null;
|
|
53
|
+
};
|
|
54
|
+
let ext = name.substring(firstDot + 1).toLowerCase();
|
|
55
|
+
if (ext.indexOf("*") != -1) {
|
|
56
|
+
return null;
|
|
57
|
+
};
|
|
58
|
+
return ext;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if query extension matches any filter extension.
|
|
63
|
+
* Handles compound extensions: query "png.meta.json" matches filter "json" or "meta.json" or "png.meta.json"
|
|
64
|
+
*/
|
|
65
|
+
static extensionMatches(queryExt, filterExts) {
|
|
66
|
+
let _g = 0;
|
|
67
|
+
while (_g < filterExts.length) {
|
|
68
|
+
let filterExt = filterExts[_g];
|
|
69
|
+
++_g;
|
|
70
|
+
if (queryExt == filterExt || queryExt.endsWith("." + filterExt)) {
|
|
71
|
+
return true;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
static get __name__() {
|
|
77
|
+
return "whet.route.OutputFilterMatcher"
|
|
78
|
+
}
|
|
79
|
+
get __class__() {
|
|
80
|
+
return OutputFilterMatcher
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {RouteResult} from "./RouteResult"
|
|
2
2
|
import {RoutePathType} from "../magic/RoutePathType"
|
|
3
3
|
import {MinimatchType} from "../magic/MinimatchType"
|
|
4
|
-
import {AnyStone} from "../Stone"
|
|
4
|
+
import {OutputFilter, AnyStone} from "../Stone"
|
|
5
5
|
import {SourceHash} from "../SourceHash"
|
|
6
6
|
import Minimatch from "minimatch"
|
|
7
7
|
|
|
@@ -28,6 +28,12 @@ export declare class Router {
|
|
|
28
28
|
*/
|
|
29
29
|
saveInto(pattern: MinimatchType, saveInto: string, clearFirst?: boolean): Promise<any>
|
|
30
30
|
listContents(pattern?: null | MinimatchType): Promise<string>
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Compute combined output filter from all routes.
|
|
34
|
+
* Must be dynamic (not cached) because Stone configs can change externally.
|
|
35
|
+
*/
|
|
36
|
+
getOutputFilter(): null | OutputFilter
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
export type Filter = {
|
package/bin/whet/route/Router.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {RouteResult} from "./RouteResult.js"
|
|
2
|
+
import {OutputFilterMatcher} from "./OutputFilterMatcher.js"
|
|
2
3
|
import {RoutePathType_Fields_} from "../magic/RoutePathType.js"
|
|
3
4
|
import {MinimatchType_Fields_} from "../magic/MinimatchType.js"
|
|
4
5
|
import {Utils} from "../Utils.js"
|
|
@@ -31,6 +32,8 @@ class Router extends Register.inherits() {
|
|
|
31
32
|
let _gthis = this;
|
|
32
33
|
return new Promise(function (res, rej) {
|
|
33
34
|
let allRouteProms = [];
|
|
35
|
+
let pattern = (mainFilters.length > 0 && mainFilters[0].filter != null) ? mainFilters[0].filter.pattern : null;
|
|
36
|
+
let queryIsPattern = pattern != null && (pattern.indexOf("*") != -1 || pattern.indexOf("?") != -1);
|
|
34
37
|
let _g = 0;
|
|
35
38
|
let _g1 = _gthis.routes;
|
|
36
39
|
while (_g < _g1.length) {
|
|
@@ -127,6 +130,18 @@ class Router extends Register.inherits() {
|
|
|
127
130
|
};
|
|
128
131
|
routeFilters.push({"pathSoFar": [], "filter": f, "inProgress": true, "remDirs": []});
|
|
129
132
|
};
|
|
133
|
+
let outputFilter = null;
|
|
134
|
+
if (((route.source) instanceof Stone)) {
|
|
135
|
+
outputFilter = route.source.getOutputFilter();
|
|
136
|
+
} else if (((route.source) instanceof Router)) {
|
|
137
|
+
outputFilter = route.source.getOutputFilter();
|
|
138
|
+
};
|
|
139
|
+
if (outputFilter != null) {
|
|
140
|
+
let queryPattern = (routeFilters.length > 0 && routeFilters[0].filter != null) ? routeFilters[0].filter.pattern : null;
|
|
141
|
+
if (queryPattern != null && !OutputFilterMatcher.couldMatch(queryPattern, outputFilter, queryIsPattern)) {
|
|
142
|
+
continue;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
130
145
|
let prom;
|
|
131
146
|
if (((route.source) instanceof Stone)) {
|
|
132
147
|
let stone = route.source;
|
|
@@ -222,6 +237,15 @@ class Router extends Register.inherits() {
|
|
|
222
237
|
result[i] = uniqueStones[i].getHash();
|
|
223
238
|
};
|
|
224
239
|
return Promise.all(result).then(function (hashes) {
|
|
240
|
+
hashes.sort(function (a, b) {
|
|
241
|
+
if (a.toString() < b.toString()) {
|
|
242
|
+
return -1;
|
|
243
|
+
} else if (a.toString() > b.toString()) {
|
|
244
|
+
return 1;
|
|
245
|
+
} else {
|
|
246
|
+
return 0;
|
|
247
|
+
};
|
|
248
|
+
});
|
|
225
249
|
return SourceHash.merge(...hashes).add(SourceHash.fromString(serveIds.join("\n")));
|
|
226
250
|
});
|
|
227
251
|
});
|
|
@@ -272,6 +296,55 @@ class Router extends Register.inherits() {
|
|
|
272
296
|
return result.join("\n");
|
|
273
297
|
});
|
|
274
298
|
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Compute combined output filter from all routes.
|
|
302
|
+
* Must be dynamic (not cached) because Stone configs can change externally.
|
|
303
|
+
*/
|
|
304
|
+
getOutputFilter() {
|
|
305
|
+
let allExtensions = new Array();
|
|
306
|
+
let allPatterns = new Array();
|
|
307
|
+
let hasUnfiltered = false;
|
|
308
|
+
let _g = 0;
|
|
309
|
+
let _g1 = this.routes;
|
|
310
|
+
while (_g < _g1.length) {
|
|
311
|
+
let route = _g1[_g];
|
|
312
|
+
++_g;
|
|
313
|
+
let childFilter = null;
|
|
314
|
+
if (((route.source) instanceof Stone)) {
|
|
315
|
+
childFilter = route.source.getOutputFilter();
|
|
316
|
+
} else if (((route.source) instanceof Router)) {
|
|
317
|
+
childFilter = route.source.getOutputFilter();
|
|
318
|
+
};
|
|
319
|
+
if (childFilter == null) {
|
|
320
|
+
hasUnfiltered = true;
|
|
321
|
+
break;
|
|
322
|
+
};
|
|
323
|
+
if (childFilter.extensions != null) {
|
|
324
|
+
let _g = 0;
|
|
325
|
+
let _g1 = childFilter.extensions;
|
|
326
|
+
while (_g < _g1.length) {
|
|
327
|
+
let ext = _g1[_g];
|
|
328
|
+
++_g;
|
|
329
|
+
if (allExtensions.indexOf(ext) == -1) {
|
|
330
|
+
allExtensions.push(ext);
|
|
331
|
+
};
|
|
332
|
+
};
|
|
333
|
+
};
|
|
334
|
+
if (childFilter.patterns != null) {
|
|
335
|
+
let _g = 0;
|
|
336
|
+
let _g1 = childFilter.patterns;
|
|
337
|
+
while (_g < _g1.length) {
|
|
338
|
+
let prefixed = Path.posix.join(route.routeUnder, _g1[_g++]);
|
|
339
|
+
allPatterns.push(prefixed);
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
};
|
|
343
|
+
if (hasUnfiltered) {
|
|
344
|
+
return null;
|
|
345
|
+
};
|
|
346
|
+
return {"extensions": (allExtensions.length > 0) ? allExtensions : null, "patterns": (allPatterns.length > 0) ? allPatterns : null};
|
|
347
|
+
}
|
|
275
348
|
static get __name__() {
|
|
276
349
|
return "whet.route.Router"
|
|
277
350
|
}
|
|
@@ -9,6 +9,7 @@ import {Project} from "../Project"
|
|
|
9
9
|
export declare class Files extends Stone<FilesConfig> {
|
|
10
10
|
constructor(config: FilesConfig)
|
|
11
11
|
protected initConfig(): void
|
|
12
|
+
protected generateHash(): Promise<SourceHash>
|
|
12
13
|
list(): Promise<string[]>
|
|
13
14
|
protected generate(hash: SourceHash): Promise<SourceData[]>
|
|
14
15
|
protected walk<T>(onFile: ((arg0: string) => T), onDirFile: ((arg0: string, arg1: string) => T)): Promise<T[]>
|
package/bin/whet/stones/Files.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {MaybeArray_Fields_} from "../magic/MaybeArray.js"
|
|
2
|
+
import {HashCache} from "../cache/HashCache.js"
|
|
2
3
|
import {Utils} from "../Utils.js"
|
|
3
4
|
import {Stone} from "../Stone.js"
|
|
4
5
|
import {IdUtils, RootDir} from "../SourceId.js"
|
|
6
|
+
import {SourceHash} from "../SourceHash.js"
|
|
5
7
|
import {SourceData} from "../Source.js"
|
|
6
8
|
import {Register} from "../../genes/Register.js"
|
|
7
9
|
|
|
@@ -15,6 +17,35 @@ class Files extends Register.inherits(() => Stone, true) {
|
|
|
15
17
|
initConfig() {
|
|
16
18
|
this.config.recursive = (this.config.recursive != null) ? this.config.recursive : true;
|
|
17
19
|
}
|
|
20
|
+
generateHash() {
|
|
21
|
+
let hashCache = HashCache.get();
|
|
22
|
+
let onDirFile = function (dir, dirFile) {
|
|
23
|
+
return hashCache.getFileHash(dirFile);
|
|
24
|
+
};
|
|
25
|
+
let files = [];
|
|
26
|
+
let dirs = [];
|
|
27
|
+
let _g = 0;
|
|
28
|
+
let _g1 = MaybeArray_Fields_.makeArray(this.config.paths);
|
|
29
|
+
while (_g < _g1.length) {
|
|
30
|
+
let path = _g1[_g];
|
|
31
|
+
++_g;
|
|
32
|
+
if (path.length == 0 || path.charCodeAt(path.length - 1) == 47) {
|
|
33
|
+
dirs.push(Utils.listDirectoryFiles(this.cwdPath(path), this.config.recursive).then(function (arr) {
|
|
34
|
+
let _g = 0;
|
|
35
|
+
while (_g < arr.length) files.push(onDirFile(path, arr[_g++]));
|
|
36
|
+
}));
|
|
37
|
+
} else {
|
|
38
|
+
files.push(hashCache.getFileHash(this.cwdPath(path)));
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
return Promise.all(dirs).then(function (_) {
|
|
42
|
+
return files;
|
|
43
|
+
}).then(function (hashProms) {
|
|
44
|
+
return Promise.all(hashProms);
|
|
45
|
+
}).then(function (hashes) {
|
|
46
|
+
return SourceHash.merge(...hashes);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
18
49
|
list() {
|
|
19
50
|
let _gthis = this;
|
|
20
51
|
let onDirFile = function (dir, dirFile) {
|