whet 0.5.0 → 0.6.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.
- package/bin/whet/ConfigStore.js +6 -8
- package/bin/whet/Log.js +1 -1
- package/bin/whet/Stone.d.ts +41 -2
- package/bin/whet/Stone.js +106 -11
- package/bin/whet/Whet.d.ts +1 -1
- package/bin/whet/Whet.js +28 -9
- package/bin/whet/cache/BaseCache.d.ts +26 -0
- package/bin/whet/cache/BaseCache.js +49 -11
- package/bin/whet/cache/CacheManager.d.ts +7 -0
- package/bin/whet/cache/CacheManager.js +23 -0
- package/bin/whet/cache/FileCache.d.ts +18 -1
- package/bin/whet/cache/FileCache.js +88 -46
- package/bin/whet/cache/MemoryCache.d.ts +2 -0
- package/bin/whet/cache/MemoryCache.js +10 -0
- package/bin/whet/stones/Files.d.ts +1 -0
- package/bin/whet/stones/Files.js +50 -0
- package/package.json +1 -1
package/bin/whet/ConfigStore.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {Router} from "./route/Router.js"
|
|
2
1
|
import {Utils} from "./Utils.js"
|
|
3
|
-
import {Stone} from "./Stone.js"
|
|
4
2
|
import * as Path from "path"
|
|
5
3
|
import {Register} from "../genes/Register.js"
|
|
6
4
|
import * as Fs from "fs"
|
|
@@ -250,16 +248,16 @@ class ConfigStore extends Register.inherits() {
|
|
|
250
248
|
if (val == null) {
|
|
251
249
|
return true;
|
|
252
250
|
};
|
|
253
|
-
if (
|
|
251
|
+
if (typeof val === "function") {
|
|
254
252
|
return false;
|
|
255
253
|
};
|
|
256
|
-
if (
|
|
257
|
-
return
|
|
254
|
+
if (typeof val !== "object") {
|
|
255
|
+
return true;
|
|
258
256
|
};
|
|
259
|
-
if (
|
|
260
|
-
return
|
|
257
|
+
if (((val) instanceof Array)) {
|
|
258
|
+
return true;
|
|
261
259
|
};
|
|
262
|
-
return
|
|
260
|
+
return (Object.getPrototypeOf(val) === null || Object.getPrototypeOf(val) === Object.prototype);
|
|
263
261
|
}
|
|
264
262
|
static deepClone(val) {
|
|
265
263
|
if (val == null) {
|
package/bin/whet/Log.js
CHANGED
package/bin/whet/Stone.d.ts
CHANGED
|
@@ -17,6 +17,12 @@ export declare class Stone<T extends StoneConfig> {
|
|
|
17
17
|
If true, hash of a cached file (not `stone.getHash()` but actual file contents) won't be checked.
|
|
18
18
|
*/
|
|
19
19
|
ignoreFileHash: boolean
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* If true, this stone's own method source code is excluded from its hash (opt out of the
|
|
23
|
+
* code-aware self-hash). Only relevant for stones that override `generateHash()`. See `codeHash`.
|
|
24
|
+
*/
|
|
25
|
+
ignoreCodeHash: boolean
|
|
20
26
|
id: string
|
|
21
27
|
cacheStrategy: CacheStrategy
|
|
22
28
|
readonly cache: CacheManager
|
|
@@ -89,6 +95,10 @@ export declare class Stone<T extends StoneConfig> {
|
|
|
89
95
|
* **Do not override.**
|
|
90
96
|
* Used by cache. Returns either null, or result of `generateHash` finalized by adding
|
|
91
97
|
* dependencies.
|
|
98
|
+
*
|
|
99
|
+
* When `generateHash()` returns a non-null hash, the stone's own code hash (`codeHash`) is mixed
|
|
100
|
+
* in, so editing the stone's generation logic busts the cache even though config/inputs are
|
|
101
|
+
* unchanged.
|
|
92
102
|
*/
|
|
93
103
|
protected finalMaybeHash(): Promise<null | SourceHash>
|
|
94
104
|
|
|
@@ -108,8 +118,9 @@ export declare class Stone<T extends StoneConfig> {
|
|
|
108
118
|
protected list(): Promise<null | string[]>
|
|
109
119
|
|
|
110
120
|
/**
|
|
111
|
-
* Public API for getting output IDs. Calls list(),
|
|
112
|
-
*
|
|
121
|
+
* Public API for getting output IDs. Calls list(); if that returns null, tries the cache
|
|
122
|
+
* metadata fast path (`cache.tryListIds`, zero file reads / zero generation); only if the cache
|
|
123
|
+
* can't answer does it fall back to a full `getSource()`.
|
|
113
124
|
*/
|
|
114
125
|
listIds(): Promise<string[]>
|
|
115
126
|
|
|
@@ -255,6 +266,34 @@ export declare class Stone<T extends StoneConfig> {
|
|
|
255
266
|
protected profilerGetCurrentSpan(): null | AnySpan
|
|
256
267
|
protected get_cache(): CacheManager
|
|
257
268
|
toString(): string
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
Per-class memo for `codeHash`, keyed by constructor. Computed once per process per class.
|
|
272
|
+
*/
|
|
273
|
+
protected static codeHashCache: Map<any, SourceHash>
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Hash of the stone class's own source: `Function.prototype.toString()` of *every* own method
|
|
277
|
+
* (instance/prototype methods, accessors, and static methods) defined on the stone's class and
|
|
278
|
+
* its ancestors down to — but excluding — `Stone` itself. Memoized per class.
|
|
279
|
+
|
|
280
|
+
*
|
|
281
|
+
* **Limitations** (not reachable by reflection from the class object): does NOT capture changes
|
|
282
|
+
* in *module-level free functions* in the same file, nor in *imported helper modules* a method
|
|
283
|
+
* delegates to. Only class-level methods (instance + static) participate; instance-own function
|
|
284
|
+
* properties are intentionally excluded so the per-class memo stays sound.
|
|
285
|
+
*/
|
|
286
|
+
protected static codeHash(stone: AnyStone): SourceHash
|
|
287
|
+
protected static ownsFinalizeHash(obj: any): boolean
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Appends `name:slot:source` for every own function-valued slot (`value`/`get`/`set`) of `obj`'s
|
|
291
|
+
* own properties to `parts`, sorted by name for determinism. Uses property descriptors so getters
|
|
292
|
+
* and setters are read by source rather than invoked. Skips `constructor` and Haxe-internal
|
|
293
|
+
* bookkeeping props (`__class__`, `__super__`, `__name__`, …) — those aren't stone logic and
|
|
294
|
+
* `__super__` would otherwise pull the parent class's source into every subclass's hash.
|
|
295
|
+
*/
|
|
296
|
+
protected static collectOwnFunctions(obj: any, parts: string[]): void
|
|
258
297
|
}
|
|
259
298
|
|
|
260
299
|
export type StoneConfig = {
|
package/bin/whet/Stone.js
CHANGED
|
@@ -10,6 +10,7 @@ import {Project} from "./Project.js"
|
|
|
10
10
|
import {Log} from "./Log.js"
|
|
11
11
|
import * as Path from "path"
|
|
12
12
|
import {Register} from "../genes/Register.js"
|
|
13
|
+
import {Reflect as Reflect__1} from "../Reflect.js"
|
|
13
14
|
|
|
14
15
|
const $global = Register.$global
|
|
15
16
|
|
|
@@ -19,6 +20,7 @@ class Stone extends Register.inherits() {
|
|
|
19
20
|
this._contextPromise = null;
|
|
20
21
|
this.locked = false;
|
|
21
22
|
this.lockQueue = [];
|
|
23
|
+
this.ignoreCodeHash = false;
|
|
22
24
|
this.ignoreFileHash = false;
|
|
23
25
|
Log.log(10, ...["Instantiating new Stone.", {"type": StoneId_Fields_.getTypeName(this)}]);
|
|
24
26
|
if (config == null) {
|
|
@@ -310,6 +312,10 @@ class Stone extends Register.inherits() {
|
|
|
310
312
|
* **Do not override.**
|
|
311
313
|
* Used by cache. Returns either null, or result of `generateHash` finalized by adding
|
|
312
314
|
* dependencies.
|
|
315
|
+
*
|
|
316
|
+
* When `generateHash()` returns a non-null hash, the stone's own code hash (`codeHash`) is mixed
|
|
317
|
+
* in, so editing the stone's generation logic busts the cache even though config/inputs are
|
|
318
|
+
* unchanged.
|
|
313
319
|
*/
|
|
314
320
|
finalMaybeHash() {
|
|
315
321
|
let tmp = this.config.configStore;
|
|
@@ -318,6 +324,9 @@ class Stone extends Register.inherits() {
|
|
|
318
324
|
let _gthis = this;
|
|
319
325
|
return patchPromise.then(function (_) {
|
|
320
326
|
return _gthis.generateHash().then(function (hash) {
|
|
327
|
+
if (hash != null && !_gthis.ignoreCodeHash) {
|
|
328
|
+
hash = hash.add(Stone.codeHash(_gthis));
|
|
329
|
+
};
|
|
321
330
|
return _gthis.finalizeHash(hash);
|
|
322
331
|
});
|
|
323
332
|
});
|
|
@@ -364,8 +373,9 @@ class Stone extends Register.inherits() {
|
|
|
364
373
|
}
|
|
365
374
|
|
|
366
375
|
/**
|
|
367
|
-
* Public API for getting output IDs. Calls list(),
|
|
368
|
-
*
|
|
376
|
+
* Public API for getting output IDs. Calls list(); if that returns null, tries the cache
|
|
377
|
+
* metadata fast path (`cache.tryListIds`, zero file reads / zero generation); only if the cache
|
|
378
|
+
* can't answer does it fall back to a full `getSource()`.
|
|
369
379
|
*/
|
|
370
380
|
listIds() {
|
|
371
381
|
let _gthis = this;
|
|
@@ -373,16 +383,21 @@ class Stone extends Register.inherits() {
|
|
|
373
383
|
if (ids != null) {
|
|
374
384
|
return ids;
|
|
375
385
|
};
|
|
376
|
-
return _gthis.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
let _g = 0;
|
|
380
|
-
let _g1 = _this.length;
|
|
381
|
-
while (_g < _g1) {
|
|
382
|
-
let i = _g++;
|
|
383
|
-
result[i] = _this[i].id;
|
|
386
|
+
return _gthis.project.cache.tryListIds(_gthis).then(function (cachedIds) {
|
|
387
|
+
if (cachedIds != null) {
|
|
388
|
+
return cachedIds;
|
|
384
389
|
};
|
|
385
|
-
return
|
|
390
|
+
return _gthis.getSource().then(function (source) {
|
|
391
|
+
let _this = source.data;
|
|
392
|
+
let result = new Array(_this.length);
|
|
393
|
+
let _g = 0;
|
|
394
|
+
let _g1 = _this.length;
|
|
395
|
+
while (_g < _g1) {
|
|
396
|
+
let i = _g++;
|
|
397
|
+
result[i] = _this[i].id;
|
|
398
|
+
};
|
|
399
|
+
return result;
|
|
400
|
+
});
|
|
386
401
|
});
|
|
387
402
|
});
|
|
388
403
|
}
|
|
@@ -701,6 +716,83 @@ class Stone extends Register.inherits() {
|
|
|
701
716
|
toString() {
|
|
702
717
|
return "" + this.id + ":" + StoneId_Fields_.getTypeName(this);
|
|
703
718
|
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Hash of the stone class's own source: `Function.prototype.toString()` of *every* own method
|
|
722
|
+
* (instance/prototype methods, accessors, and static methods) defined on the stone's class and
|
|
723
|
+
* its ancestors down to — but excluding — `Stone` itself. Memoized per class.
|
|
724
|
+
|
|
725
|
+
*
|
|
726
|
+
* **Limitations** (not reachable by reflection from the class object): does NOT capture changes
|
|
727
|
+
* in *module-level free functions* in the same file, nor in *imported helper modules* a method
|
|
728
|
+
* delegates to. Only class-level methods (instance + static) participate; instance-own function
|
|
729
|
+
* properties are intentionally excluded so the per-class memo stays sound.
|
|
730
|
+
*/
|
|
731
|
+
static codeHash(stone) {
|
|
732
|
+
let ctor = stone.constructor;
|
|
733
|
+
let cached = Stone.codeHashCache.get(ctor);
|
|
734
|
+
if (cached != null) {
|
|
735
|
+
return cached;
|
|
736
|
+
};
|
|
737
|
+
let parts = [];
|
|
738
|
+
let proto = Object.getPrototypeOf(stone);
|
|
739
|
+
while (proto != null && !Object.prototype.hasOwnProperty.call(proto, 'finalizeHash')) {
|
|
740
|
+
Stone.collectOwnFunctions(proto, parts);
|
|
741
|
+
proto = Object.getPrototypeOf(proto);
|
|
742
|
+
};
|
|
743
|
+
let funcProto = Function.prototype;
|
|
744
|
+
let klass = ctor;
|
|
745
|
+
while (klass != null && klass != funcProto) {
|
|
746
|
+
let kproto = klass.prototype;
|
|
747
|
+
if (kproto != null && Object.prototype.hasOwnProperty.call(kproto, 'finalizeHash')) {
|
|
748
|
+
break;
|
|
749
|
+
};
|
|
750
|
+
Stone.collectOwnFunctions(klass, parts);
|
|
751
|
+
klass = Object.getPrototypeOf(klass);
|
|
752
|
+
};
|
|
753
|
+
let hash = SourceHash.fromString(parts.join("\n"));
|
|
754
|
+
Stone.codeHashCache.set(ctor, hash);
|
|
755
|
+
return hash;
|
|
756
|
+
}
|
|
757
|
+
static ownsFinalizeHash(obj) {
|
|
758
|
+
return Object.prototype.hasOwnProperty.call(obj, 'finalizeHash');
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Appends `name:slot:source` for every own function-valued slot (`value`/`get`/`set`) of `obj`'s
|
|
763
|
+
* own properties to `parts`, sorted by name for determinism. Uses property descriptors so getters
|
|
764
|
+
* and setters are read by source rather than invoked. Skips `constructor` and Haxe-internal
|
|
765
|
+
* bookkeeping props (`__class__`, `__super__`, `__name__`, …) — those aren't stone logic and
|
|
766
|
+
* `__super__` would otherwise pull the parent class's source into every subclass's hash.
|
|
767
|
+
*/
|
|
768
|
+
static collectOwnFunctions(obj, parts) {
|
|
769
|
+
let names = Object.getOwnPropertyNames(obj);
|
|
770
|
+
names.sort(Reflect__1.compare);
|
|
771
|
+
let _g = 0;
|
|
772
|
+
while (_g < names.length) {
|
|
773
|
+
let name = names[_g];
|
|
774
|
+
++_g;
|
|
775
|
+
if (name == "constructor" || name.startsWith("__")) {
|
|
776
|
+
continue;
|
|
777
|
+
};
|
|
778
|
+
let desc = Object.getOwnPropertyDescriptor(obj, name);
|
|
779
|
+
if (desc == null) {
|
|
780
|
+
continue;
|
|
781
|
+
};
|
|
782
|
+
let fn = desc["value"];
|
|
783
|
+
if (fn != null && typeof fn === 'function') {
|
|
784
|
+
parts.push(name + ":" + "value" + ":" + Function.prototype.toString.call(fn));
|
|
785
|
+
};
|
|
786
|
+
let fn1 = desc["get"];
|
|
787
|
+
if (fn1 != null && typeof fn1 === 'function') {
|
|
788
|
+
parts.push(name + ":" + "get" + ":" + Function.prototype.toString.call(fn1));
|
|
789
|
+
};
|
|
790
|
+
let fn2 = desc["set"];
|
|
791
|
+
if (fn2 != null && typeof fn2 === 'function') {
|
|
792
|
+
parts.push(name + ":" + "set" + ":" + Function.prototype.toString.call(fn2));
|
|
793
|
+
};
|
|
794
|
+
};
|
|
795
|
+
}
|
|
704
796
|
static get __name__() {
|
|
705
797
|
return "whet.Stone"
|
|
706
798
|
}
|
|
@@ -710,6 +802,7 @@ class Stone extends Register.inherits() {
|
|
|
710
802
|
}
|
|
711
803
|
Stone.prototype.config = null;
|
|
712
804
|
Stone.prototype.ignoreFileHash = null;
|
|
805
|
+
Stone.prototype.ignoreCodeHash = null;
|
|
713
806
|
Stone.prototype.id = null;
|
|
714
807
|
Stone.prototype.cacheStrategy = null;
|
|
715
808
|
Stone.prototype.project = null;
|
|
@@ -717,3 +810,5 @@ Stone.prototype.lockQueue = null;
|
|
|
717
810
|
Stone.prototype.locked = null;
|
|
718
811
|
Stone.prototype._contextPromise = null;
|
|
719
812
|
|
|
813
|
+
|
|
814
|
+
Stone.codeHashCache = new Map()
|
package/bin/whet/Whet.d.ts
CHANGED
package/bin/whet/Whet.js
CHANGED
|
@@ -5,7 +5,7 @@ import PinoPretty from "pino-pretty"
|
|
|
5
5
|
import {Exception} from "../haxe/Exception.js"
|
|
6
6
|
import {Register} from "../genes/Register.js"
|
|
7
7
|
import * as Fs from "fs"
|
|
8
|
-
import {Command, CommanderError} from "commander"
|
|
8
|
+
import {Command, Option, CommanderError} from "commander"
|
|
9
9
|
import {Std} from "../Std.js"
|
|
10
10
|
|
|
11
11
|
const $global = Register.$global
|
|
@@ -26,7 +26,7 @@ class Whet_Fields_ {
|
|
|
26
26
|
if (entryUrl != thisUrl) {
|
|
27
27
|
return;
|
|
28
28
|
};
|
|
29
|
-
Whet_Fields_.program.enablePositionalOptions().passThroughOptions().description("Project tooling.").usage("[options] [command] [+ [command]...]").version("0.
|
|
29
|
+
Whet_Fields_.program.enablePositionalOptions().passThroughOptions().description("Project tooling.").usage("[options] [command] [+ [command]...]").version("0.6.0", "-v, --version").allowUnknownOption(true).allowExcessArguments(true).showSuggestionAfterError(true).helpOption(false).option("-p, --project <file>", "project to run", "Project.mjs").addOption(new Option("-l, --log-level <level>", "log level, a string/number")["default"]("info").env("WHET_LOG_LEVEL")).option("--no-pretty", "disable pretty logging").option("-q, --quiet", "quiet output: warn level + no color (the default when stdout is not a TTY)").option("--profile <format>", "enable profiling, export to whet-profile.json on exit (format: json or trace, default: json)").exitOverride();
|
|
30
30
|
try {
|
|
31
31
|
Whet_Fields_.program.parse();
|
|
32
32
|
}catch (_g) {
|
|
@@ -37,6 +37,8 @@ class Whet_Fields_ {
|
|
|
37
37
|
throw Exception.thrown(_g1);
|
|
38
38
|
};
|
|
39
39
|
};
|
|
40
|
+
let firstSegment = Whet_Fields_.getCommands(Whet_Fields_.program.args)[0];
|
|
41
|
+
Whet_Fields_.topLevelHelp = firstSegment.length > 0 && (firstSegment[0] == "--help" || firstSegment[0] == "-h");
|
|
40
42
|
let options = Whet_Fields_.program.opts();
|
|
41
43
|
if (options.logLevel != null) {
|
|
42
44
|
let n = Std.parseInt(options.logLevel);
|
|
@@ -49,8 +51,14 @@ class Whet_Fields_ {
|
|
|
49
51
|
Log.logLevel = n;
|
|
50
52
|
};
|
|
51
53
|
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
let nonTty = process.stdout.isTTY != true;
|
|
55
|
+
let levelSource = Whet_Fields_.program.getOptionValueSource("logLevel");
|
|
56
|
+
let prettySource = Whet_Fields_.program.getOptionValueSource("pretty");
|
|
57
|
+
if (options.quiet || nonTty && levelSource == "default") {
|
|
58
|
+
Log.logLevel = 40;
|
|
59
|
+
};
|
|
60
|
+
if ((options.quiet) ? false : (prettySource != "default") ? options.pretty : !nonTty) {
|
|
61
|
+
Log.stream = PinoPretty({"destination": 2});
|
|
54
62
|
};
|
|
55
63
|
global.setImmediate(Whet_Fields_.init, options);
|
|
56
64
|
}
|
|
@@ -66,9 +74,11 @@ class Whet_Fields_ {
|
|
|
66
74
|
Log.log(10, ...["Project module imported."]);
|
|
67
75
|
Whet_Fields_.initProjects();
|
|
68
76
|
})["catch"](function (e) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
if (!Whet_Fields_.topLevelHelp) {
|
|
78
|
+
Log.log(50, ...["Error loading project.", {"error": e}]);
|
|
79
|
+
if (((e) instanceof Error)) {
|
|
80
|
+
Log.log(50, ...[e.stack]);
|
|
81
|
+
};
|
|
72
82
|
};
|
|
73
83
|
try {
|
|
74
84
|
Whet_Fields_.program.help();
|
|
@@ -87,6 +97,7 @@ class Whet_Fields_ {
|
|
|
87
97
|
while (_g2 < _g3.length) Whet_Fields_.program.addOption(_g3[_g2++]);
|
|
88
98
|
};
|
|
89
99
|
Whet_Fields_.program.allowUnknownOption(false);
|
|
100
|
+
Whet_Fields_.program.helpOption("-h, --help", "display help for command");
|
|
90
101
|
let schemaCmd = new Command("schema");
|
|
91
102
|
schemaCmd.description("Export project schema as JSON.");
|
|
92
103
|
schemaCmd.action(Whet_Fields_.outputSchema);
|
|
@@ -144,7 +155,7 @@ class Whet_Fields_ {
|
|
|
144
155
|
Whet_Fields_.program.parseAsync(c, {"from": "user"}).then(function (_) {
|
|
145
156
|
nextCommand();
|
|
146
157
|
})["catch"](function (err) {
|
|
147
|
-
if (((err) instanceof CommanderError) && err.code == "commander.help") {
|
|
158
|
+
if (((err) instanceof CommanderError) && (err.code == "commander.help" || err.code == "commander.helpDisplayed")) {
|
|
148
159
|
return;
|
|
149
160
|
};
|
|
150
161
|
Log.log(50, ...["Error while executing command.", {"error": err}]);
|
|
@@ -188,6 +199,9 @@ class Whet_Fields_ {
|
|
|
188
199
|
static serializeOption(opt) {
|
|
189
200
|
return {"name": opt.name(), "attributeName": opt.attributeName(), "flags": opt.flags, "description": opt.description, "choices": opt.argChoices, "defaultValue": opt.defaultValue, "required": opt.required, "mandatory": opt.mandatory, "boolean": opt.isBoolean(), "hidden": opt.hidden};
|
|
190
201
|
}
|
|
202
|
+
static serializeArgument(arg) {
|
|
203
|
+
return {"name": arg.name(), "description": arg.description, "choices": arg.argChoices, "defaultValue": arg.defaultValue, "required": arg.required, "variadic": arg.variadic};
|
|
204
|
+
}
|
|
191
205
|
static serializeCommand(cmd) {
|
|
192
206
|
let tmp = cmd.name();
|
|
193
207
|
let tmp1 = cmd.description();
|
|
@@ -196,7 +210,11 @@ class Whet_Fields_ {
|
|
|
196
210
|
let _g1 = 0;
|
|
197
211
|
let _g2 = cmd.options;
|
|
198
212
|
while (_g1 < _g2.length) _g.push(Whet_Fields_.serializeOption(_g2[_g1++]));
|
|
199
|
-
|
|
213
|
+
let _g3 = [];
|
|
214
|
+
let _g4 = 0;
|
|
215
|
+
let _g5 = cmd.registeredArguments;
|
|
216
|
+
while (_g4 < _g5.length) _g3.push(Whet_Fields_.serializeArgument(_g5[_g4++]));
|
|
217
|
+
return {"name": tmp, "description": tmp1, "aliases": tmp2, "options": _g, "arguments": _g3};
|
|
200
218
|
}
|
|
201
219
|
static getCommands(args) {
|
|
202
220
|
let commands = [];
|
|
@@ -221,5 +239,6 @@ class Whet_Fields_ {
|
|
|
221
239
|
|
|
222
240
|
|
|
223
241
|
Whet_Fields_.program = new Command("whet")
|
|
242
|
+
Whet_Fields_.topLevelHelp = false
|
|
224
243
|
export const program = Whet_Fields_.program
|
|
225
244
|
export const main = Whet_Fields_.main
|
|
@@ -15,6 +15,20 @@ export declare class BaseCache<Key, Value extends {
|
|
|
15
15
|
protected rootDir: string
|
|
16
16
|
get(stone: AnyStone, durability: CacheDurability, check: DurabilityCheck): Promise<Source>
|
|
17
17
|
getPartial(stone: AnyStone, sourceId: string, durability: CacheDurability, check: DurabilityCheck): Promise<null | Source>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Enumerate a stone's output ids from cache metadata alone — no file reads, no generation.
|
|
21
|
+
* Used by `Stone.listIds()` as a fast path before falling back to a full `getSource()`.
|
|
22
|
+
*
|
|
23
|
+
* Returns null (caller falls back) unless there is a *complete* entry whose hash matches the
|
|
24
|
+
* stone's current `finalMaybeHash()`. Sound because the output id-set is a pure function of that
|
|
25
|
+
* hash, so a complete matching entry's files are the authoritative id list. Stale/missing files
|
|
26
|
+
* on disk are irrelevant here — they're stat-validated (and regenerated) when actually fetched.
|
|
27
|
+
*
|
|
28
|
+
* Intentionally takes no lock and does not touch use-order/durability: listing is a read-only
|
|
29
|
+
* "what does this produce" query, not a use of the cached bytes.
|
|
30
|
+
*/
|
|
31
|
+
tryListIds(stone: AnyStone): Promise<null | string[]>
|
|
18
32
|
protected set(source: Source): Promise<Value>
|
|
19
33
|
getUniqueDir(stone: AnyStone, baseDir: string, hash?: null | SourceHash): string
|
|
20
34
|
|
|
@@ -35,6 +49,13 @@ export declare class BaseCache<Key, Value extends {
|
|
|
35
49
|
protected key(stone: AnyStone): Key
|
|
36
50
|
protected value(source: Source): Promise<Value>
|
|
37
51
|
protected source(stone: AnyStone, value: Value): Promise<Source>
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Read & validate just the requested file from an entry, without touching the rest. Resolves
|
|
55
|
+
* the single-output Source when valid, or null when that file is invalid/missing (the caller
|
|
56
|
+
* then regenerates it).
|
|
57
|
+
*/
|
|
58
|
+
protected sourcePartial(stone: AnyStone, value: Value, sourceId: string): Promise<null | Source>
|
|
38
59
|
protected getExistingDirs(stone: AnyStone): string[]
|
|
39
60
|
protected getDirFor(value: Value): string
|
|
40
61
|
|
|
@@ -43,6 +64,11 @@ export declare class BaseCache<Key, Value extends {
|
|
|
43
64
|
*/
|
|
44
65
|
protected hasSourceId(value: Value, sourceId: string): boolean
|
|
45
66
|
|
|
67
|
+
/**
|
|
68
|
+
All output ids held by an entry, read from metadata without touching files.
|
|
69
|
+
*/
|
|
70
|
+
protected getValueIds(value: Value): string[]
|
|
71
|
+
|
|
46
72
|
/**
|
|
47
73
|
Merge partial source data into an existing entry, returns the updated value.
|
|
48
74
|
*/
|
|
@@ -179,17 +179,7 @@ class BaseCache extends Register.inherits() {
|
|
|
179
179
|
_gthis.setRecentUseOrder(values, value);
|
|
180
180
|
};
|
|
181
181
|
};
|
|
182
|
-
|
|
183
|
-
return _gthis.source(stone, value).then(function (src) {
|
|
184
|
-
if (src != null) {
|
|
185
|
-
return src.filterTo(sourceId);
|
|
186
|
-
} else {
|
|
187
|
-
return null;
|
|
188
|
-
};
|
|
189
|
-
});
|
|
190
|
-
} else if (value != null && value.complete) {
|
|
191
|
-
return Promise.resolve(null);
|
|
192
|
-
} else {
|
|
182
|
+
let regenerate = function () {
|
|
193
183
|
return ((stone.project.profiler != null) ? stone.project.profiler.withSpan(stone, "GeneratePartial", function () {
|
|
194
184
|
return stone.generatePartialSource(sourceId, hash);
|
|
195
185
|
}, {"sourceId": sourceId}) : stone.generatePartialSource(sourceId, hash)).then(function (result) {
|
|
@@ -208,7 +198,55 @@ class BaseCache extends Register.inherits() {
|
|
|
208
198
|
};
|
|
209
199
|
});
|
|
210
200
|
};
|
|
201
|
+
if (value != null && _gthis.hasSourceId(value, sourceId)) {
|
|
202
|
+
return _gthis.sourcePartial(stone, value, sourceId).then(function (src) {
|
|
203
|
+
if (src != null) {
|
|
204
|
+
return src;
|
|
205
|
+
};
|
|
206
|
+
return regenerate();
|
|
207
|
+
});
|
|
208
|
+
} else if (value != null && value.complete) {
|
|
209
|
+
return Promise.resolve(null);
|
|
210
|
+
} else {
|
|
211
|
+
return regenerate();
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Enumerate a stone's output ids from cache metadata alone — no file reads, no generation.
|
|
219
|
+
* Used by `Stone.listIds()` as a fast path before falling back to a full `getSource()`.
|
|
220
|
+
*
|
|
221
|
+
* Returns null (caller falls back) unless there is a *complete* entry whose hash matches the
|
|
222
|
+
* stone's current `finalMaybeHash()`. Sound because the output id-set is a pure function of that
|
|
223
|
+
* hash, so a complete matching entry's files are the authoritative id list. Stale/missing files
|
|
224
|
+
* on disk are irrelevant here — they're stat-validated (and regenerated) when actually fetched.
|
|
225
|
+
*
|
|
226
|
+
* Intentionally takes no lock and does not touch use-order/durability: listing is a read-only
|
|
227
|
+
* "what does this produce" query, not a use of the cached bytes.
|
|
228
|
+
*/
|
|
229
|
+
tryListIds(stone) {
|
|
230
|
+
let _gthis = this;
|
|
231
|
+
return stone.finalMaybeHash().then(function (hash) {
|
|
232
|
+
if (hash == null) {
|
|
233
|
+
return null;
|
|
234
|
+
};
|
|
235
|
+
let values = _gthis.cache.get(_gthis.key(stone));
|
|
236
|
+
if (values == null) {
|
|
237
|
+
return null;
|
|
238
|
+
};
|
|
239
|
+
let value = Lambda.find(values, function (v) {
|
|
240
|
+
if (SourceHash.equals(v.hash, hash)) {
|
|
241
|
+
return v.complete;
|
|
242
|
+
} else {
|
|
243
|
+
return false;
|
|
244
|
+
};
|
|
211
245
|
});
|
|
246
|
+
if (value == null) {
|
|
247
|
+
return null;
|
|
248
|
+
};
|
|
249
|
+
return _gthis.getValueIds(value);
|
|
212
250
|
});
|
|
213
251
|
}
|
|
214
252
|
set(source) {
|
|
@@ -20,6 +20,13 @@ export declare class CacheManager {
|
|
|
20
20
|
getSource(stone: AnyStone): Promise<Source>
|
|
21
21
|
getPartialSource(stone: AnyStone, sourceId: string): Promise<null | Source>
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Fast path for `Stone.listIds()`: enumerate output ids from cache metadata, without reading
|
|
25
|
+
* files or generating. Returns null when no usable entry exists (caller falls back to
|
|
26
|
+
* `getSource()`). `None` is never cached, so there's nothing to answer from.
|
|
27
|
+
*/
|
|
28
|
+
tryListIds(stone: AnyStone): Promise<null | string[]>
|
|
29
|
+
|
|
23
30
|
/**
|
|
24
31
|
* Re-generates source even if the currently cached value is valid.
|
|
25
32
|
*/
|
|
@@ -128,6 +128,29 @@ class CacheManager extends Register.inherits() {
|
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Fast path for `Stone.listIds()`: enumerate output ids from cache metadata, without reading
|
|
133
|
+
* files or generating. Returns null when no usable entry exists (caller falls back to
|
|
134
|
+
* `getSource()`). `None` is never cached, so there's nothing to answer from.
|
|
135
|
+
*/
|
|
136
|
+
tryListIds(stone) {
|
|
137
|
+
switch (stone.cacheStrategy._hx_index) {
|
|
138
|
+
case 0:
|
|
139
|
+
return Promise.resolve(null);
|
|
140
|
+
break
|
|
141
|
+
case 1:
|
|
142
|
+
return this.memCache.tryListIds(stone);
|
|
143
|
+
break
|
|
144
|
+
case 2:
|
|
145
|
+
return this.fileCache.tryListIds(stone);
|
|
146
|
+
break
|
|
147
|
+
case 3:
|
|
148
|
+
return this.fileCache.tryListIds(stone);
|
|
149
|
+
break
|
|
150
|
+
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
131
154
|
/**
|
|
132
155
|
* Re-generates source even if the currently cached value is valid.
|
|
133
156
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {BaseCache} from "./BaseCache"
|
|
2
2
|
import {AnyStone} from "../Stone"
|
|
3
3
|
import {SourceHash} from "../SourceHash"
|
|
4
|
-
import {Source} from "../Source"
|
|
4
|
+
import {Source, SourceData} from "../Source"
|
|
5
5
|
|
|
6
6
|
export declare class FileCache extends BaseCache<string, RuntimeFileCacheValue> {
|
|
7
7
|
constructor(rootDir: string)
|
|
@@ -11,7 +11,15 @@ export declare class FileCache extends BaseCache<string, RuntimeFileCacheValue>
|
|
|
11
11
|
protected flushPromise: Promise<void>
|
|
12
12
|
protected key(stone: AnyStone): string
|
|
13
13
|
protected value(source: Source): Promise<RuntimeFileCacheValue>
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Read & validate a single cached file. Resolves with its `SourceData` when valid, or `null`
|
|
17
|
+
* when the file is missing or fails mtime+hash validation (a cache miss for that file). Rejects
|
|
18
|
+
* only on unexpected IO errors.
|
|
19
|
+
*/
|
|
20
|
+
protected readCachedFile(stone: AnyStone, file: RuntimeFileEntry): Promise<null | SourceData>
|
|
14
21
|
protected source(stone: AnyStone, value: RuntimeFileCacheValue): Promise<Source>
|
|
22
|
+
protected sourcePartial(stone: AnyStone, value: RuntimeFileCacheValue, sourceId: string): Promise<null | Source>
|
|
15
23
|
protected set(source: Source): Promise<RuntimeFileCacheValue>
|
|
16
24
|
protected getExistingDirs(stone: AnyStone): string[]
|
|
17
25
|
protected remove(stone: AnyStone, value: RuntimeFileCacheValue): Promise<any>
|
|
@@ -19,6 +27,7 @@ export declare class FileCache extends BaseCache<string, RuntimeFileCacheValue>
|
|
|
19
27
|
protected setRecentUseOrder(values: RuntimeFileCacheValue[], value: RuntimeFileCacheValue): boolean
|
|
20
28
|
protected getDirFor(value: RuntimeFileCacheValue): string
|
|
21
29
|
protected hasSourceId(value: RuntimeFileCacheValue, sourceId: string): boolean
|
|
30
|
+
protected getValueIds(value: RuntimeFileCacheValue): string[]
|
|
22
31
|
protected mergePartial(stone: AnyStone, existing: RuntimeFileCacheValue, addition: Source, markComplete: boolean): Promise<RuntimeFileCacheValue>
|
|
23
32
|
protected replaceEntry(stone: AnyStone, existing: RuntimeFileCacheValue, replacement: Source): Promise<RuntimeFileCacheValue>
|
|
24
33
|
protected flush(): Promise<void>
|
|
@@ -44,3 +53,11 @@ export type FileCacheValue<H, S> = {
|
|
|
44
53
|
export type DbJson = {[key: string]: FileCacheValue<string, string>[]}
|
|
45
54
|
|
|
46
55
|
export type RuntimeFileCacheValue = FileCacheValue<SourceHash, string>
|
|
56
|
+
|
|
57
|
+
export type RuntimeFileEntry = {
|
|
58
|
+
fileHash: SourceHash,
|
|
59
|
+
filePath: string,
|
|
60
|
+
id: string,
|
|
61
|
+
mtime?: null | number,
|
|
62
|
+
size?: null | number
|
|
63
|
+
}
|
|
@@ -84,67 +84,102 @@ class FileCache extends Register.inherits(() => BaseCache, true) {
|
|
|
84
84
|
return {"hash": source.hash, "ctime": source.ctime, "baseDir": source.getDirPath(), "complete": source.complete, "ctimePretty": null, "files": files};
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Read & validate a single cached file. Resolves with its `SourceData` when valid, or `null`
|
|
90
|
+
* when the file is missing or fails mtime+hash validation (a cache miss for that file). Rejects
|
|
91
|
+
* only on unexpected IO errors.
|
|
92
|
+
*/
|
|
93
|
+
readCachedFile(stone, file) {
|
|
94
|
+
let _gthis = this;
|
|
95
|
+
return new Promise(function (res, rej) {
|
|
96
|
+
let path = Path.posix.join(".", _gthis.rootDir, ".", file.filePath);
|
|
97
|
+
Fs.stat(path, function (statErr, stats) {
|
|
98
|
+
if (statErr != null) {
|
|
99
|
+
if (((statErr) instanceof Error) && statErr.code == "ENOENT") {
|
|
100
|
+
res(null);
|
|
101
|
+
} else {
|
|
102
|
+
rej(statErr);
|
|
103
|
+
};
|
|
104
|
+
return;
|
|
105
|
+
};
|
|
106
|
+
let mtimeMatch = file.mtime != null && stats.mtimeMs == file.mtime && (stats.size | 0) == file.size;
|
|
107
|
+
if (mtimeMatch && !stone.ignoreFileHash) {
|
|
108
|
+
SourceData.fromFileSkipHash(file.id, path, file.filePath, file.fileHash).then(res, rej);
|
|
109
|
+
} else {
|
|
110
|
+
SourceData.fromFile(file.id, path, file.filePath).then(function (sourceData) {
|
|
111
|
+
if (sourceData == null || !stone.ignoreFileHash && !SourceHash.equals(sourceData.hash, file.fileHash)) {
|
|
112
|
+
res(null);
|
|
113
|
+
} else {
|
|
114
|
+
res(sourceData);
|
|
115
|
+
};
|
|
116
|
+
}, function (err) {
|
|
117
|
+
if (((err) instanceof Error) && err.code == "ENOENT") {
|
|
118
|
+
res(null);
|
|
119
|
+
} else {
|
|
120
|
+
rej(err);
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
87
127
|
source(stone, value) {
|
|
128
|
+
let tmp;
|
|
88
129
|
let _g = stone.cacheStrategy;
|
|
89
130
|
if (_g._hx_index == 3) {
|
|
90
131
|
let _g1 = _g.path;
|
|
91
|
-
let invalidPath;
|
|
92
132
|
if (value.files.length == 1 && !(_g1.length == 0 || _g1.charCodeAt(_g1.length - 1) == 47)) {
|
|
93
|
-
|
|
133
|
+
tmp = value.files[0].filePath != _g1;
|
|
94
134
|
} else {
|
|
95
135
|
let dir = _g1.substring(0, _g1.lastIndexOf("/") + 1);
|
|
96
|
-
|
|
97
|
-
};
|
|
98
|
-
if (invalidPath) {
|
|
99
|
-
return Promise.resolve(null);
|
|
136
|
+
tmp = value.baseDir != ((dir.length == 0) ? "./" : dir);
|
|
100
137
|
};
|
|
138
|
+
} else {
|
|
139
|
+
tmp = false;
|
|
140
|
+
};
|
|
141
|
+
if (tmp) {
|
|
142
|
+
return Promise.resolve(null);
|
|
101
143
|
};
|
|
102
|
-
let _gthis = this;
|
|
103
144
|
let _g1 = [];
|
|
104
145
|
let _g2 = 0;
|
|
105
146
|
let _g3 = value.files;
|
|
106
|
-
while (_g2 < _g3.length)
|
|
107
|
-
let file = _g3[_g2];
|
|
108
|
-
++_g2;
|
|
109
|
-
_g1.push(new Promise(function (res, rej) {
|
|
110
|
-
let path = Path.posix.join(".", _gthis.rootDir, ".", file.filePath);
|
|
111
|
-
Fs.stat(path, function (statErr, stats) {
|
|
112
|
-
if (statErr != null) {
|
|
113
|
-
if (((statErr) instanceof Error) && statErr.code == "ENOENT") {
|
|
114
|
-
rej("Invalid.");
|
|
115
|
-
} else {
|
|
116
|
-
rej(statErr);
|
|
117
|
-
};
|
|
118
|
-
return;
|
|
119
|
-
};
|
|
120
|
-
let mtimeMatch = file.mtime != null && stats.mtimeMs == file.mtime && (stats.size | 0) == file.size;
|
|
121
|
-
if (mtimeMatch && !stone.ignoreFileHash) {
|
|
122
|
-
SourceData.fromFileSkipHash(file.id, path, file.filePath, file.fileHash).then(res, rej);
|
|
123
|
-
} else {
|
|
124
|
-
SourceData.fromFile(file.id, path, file.filePath).then(function (sourceData) {
|
|
125
|
-
if (sourceData == null || !stone.ignoreFileHash && !SourceHash.equals(sourceData.hash, file.fileHash)) {
|
|
126
|
-
rej("Invalid.");
|
|
127
|
-
} else {
|
|
128
|
-
res(sourceData);
|
|
129
|
-
};
|
|
130
|
-
}, function (err) {
|
|
131
|
-
if (((err) instanceof Error) && err.code == "ENOENT") {
|
|
132
|
-
rej("Invalid.");
|
|
133
|
-
} else {
|
|
134
|
-
rej(err);
|
|
135
|
-
};
|
|
136
|
-
});
|
|
137
|
-
};
|
|
138
|
-
});
|
|
139
|
-
}));
|
|
140
|
-
};
|
|
147
|
+
while (_g2 < _g3.length) _g1.push(this.readCachedFile(stone, _g3[_g2++]));
|
|
141
148
|
return Promise.all(_g1).then(function (data) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (rejected == "Invalid.") {
|
|
149
|
+
let _g = 0;
|
|
150
|
+
while (_g < data.length) if (data[_g++] == null) {
|
|
145
151
|
return null;
|
|
152
|
+
};
|
|
153
|
+
return new Source(data, value.hash, stone, value.ctime, (value.complete != null) ? value.complete : true);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
sourcePartial(stone, value, sourceId) {
|
|
157
|
+
let tmp;
|
|
158
|
+
let _g = stone.cacheStrategy;
|
|
159
|
+
if (_g._hx_index == 3) {
|
|
160
|
+
let _g1 = _g.path;
|
|
161
|
+
if (value.files.length == 1 && !(_g1.length == 0 || _g1.charCodeAt(_g1.length - 1) == 47)) {
|
|
162
|
+
tmp = value.files[0].filePath != _g1;
|
|
163
|
+
} else {
|
|
164
|
+
let dir = _g1.substring(0, _g1.lastIndexOf("/") + 1);
|
|
165
|
+
tmp = value.baseDir != ((dir.length == 0) ? "./" : dir);
|
|
166
|
+
};
|
|
167
|
+
} else {
|
|
168
|
+
tmp = false;
|
|
169
|
+
};
|
|
170
|
+
if (tmp) {
|
|
171
|
+
return Promise.resolve(null);
|
|
172
|
+
};
|
|
173
|
+
let file = Lambda.find(value.files, function (f) {
|
|
174
|
+
return f.id == sourceId;
|
|
175
|
+
});
|
|
176
|
+
if (file == null) {
|
|
177
|
+
return Promise.resolve(null);
|
|
178
|
+
};
|
|
179
|
+
return this.readCachedFile(stone, file).then(function (sd) {
|
|
180
|
+
if (sd != null) {
|
|
181
|
+
return new Source([sd], value.hash, stone, value.ctime, false);
|
|
146
182
|
} else {
|
|
147
|
-
throw rejected;
|
|
148
183
|
return null;
|
|
149
184
|
};
|
|
150
185
|
});
|
|
@@ -239,6 +274,13 @@ class FileCache extends Register.inherits(() => BaseCache, true) {
|
|
|
239
274
|
return f.id == sourceId;
|
|
240
275
|
});
|
|
241
276
|
}
|
|
277
|
+
getValueIds(value) {
|
|
278
|
+
let _g = [];
|
|
279
|
+
let _g1 = 0;
|
|
280
|
+
let _g2 = value.files;
|
|
281
|
+
while (_g1 < _g2.length) _g.push(_g2[_g1++].id);
|
|
282
|
+
return _g;
|
|
283
|
+
}
|
|
242
284
|
mergePartial(stone, existing, addition, markComplete) {
|
|
243
285
|
let _g = [];
|
|
244
286
|
let _g1 = 0;
|
|
@@ -7,9 +7,11 @@ export declare class MemoryCache extends BaseCache<AnyStone, Source> {
|
|
|
7
7
|
protected key(stone: AnyStone): AnyStone
|
|
8
8
|
protected value(source: Source): Promise<Source>
|
|
9
9
|
protected source(stone: AnyStone, value: Source): Promise<Source>
|
|
10
|
+
protected sourcePartial(stone: AnyStone, value: Source, sourceId: string): Promise<null | Source>
|
|
10
11
|
protected getExistingDirs(stone: AnyStone): string[]
|
|
11
12
|
protected getDirFor(value: Source): string
|
|
12
13
|
protected hasSourceId(value: Source, sourceId: string): boolean
|
|
14
|
+
protected getValueIds(value: Source): string[]
|
|
13
15
|
protected mergePartial(stone: AnyStone, existing: Source, addition: Source, markComplete: boolean): Promise<Source>
|
|
14
16
|
protected replaceEntry(stone: AnyStone, existing: Source, replacement: Source): Promise<Source>
|
|
15
17
|
}
|
|
@@ -20,6 +20,9 @@ class MemoryCache extends Register.inherits(() => BaseCache, true) {
|
|
|
20
20
|
source(stone, value) {
|
|
21
21
|
return Promise.resolve(value);
|
|
22
22
|
}
|
|
23
|
+
sourcePartial(stone, value, sourceId) {
|
|
24
|
+
return Promise.resolve(value.filterTo(sourceId));
|
|
25
|
+
}
|
|
23
26
|
getExistingDirs(stone) {
|
|
24
27
|
let list = this.cache.inst.get(stone);
|
|
25
28
|
if (list != null) {
|
|
@@ -52,6 +55,13 @@ class MemoryCache extends Register.inherits(() => BaseCache, true) {
|
|
|
52
55
|
return d.id == sourceId;
|
|
53
56
|
});
|
|
54
57
|
}
|
|
58
|
+
getValueIds(value) {
|
|
59
|
+
let _g = [];
|
|
60
|
+
let _g1 = 0;
|
|
61
|
+
let _g2 = value.data;
|
|
62
|
+
while (_g1 < _g2.length) _g.push(_g2[_g1++].id);
|
|
63
|
+
return _g;
|
|
64
|
+
}
|
|
55
65
|
mergePartial(stone, existing, addition, markComplete) {
|
|
56
66
|
let mergedData = existing.data.slice();
|
|
57
67
|
if (addition != null) {
|
|
@@ -13,6 +13,7 @@ export declare class Files extends Stone<FilesConfig> {
|
|
|
13
13
|
protected generateHash(): Promise<SourceHash>
|
|
14
14
|
protected list(): Promise<null | string[]>
|
|
15
15
|
protected generate(hash: SourceHash): Promise<SourceData[]>
|
|
16
|
+
protected generatePartial(sourceId: string, hash: SourceHash): Promise<null | SourceData[]>
|
|
16
17
|
protected walk<T>(onFile: ((arg0: string) => T), onDirFile: ((arg0: string, arg1: string) => T)): Promise<T[]>
|
|
17
18
|
protected fromCwd(file: string, dir: string): {
|
|
18
19
|
id: string,
|
package/bin/whet/stones/Files.js
CHANGED
|
@@ -7,6 +7,7 @@ import {SourceHash} from "../SourceHash.js"
|
|
|
7
7
|
import {SourceData} from "../Source.js"
|
|
8
8
|
import {Project} from "../Project.js"
|
|
9
9
|
import {Register} from "../../genes/Register.js"
|
|
10
|
+
import {Lambda} from "../../Lambda.js"
|
|
10
11
|
|
|
11
12
|
const $global = Register.$global
|
|
12
13
|
|
|
@@ -128,6 +129,55 @@ class Files extends Register.inherits(() => Stone, true) {
|
|
|
128
129
|
return Promise.all(fileProms);
|
|
129
130
|
});
|
|
130
131
|
}
|
|
132
|
+
generatePartial(sourceId, hash) {
|
|
133
|
+
let _gthis = this;
|
|
134
|
+
let onDirFile = function (dir, dirFile) {
|
|
135
|
+
let p_id;
|
|
136
|
+
let pathId = IdUtils.fromCwdPath(dirFile, RootDir.fromProject(_gthis.project));
|
|
137
|
+
if (dir == "/") {
|
|
138
|
+
p_id = pathId;
|
|
139
|
+
} else {
|
|
140
|
+
if (!(dir.length == 0 || dir.charCodeAt(dir.length - 1) == 47)) {
|
|
141
|
+
throw new Error("\"" + dir + "\" is not a directory.");
|
|
142
|
+
};
|
|
143
|
+
let p_id1;
|
|
144
|
+
let dir1 = pathId.substring(0, pathId.lastIndexOf("/") + 1);
|
|
145
|
+
p_id1 = ((dir1.length == 0) ? "./" : dir1).indexOf(dir) == 0;
|
|
146
|
+
p_id = (p_id1) ? pathId.substring(dir.length) : null;
|
|
147
|
+
};
|
|
148
|
+
return {"id": p_id, "cwd": dirFile, "pathId": pathId};
|
|
149
|
+
};
|
|
150
|
+
let files = [];
|
|
151
|
+
let dirs = [];
|
|
152
|
+
let _g = 0;
|
|
153
|
+
let _g1 = MaybeArray_Fields_.makeArray(this.config.paths);
|
|
154
|
+
while (_g < _g1.length) {
|
|
155
|
+
let path = _g1[_g];
|
|
156
|
+
++_g;
|
|
157
|
+
if (path.length == 0 || path.charCodeAt(path.length - 1) == 47) {
|
|
158
|
+
dirs.push(Utils.listDirectoryFiles(this.cwdPath(path), this.config.recursive).then(function (arr) {
|
|
159
|
+
let _g = 0;
|
|
160
|
+
while (_g < arr.length) files.push(onDirFile(path, arr[_g++]));
|
|
161
|
+
}));
|
|
162
|
+
} else {
|
|
163
|
+
let id = path;
|
|
164
|
+
files.push({"id": id.substring(id.lastIndexOf("/") + 1), "cwd": _gthis.cwdPath(path), "pathId": path});
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
return Promise.all(dirs).then(function (_) {
|
|
168
|
+
return files;
|
|
169
|
+
}).then(function (entries) {
|
|
170
|
+
let match = Lambda.find(entries, function (e) {
|
|
171
|
+
return e.id == sourceId;
|
|
172
|
+
});
|
|
173
|
+
if (match == null) {
|
|
174
|
+
return Promise.resolve(null);
|
|
175
|
+
};
|
|
176
|
+
return SourceData.fromFile(match.id, match.cwd, match.pathId).then(function (sd) {
|
|
177
|
+
return [sd];
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
131
181
|
walk(onFile, onDirFile) {
|
|
132
182
|
let files = [];
|
|
133
183
|
let dirs = [];
|
package/package.json
CHANGED