@weborigami/language 0.6.17 → 0.7.0-beta.1
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/index.ts +1 -0
- package/main.js +7 -1
- package/package.json +6 -6
- package/src/compiler/compile.js +10 -3
- package/src/compiler/optimize.js +71 -40
- package/src/compiler/parse.js +1 -1
- package/src/compiler/parserHelpers.js +5 -3
- package/src/handlers/getSource.js +11 -0
- package/src/handlers/htm_handler.js +1 -1
- package/src/handlers/js_handler.js +13 -4
- package/src/handlers/mediaTypeExtensions.json +15 -0
- package/src/handlers/ori_handler.js +8 -7
- package/src/handlers/oridocument_handler.js +17 -10
- package/src/handlers/processOriExport.js +17 -0
- package/src/handlers/tsv_handler.js +1 -1
- package/src/handlers/txt_handler.js +4 -2
- package/src/handlers/xhtml_handler.js +1 -1
- package/src/handlers/yaml_handler.js +6 -3
- package/src/project/activeProjectRoot.js +9 -0
- package/src/project/getGlobalsForTree.js +5 -0
- package/src/project/{projectGlobals.js → initializeGlobalsForTree.js} +8 -13
- package/src/project/jsGlobals.js +1 -0
- package/src/project/projectConfig.js +2 -2
- package/src/project/projectRootFromPath.js +2 -0
- package/src/protocols/constructHref.js +3 -3
- package/src/protocols/constructSiteTree.js +11 -2
- package/src/protocols/explore.js +1 -1
- package/src/protocols/explorehttp.js +1 -1
- package/src/protocols/fetchAndHandleExtension.js +23 -11
- package/src/protocols/files.js +1 -0
- package/src/protocols/http.js +4 -1
- package/src/protocols/https.js +4 -1
- package/src/protocols/httpstree.js +1 -1
- package/src/protocols/httptree.js +1 -1
- package/src/protocols/package.js +15 -3
- package/src/runtime/AsyncCacheTransform.d.ts +5 -0
- package/src/runtime/AsyncCacheTransform.js +134 -0
- package/src/runtime/HandleExtensionsTransform.d.ts +3 -1
- package/src/runtime/HandleExtensionsTransform.js +18 -2
- package/src/runtime/OrigamiFileMap.d.ts +5 -2
- package/src/runtime/OrigamiFileMap.js +27 -4
- package/src/runtime/ScopeMap.js +72 -0
- package/src/runtime/SyncCacheTransform.d.ts +8 -0
- package/src/runtime/SyncCacheTransform.js +133 -0
- package/src/runtime/SystemCacheMap.js +259 -0
- package/src/runtime/WatchFilesMixin.js +52 -19
- package/src/runtime/enableValueCaching.js +192 -0
- package/src/runtime/execute.js +2 -2
- package/src/runtime/executionContext.js +7 -0
- package/src/runtime/explainReferenceError.js +7 -2
- package/src/runtime/expressionObject.js +54 -46
- package/src/runtime/handleExtension.js +65 -34
- package/src/runtime/interop.js +2 -2
- package/src/runtime/mergeTrees.js +1 -1
- package/src/runtime/ops.js +28 -33
- package/src/runtime/symbols.js +3 -0
- package/src/runtime/systemCache.js +3 -0
- package/src/runtime/volatile.js +14 -0
- package/test/compiler/codeHelpers.js +2 -1
- package/test/compiler/optimize.test.js +62 -54
- package/test/handlers/ori_handler.test.js +22 -3
- package/test/handlers/oridocument_handler.test.js +1 -1
- package/test/protocols/https.test.js +19 -0
- package/test/protocols/package.test.js +7 -2
- package/test/runtime/AsyncCacheTransform.test.js +91 -0
- package/test/runtime/OrigamiFileMap.test.js +26 -23
- package/test/runtime/ScopeMap.test.js +49 -0
- package/test/runtime/SyncCacheTransform.test.js +93 -0
- package/test/runtime/SystemCacheMap.test.js +239 -0
- package/test/runtime/asyncCalcs.js +28 -0
- package/test/runtime/enableValueCaching.test.js +55 -0
- package/test/runtime/errors.test.js +53 -30
- package/test/runtime/evaluate.test.js +9 -4
- package/test/runtime/execute.test.js +6 -1
- package/test/runtime/expressionObject.test.js +55 -15
- package/test/runtime/fetchAndHandleExtension.test.js +24 -0
- package/test/runtime/fixtures/unpack/hello.json +1 -0
- package/test/runtime/handleExtension.test.js +12 -1
- package/test/runtime/ops.test.js +70 -65
- package/test/runtime/syncCalcs.js +27 -0
- package/test/runtime/systemCache.test.js +66 -0
- package/src/runtime/assignPropertyDescriptors.js +0 -23
- package/src/runtime/asyncStorage.js +0 -7
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { ObjectMap } from "@weborigami/async-tree";
|
|
2
2
|
import assert from "node:assert";
|
|
3
3
|
import { describe, test } from "node:test";
|
|
4
|
+
import * as handlers from "../../src/handlers/handlers.js";
|
|
4
5
|
import handleExtension from "../../src/runtime/handleExtension.js";
|
|
6
|
+
import OrigamiFileMap from "../../src/runtime/OrigamiFileMap.js";
|
|
7
|
+
import { cachePathSymbol } from "../../src/runtime/symbols.js";
|
|
8
|
+
|
|
9
|
+
const fixturesUrl = new URL("fixtures/unpack", import.meta.url);
|
|
10
|
+
const fixtureFiles = new OrigamiFileMap(fixturesUrl);
|
|
11
|
+
fixtureFiles[cachePathSymbol] = "fixtures";
|
|
12
|
+
fixtureFiles.globals = handlers;
|
|
5
13
|
|
|
6
14
|
describe("handleExtension", () => {
|
|
7
15
|
test("attaches an unpack method to a value with an extension", async () => {
|
|
@@ -10,7 +18,10 @@ describe("handleExtension", () => {
|
|
|
10
18
|
assert(typeof numberValue === "number");
|
|
11
19
|
assert.equal(numberValue, 1);
|
|
12
20
|
const jsonFile = await fixture.get("bar.json");
|
|
13
|
-
const
|
|
21
|
+
const globals = {
|
|
22
|
+
json_handler: { unpack: async (data) => JSON.parse(data) },
|
|
23
|
+
};
|
|
24
|
+
const withHandler = handleExtension(jsonFile, "bar.json", globals, fixture);
|
|
14
25
|
assert.equal(String(withHandler), `{ "bar": 2 }`);
|
|
15
26
|
const data = await withHandler.unpack();
|
|
16
27
|
assert.deepEqual(data, { bar: 2 });
|
package/test/runtime/ops.test.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { ObjectMap, Tree } from "@weborigami/async-tree";
|
|
2
2
|
import assert from "node:assert";
|
|
3
|
-
import { describe, test } from "node:test";
|
|
3
|
+
import { beforeEach, describe, test } from "node:test";
|
|
4
4
|
|
|
5
5
|
import execute from "../../src/runtime/execute.js";
|
|
6
6
|
import { ops } from "../../src/runtime/internal.js";
|
|
7
|
+
import systemCache from "../../src/runtime/systemCache.js";
|
|
7
8
|
import { createCode } from "../compiler/codeHelpers.js";
|
|
8
9
|
|
|
9
10
|
describe("ops", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
systemCache.clear();
|
|
13
|
+
});
|
|
14
|
+
|
|
10
15
|
test("ops.addition adds two numbers", async () => {
|
|
11
16
|
assert.strictEqual(ops.addition(2, 2), 4);
|
|
12
17
|
assert.strictEqual(ops.addition(2, true), 3);
|
|
@@ -49,6 +54,15 @@ describe("ops", () => {
|
|
|
49
54
|
assert.strictEqual(ops.bitwiseXor(5, 3), 6);
|
|
50
55
|
});
|
|
51
56
|
|
|
57
|
+
test("ops.cache", async () => {
|
|
58
|
+
const fn = () => 1;
|
|
59
|
+
const code = createCode([ops.object, ["a", [ops.getter, [fn]]]]);
|
|
60
|
+
const result = await ops.cache("a.ori/_refs/b.ori/", code, {});
|
|
61
|
+
assert.deepEqual(await Tree.plain(result), { a: 1 });
|
|
62
|
+
const cachedResult = await systemCache.get("a.ori/_refs/b.ori/");
|
|
63
|
+
assert.strictEqual(await cachedResult.value.a, 1);
|
|
64
|
+
});
|
|
65
|
+
|
|
52
66
|
test("ops.comma returns the last value", async () => {
|
|
53
67
|
const code = createCode([ops.comma, 1, 2, 3]);
|
|
54
68
|
const result = await execute(code);
|
|
@@ -70,17 +84,17 @@ describe("ops", () => {
|
|
|
70
84
|
});
|
|
71
85
|
|
|
72
86
|
test("ops.deepText concatenates tree value text", async () => {
|
|
73
|
-
const
|
|
87
|
+
const parent = new ObjectMap({
|
|
74
88
|
name: "world",
|
|
75
|
-
};
|
|
89
|
+
});
|
|
76
90
|
const code = createCode([
|
|
77
91
|
ops.deepText,
|
|
78
92
|
"Hello, ",
|
|
79
|
-
[[ops.scope
|
|
93
|
+
[[ops.scope], "name"],
|
|
80
94
|
".",
|
|
81
95
|
]);
|
|
82
96
|
|
|
83
|
-
const result = await execute(code);
|
|
97
|
+
const result = await execute(code, { parent });
|
|
84
98
|
assert.strictEqual(result, "Hello, world.");
|
|
85
99
|
});
|
|
86
100
|
|
|
@@ -106,27 +120,22 @@ describe("ops", () => {
|
|
|
106
120
|
|
|
107
121
|
test("ops.cache evaluates code and cache its result", async () => {
|
|
108
122
|
let count = 0;
|
|
109
|
-
const
|
|
123
|
+
const parent = new ObjectMap({
|
|
110
124
|
group: {
|
|
111
125
|
get count() {
|
|
112
126
|
// Use promise to test async behavior
|
|
113
127
|
return Promise.resolve(++count);
|
|
114
128
|
},
|
|
115
129
|
},
|
|
116
|
-
};
|
|
130
|
+
});
|
|
117
131
|
const code = createCode([
|
|
118
132
|
ops.cache,
|
|
119
|
-
|
|
120
|
-
"group
|
|
121
|
-
[
|
|
122
|
-
[ops.scope, container],
|
|
123
|
-
[ops.literal, "group"],
|
|
124
|
-
[ops.literal, "count"],
|
|
125
|
-
],
|
|
133
|
+
"_refs/test.ori/group/count",
|
|
134
|
+
[[ops.scope], [ops.literal, "group"], [ops.literal, "count"]],
|
|
126
135
|
]);
|
|
127
|
-
const result = await execute(code);
|
|
136
|
+
const result = await execute(code, { parent });
|
|
128
137
|
assert.strictEqual(result, 1);
|
|
129
|
-
const result2 = await execute(code);
|
|
138
|
+
const result2 = await execute(code, { parent });
|
|
130
139
|
assert.strictEqual(result2, 1);
|
|
131
140
|
});
|
|
132
141
|
|
|
@@ -146,6 +155,7 @@ describe("ops", () => {
|
|
|
146
155
|
describe("ops.flat", () => {
|
|
147
156
|
test("flattens arrays", async () => {
|
|
148
157
|
assert.deepEqual(await ops.flat(1, 2, [3]), [1, 2, 3]);
|
|
158
|
+
assert.deepEqual(await ops.flat([1], [2], [[3]]), [1, 2, [3]]);
|
|
149
159
|
});
|
|
150
160
|
|
|
151
161
|
test("flattens maplike objects", async () => {
|
|
@@ -171,6 +181,15 @@ describe("ops", () => {
|
|
|
171
181
|
3: 8,
|
|
172
182
|
});
|
|
173
183
|
});
|
|
184
|
+
|
|
185
|
+
test("flattens arrays of objects", async () => {
|
|
186
|
+
const result = await ops.flat([{ a: 1 }], [{ b: 2 }, { c: 3 }]);
|
|
187
|
+
assert.deepEqual(await Tree.plain(result), [
|
|
188
|
+
{ a: 1 },
|
|
189
|
+
{ b: 2 },
|
|
190
|
+
{ c: 3 },
|
|
191
|
+
]);
|
|
192
|
+
});
|
|
174
193
|
});
|
|
175
194
|
|
|
176
195
|
test("ops.greaterThan", () => {
|
|
@@ -225,18 +244,18 @@ describe("ops", () => {
|
|
|
225
244
|
});
|
|
226
245
|
|
|
227
246
|
test("ops.lambda defines a function with underscore input", async () => {
|
|
228
|
-
const
|
|
247
|
+
const parent = new ObjectMap({
|
|
229
248
|
message: "Hello",
|
|
230
|
-
};
|
|
249
|
+
});
|
|
231
250
|
|
|
232
251
|
const code = createCode([
|
|
233
252
|
ops.lambda,
|
|
234
253
|
1,
|
|
235
254
|
[["_", [[ops.params, 0], 0]]],
|
|
236
|
-
[[ops.scope
|
|
255
|
+
[[ops.scope], "message"],
|
|
237
256
|
]);
|
|
238
257
|
|
|
239
|
-
const fn = await execute(code);
|
|
258
|
+
const fn = await execute(code, { parent });
|
|
240
259
|
assert.equal(fn.length, 1);
|
|
241
260
|
const result = await fn();
|
|
242
261
|
assert.strictEqual(result, "Hello");
|
|
@@ -308,9 +327,9 @@ describe("ops", () => {
|
|
|
308
327
|
// ...more
|
|
309
328
|
// c: a
|
|
310
329
|
// }
|
|
311
|
-
const
|
|
330
|
+
const parent = new ObjectMap({
|
|
312
331
|
more: { b: 2 },
|
|
313
|
-
};
|
|
332
|
+
});
|
|
314
333
|
const code = createCode([
|
|
315
334
|
[
|
|
316
335
|
ops.object,
|
|
@@ -321,14 +340,14 @@ describe("ops", () => {
|
|
|
321
340
|
[
|
|
322
341
|
ops.merge,
|
|
323
342
|
[ops.object, ["a", [ops.getter, [[ops.inherited, 1], "a"]]]],
|
|
324
|
-
[[ops.scope
|
|
343
|
+
[[ops.scope], "more"],
|
|
325
344
|
[ops.object, ["c", [ops.getter, [[ops.inherited, 1], "c"]]]],
|
|
326
345
|
],
|
|
327
346
|
],
|
|
328
347
|
],
|
|
329
348
|
"_result",
|
|
330
349
|
]);
|
|
331
|
-
const result = await execute(code);
|
|
350
|
+
const result = await execute(code, { parent });
|
|
332
351
|
assert.deepEqual(await Tree.plain(result), { a: 1, b: 2, c: 1 });
|
|
333
352
|
});
|
|
334
353
|
|
|
@@ -339,31 +358,6 @@ describe("ops", () => {
|
|
|
339
358
|
assert.strictEqual(ops.multiplication("foo", 2), NaN);
|
|
340
359
|
});
|
|
341
360
|
|
|
342
|
-
test("ops.objectRest returns an object without specified keys", async () => {
|
|
343
|
-
const obj = {
|
|
344
|
-
a: 1,
|
|
345
|
-
b: 2,
|
|
346
|
-
c: 3,
|
|
347
|
-
};
|
|
348
|
-
const result = await ops.objectRest(obj, ["a", "b"]);
|
|
349
|
-
assert.deepEqual(result, { c: 3 });
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
test("ops.optional", async () => {
|
|
353
|
-
assert.equal(
|
|
354
|
-
ops.optional(null, (x) => x.a),
|
|
355
|
-
undefined,
|
|
356
|
-
);
|
|
357
|
-
assert.equal(
|
|
358
|
-
ops.optional(undefined, (x) => x.a),
|
|
359
|
-
undefined,
|
|
360
|
-
);
|
|
361
|
-
assert.equal(
|
|
362
|
-
ops.optional({ a: 1 }, (x) => x.a),
|
|
363
|
-
1,
|
|
364
|
-
);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
361
|
test("ops.notEqual", () => {
|
|
368
362
|
assert(!ops.notEqual(1, 1));
|
|
369
363
|
assert(ops.notEqual(1, 2));
|
|
@@ -390,33 +384,44 @@ describe("ops", () => {
|
|
|
390
384
|
});
|
|
391
385
|
|
|
392
386
|
test("ops.object instantiates an object", async () => {
|
|
393
|
-
const
|
|
387
|
+
const parent = new ObjectMap({
|
|
394
388
|
upper: (s) => s.toUpperCase(),
|
|
395
|
-
};
|
|
389
|
+
});
|
|
396
390
|
|
|
397
391
|
const code = createCode([
|
|
398
392
|
ops.object,
|
|
399
|
-
["hello", [[[ops.scope
|
|
400
|
-
["world", [[[ops.scope
|
|
393
|
+
["hello", [[[ops.scope], "upper"], "hello"]],
|
|
394
|
+
["world", [[[ops.scope], "upper"], "world"]],
|
|
401
395
|
]);
|
|
402
396
|
|
|
403
|
-
const result = await execute(code);
|
|
397
|
+
const result = await execute(code, { parent });
|
|
404
398
|
assert.strictEqual(result.hello, "HELLO");
|
|
405
399
|
assert.strictEqual(result.world, "WORLD");
|
|
406
400
|
});
|
|
407
401
|
|
|
408
|
-
test("ops.
|
|
409
|
-
const
|
|
410
|
-
|
|
402
|
+
test("ops.objectRest returns an object without specified keys", async () => {
|
|
403
|
+
const obj = {
|
|
404
|
+
a: 1,
|
|
405
|
+
b: 2,
|
|
406
|
+
c: 3,
|
|
411
407
|
};
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
408
|
+
const result = await ops.objectRest(obj, ["a", "b"]);
|
|
409
|
+
assert.deepEqual(result, { c: 3 });
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
test("ops.optional", async () => {
|
|
413
|
+
assert.equal(
|
|
414
|
+
ops.optional(null, (x) => x.a),
|
|
415
|
+
undefined,
|
|
416
|
+
);
|
|
417
|
+
assert.equal(
|
|
418
|
+
ops.optional(undefined, (x) => x.a),
|
|
419
|
+
undefined,
|
|
420
|
+
);
|
|
421
|
+
assert.equal(
|
|
422
|
+
ops.optional({ a: 1 }, (x) => x.a),
|
|
415
423
|
1,
|
|
416
|
-
|
|
417
|
-
]);
|
|
418
|
-
const result = await execute(code);
|
|
419
|
-
assert.deepEqual(result, ["Hello", 1, "WORLD"]);
|
|
424
|
+
);
|
|
420
425
|
});
|
|
421
426
|
|
|
422
427
|
test("ops.params returns a stack frame", async () => {
|
|
@@ -455,7 +460,7 @@ describe("ops", () => {
|
|
|
455
460
|
);
|
|
456
461
|
const a = await tree.get("a");
|
|
457
462
|
const b = await a.get("b");
|
|
458
|
-
const scope = await ops.scope(b);
|
|
463
|
+
const scope = await ops.scope({ parent: b });
|
|
459
464
|
assert.equal(await scope?.get("c"), 1);
|
|
460
465
|
});
|
|
461
466
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SyncMap } from "@weborigami/async-tree";
|
|
2
|
+
import SyncCacheTransform from "../../src/runtime/SyncCacheTransform.js";
|
|
3
|
+
|
|
4
|
+
export default function syncCalcs(iterable) {
|
|
5
|
+
const data = new (SyncCacheTransform(SyncMap))(iterable);
|
|
6
|
+
const calcs = new (SyncCacheTransform(SyncResultsMap))(data);
|
|
7
|
+
return { calcs, data };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class SyncResultsMap extends SyncMap {
|
|
11
|
+
constructor(source) {
|
|
12
|
+
super();
|
|
13
|
+
this.source = source;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get(key) {
|
|
17
|
+
let value = this.source.get(key);
|
|
18
|
+
if (typeof value === "function") {
|
|
19
|
+
value = value();
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
keys() {
|
|
25
|
+
return this.source.keys();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests of several forms of caching using the system cache
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { SyncMap, Tree } from "@weborigami/async-tree";
|
|
6
|
+
import assert from "node:assert";
|
|
7
|
+
import { describe, test } from "node:test";
|
|
8
|
+
import * as handlers from "../../src/handlers/handlers.js";
|
|
9
|
+
import HandleExtensionsTransform from "../../src/runtime/HandleExtensionsTransform.js";
|
|
10
|
+
import SyncCacheTransform from "../../src/runtime/SyncCacheTransform.js";
|
|
11
|
+
import { cachePathSymbol } from "../../src/runtime/symbols.js";
|
|
12
|
+
|
|
13
|
+
describe("systemCache", () => {
|
|
14
|
+
test("property based on external scope recalculates when scope changes", async () => {
|
|
15
|
+
// Virtual src folder
|
|
16
|
+
const src = new OrigamiSyncMap([
|
|
17
|
+
[
|
|
18
|
+
"site.ori",
|
|
19
|
+
`
|
|
20
|
+
{
|
|
21
|
+
value = data.json/
|
|
22
|
+
}
|
|
23
|
+
`,
|
|
24
|
+
],
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
// Virtual project root folder
|
|
28
|
+
const project = new OrigamiSyncMap([
|
|
29
|
+
["data.json", "1"],
|
|
30
|
+
["src", src],
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
// Make src a child of the project root
|
|
34
|
+
src.parent = project;
|
|
35
|
+
|
|
36
|
+
// Paths are optional but make cache keys more meaningful
|
|
37
|
+
src[cachePathSymbol] = "project/src";
|
|
38
|
+
project[cachePathSymbol] = "project";
|
|
39
|
+
|
|
40
|
+
// Add handlers so we can unpack values
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
project.globals = handlers;
|
|
43
|
+
|
|
44
|
+
const site = await Tree.traverseOrThrow(project, "src/", "site.ori/");
|
|
45
|
+
|
|
46
|
+
const value1 = await site.value;
|
|
47
|
+
assert.equal(value1, 1);
|
|
48
|
+
|
|
49
|
+
// Add new data.json to src folder, overriding the one in project root
|
|
50
|
+
src.set("data.json", "2");
|
|
51
|
+
|
|
52
|
+
const value2 = await site.value;
|
|
53
|
+
assert.equal(value2, 2);
|
|
54
|
+
|
|
55
|
+
// Delete data.json from src folder, reverting to the one in project root
|
|
56
|
+
src.delete("data.json");
|
|
57
|
+
|
|
58
|
+
const value3 = await site.value;
|
|
59
|
+
assert.equal(value3, 1);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Like OrigamiFileMap, but in memory
|
|
64
|
+
class OrigamiSyncMap extends SyncCacheTransform(
|
|
65
|
+
HandleExtensionsTransform(SyncMap),
|
|
66
|
+
) {}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This is an analogue of Object.assign that destructively copies properties to
|
|
3
|
-
* a target object -- but avoids invoking property getters. Instead, it copies
|
|
4
|
-
* property descriptors over to the target object.
|
|
5
|
-
*
|
|
6
|
-
* @param {any} target
|
|
7
|
-
* @param {...any} sources
|
|
8
|
-
*/
|
|
9
|
-
export default function assignPropertyDescriptors(target, ...sources) {
|
|
10
|
-
for (const source of sources) {
|
|
11
|
-
const descriptors = Object.getOwnPropertyDescriptors(source);
|
|
12
|
-
for (const [key, descriptor] of Object.entries(descriptors)) {
|
|
13
|
-
if (descriptor.value !== undefined) {
|
|
14
|
-
// Simple value, copy it
|
|
15
|
-
target[key] = descriptor.value;
|
|
16
|
-
} else {
|
|
17
|
-
// Getter and/or setter, copy the descriptor
|
|
18
|
-
Object.defineProperty(target, key, descriptor);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return target;
|
|
23
|
-
}
|