@reventlessdev/reventless-aws 3.0.0-alpha.334 → 3.0.0-alpha.335
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/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.335 (2026-09-02)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **aws:** a deployed Lambda carries its plugin's own dependencies ([8dfac24](https://github.com/ReventlessDev/reventless-core/commit/8dfac24285723beb74f84c6cc821864e56ee75a2))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.334 (2026-09-02)
|
|
7
14
|
|
|
8
15
|
* feat(aws)!: the messaging sender is configuration, and a stack can choose to only log ([23b8b4b](https://github.com/ReventlessDev/reventless-core/commit/23b8b4bfe9c70555de4d74266ca686cb427485ca))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.335",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
"@aws-sdk/s3-request-presigner": "3.970.0",
|
|
16
16
|
"sury": "11.0.0-rc.2",
|
|
17
17
|
"uuid": "^13.0.0",
|
|
18
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
19
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
20
18
|
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.14",
|
|
19
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
21
20
|
"@reventlessdev/rescript-node": "2.0.0-alpha.8",
|
|
22
21
|
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.7",
|
|
22
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
23
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
23
24
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
24
25
|
"@reventlessdev/reventless-core": "3.0.0-alpha.254",
|
|
25
26
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.155",
|
|
26
|
-
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
27
27
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.34",
|
|
28
28
|
"@reventlessdev/reventless-postgres": "3.0.0-alpha.118",
|
|
29
29
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.127"
|
package/src/util/Util_Bundle.res
CHANGED
|
@@ -349,6 +349,126 @@ let isRuntimeProvided = (specifier: string, ~pkgName: string): bool =>
|
|
|
349
349
|
}
|
|
350
350
|
)
|
|
351
351
|
|
|
352
|
+
// Memo for isFrameworkPackage — one require.resolve per package name per deploy.
|
|
353
|
+
let frameworkPackageCache: dict<bool> = Dict.make()
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Whether the framework's own module resolution reaches this package: the
|
|
357
|
+
* deploy-time stand-in for "the Lambda layer already carries it", since the
|
|
358
|
+
* layer is built from reventless-aws's dependency closure.
|
|
359
|
+
*
|
|
360
|
+
* Deliberately framework-rooted only — no Pulumi-project fallback. The question
|
|
361
|
+
* here is the opposite of `resolvePackageRoot`'s: a package the *project*
|
|
362
|
+
* resolves but the framework does not is precisely a user package, which is
|
|
363
|
+
* what the closure walk below must treat as a starting point rather than as
|
|
364
|
+
* something the runtime provides.
|
|
365
|
+
*/
|
|
366
|
+
let isFrameworkPackage = (pkgName: string): bool =>
|
|
367
|
+
switch frameworkPackageCache->Dict.get(pkgName) {
|
|
368
|
+
| Some(known) => known
|
|
369
|
+
| None =>
|
|
370
|
+
let known = try {
|
|
371
|
+
let _ = localRequire->NodeModule.requireResolve(pkgName ++ "/package.json")
|
|
372
|
+
true
|
|
373
|
+
} catch {
|
|
374
|
+
| _ => false
|
|
375
|
+
}
|
|
376
|
+
frameworkPackageCache->Dict.set(pkgName, known)
|
|
377
|
+
known
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// The distinct bare packages a package's own bundled files import, memoised per
|
|
381
|
+
// package root: the same file set the archive carries, so what is scanned is
|
|
382
|
+
// what is deployed.
|
|
383
|
+
let importedPackagesCache: dict<array<string>> = Dict.make()
|
|
384
|
+
let importedPackages = (packageRoot: string): array<string> =>
|
|
385
|
+
switch importedPackagesCache->Dict.get(packageRoot) {
|
|
386
|
+
| Some(names) => names
|
|
387
|
+
| None =>
|
|
388
|
+
let assets: dict<Pulumi.Archive.assetOrArchive> = Dict.make()
|
|
389
|
+
let paths: array<(string, string)> = []
|
|
390
|
+
walkDir(~dir=packageRoot, ~prefix="", ~assets, ~paths)
|
|
391
|
+
let names: array<string> = []
|
|
392
|
+
paths->Array.forEach(((relPath, absPath)) =>
|
|
393
|
+
if relPath->String.endsWith(".mjs") || relPath->String.endsWith(".js") {
|
|
394
|
+
NodeFs.readFileSync(absPath)
|
|
395
|
+
->staticImportSpecifiers
|
|
396
|
+
->Array.forEach(specifier =>
|
|
397
|
+
if isBareSpecifier(specifier) {
|
|
398
|
+
let name = extractPackageName(specifier)
|
|
399
|
+
if !(names->Array.includes(name)) {
|
|
400
|
+
names->Array.push(name)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
)
|
|
406
|
+
importedPackagesCache->Dict.set(packageRoot, names)
|
|
407
|
+
names
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Resolve a package root as the importing package would — from that package's
|
|
412
|
+
* own directory. Under pnpm a plugin's dependency is reachable only from the
|
|
413
|
+
* plugin, never from the framework or the Pulumi project, so this is the one
|
|
414
|
+
* rooting that finds it. `None` when nothing is installed there: the archive
|
|
415
|
+
* cannot carry what is not on disk, and a package the code truly imported at
|
|
416
|
+
* runtime would already be failing today.
|
|
417
|
+
*/
|
|
418
|
+
let resolvePackageRootFrom = (~fromRoot: string, pkgName: string): option<string> =>
|
|
419
|
+
try {
|
|
420
|
+
Some(
|
|
421
|
+
NodePath.dirname(
|
|
422
|
+
NodeModule.createRequire(NodePath.join([fromRoot, "index.js"]))
|
|
423
|
+
->NodeModule.requireResolve(pkgName ++ "/package.json"),
|
|
424
|
+
),
|
|
425
|
+
)
|
|
426
|
+
} catch {
|
|
427
|
+
| _ => None
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Carry every package a bundled *user* package imports into the archive, and
|
|
432
|
+
* every package those import in turn.
|
|
433
|
+
*
|
|
434
|
+
* A plugin package rides in `/var/task/node_modules/<plugin>`, but its own
|
|
435
|
+
* dependencies do not: the layer holds reventless-aws's closure, which a domain
|
|
436
|
+
* trait or any other plugin-level library is by definition outside of. The
|
|
437
|
+
* import then fails at the first command the slice handles — a green deploy and
|
|
438
|
+
* a `Cannot find package` at runtime.
|
|
439
|
+
*
|
|
440
|
+
* Framework packages are the starting points that are skipped, not walked:
|
|
441
|
+
* their closure is the layer's, and their sources reach deploy-time-only
|
|
442
|
+
* imports (the @pulumi bindings) that no Lambda loads — walking them would add
|
|
443
|
+
* tens of megabytes to every archive.
|
|
444
|
+
*/
|
|
445
|
+
let addImportedPackageClosure = (packageDirs: dict<string>): unit => {
|
|
446
|
+
let pending =
|
|
447
|
+
packageDirs->Dict.toArray->Array.filter(((pkgName, _)) => !isFrameworkPackage(pkgName))
|
|
448
|
+
let next = ref(0)
|
|
449
|
+
while next.contents < pending->Array.length {
|
|
450
|
+
let (_, pkgRoot) = pending->Array.getUnsafe(next.contents)
|
|
451
|
+
next := next.contents + 1
|
|
452
|
+
importedPackages(pkgRoot)->Array.forEach(dep =>
|
|
453
|
+
if (
|
|
454
|
+
!(packageDirs->Dict.has(dep)) &&
|
|
455
|
+
!(dep->String.startsWith("node:")) &&
|
|
456
|
+
!(nodeBuiltins->Set.has(dep)) &&
|
|
457
|
+
!(dep->String.startsWith("@aws-sdk/")) &&
|
|
458
|
+
!(dep->String.startsWith("@smithy/")) &&
|
|
459
|
+
!isFrameworkPackage(dep)
|
|
460
|
+
) {
|
|
461
|
+
switch resolvePackageRootFrom(~fromRoot=pkgRoot, dep) {
|
|
462
|
+
| Some(depRoot) =>
|
|
463
|
+
packageDirs->Dict.set(dep, depRoot)
|
|
464
|
+
pending->Array.push((dep, depRoot))
|
|
465
|
+
| None => ()
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
)
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
352
472
|
/**
|
|
353
473
|
* Deploy-time guard: every bare package statically imported by a bundled
|
|
354
474
|
* runtime-extension (or companion) package must either be in the archive
|
|
@@ -451,7 +571,12 @@ let buildCodeArchive = (
|
|
|
451
571
|
// deploy whose extensions are silently skipped.
|
|
452
572
|
if bundleRuntimeExtensions {
|
|
453
573
|
let extensionPackages = allPackageDirs->addRuntimeExtensionPackages
|
|
574
|
+
// The closure runs first so a companion package that is simply a static
|
|
575
|
+
// import of the extension is already bundled by the time the guard looks.
|
|
576
|
+
allPackageDirs->addImportedPackageClosure
|
|
454
577
|
assertRuntimeExtensionImportsResolvable(~extensionPackages, ~bundledPackages=allPackageDirs)
|
|
578
|
+
} else {
|
|
579
|
+
allPackageDirs->addImportedPackageClosure
|
|
455
580
|
}
|
|
456
581
|
let packageContentHashes: ref<array<string>> = ref([])
|
|
457
582
|
allPackageDirs->Dict.forEachWithKey((pkgRoot, pkgName) => {
|
|
@@ -232,6 +232,87 @@ function isRuntimeProvided(specifier, pkgName) {
|
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
let frameworkPackageCache = {};
|
|
236
|
+
|
|
237
|
+
function isFrameworkPackage(pkgName) {
|
|
238
|
+
let known = frameworkPackageCache[pkgName];
|
|
239
|
+
if (known !== undefined) {
|
|
240
|
+
return known;
|
|
241
|
+
}
|
|
242
|
+
let known$1;
|
|
243
|
+
try {
|
|
244
|
+
localRequire.resolve(pkgName + "/package.json");
|
|
245
|
+
known$1 = true;
|
|
246
|
+
} catch (exn) {
|
|
247
|
+
known$1 = false;
|
|
248
|
+
}
|
|
249
|
+
frameworkPackageCache[pkgName] = known$1;
|
|
250
|
+
return known$1;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
let importedPackagesCache = {};
|
|
254
|
+
|
|
255
|
+
function importedPackages(packageRoot) {
|
|
256
|
+
let names = importedPackagesCache[packageRoot];
|
|
257
|
+
if (names !== undefined) {
|
|
258
|
+
return names;
|
|
259
|
+
}
|
|
260
|
+
let assets = {};
|
|
261
|
+
let paths = [];
|
|
262
|
+
walkDir(packageRoot, "", assets, paths);
|
|
263
|
+
let names$1 = [];
|
|
264
|
+
paths.forEach(param => {
|
|
265
|
+
let relPath = param[0];
|
|
266
|
+
if (relPath.endsWith(".mjs") || relPath.endsWith(".js")) {
|
|
267
|
+
staticImportSpecifiers(Nodefs.readFileSync(param[1], "utf8")).forEach(specifier => {
|
|
268
|
+
if (!isBareSpecifier(specifier)) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
let name = extractPackageName(specifier);
|
|
272
|
+
if (!names$1.includes(name)) {
|
|
273
|
+
names$1.push(name);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
importedPackagesCache[packageRoot] = names$1;
|
|
281
|
+
return names$1;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function resolvePackageRootFrom(fromRoot, pkgName) {
|
|
285
|
+
try {
|
|
286
|
+
return Nodepath.dirname(Nodemodule.createRequire(Nodepath.join(fromRoot, "index.js")).resolve(pkgName + "/package.json"));
|
|
287
|
+
} catch (exn) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function addImportedPackageClosure(packageDirs) {
|
|
293
|
+
let pending = Object.entries(packageDirs).filter(param => !isFrameworkPackage(param[0]));
|
|
294
|
+
let next = 0;
|
|
295
|
+
while (next < pending.length) {
|
|
296
|
+
let match = pending[next];
|
|
297
|
+
let pkgRoot = match[1];
|
|
298
|
+
next = next + 1 | 0;
|
|
299
|
+
importedPackages(pkgRoot).forEach(dep => {
|
|
300
|
+
if (dep in packageDirs || dep.startsWith("node:") || nodeBuiltins.has(dep) || dep.startsWith("@aws-sdk/") || dep.startsWith("@smithy/") || isFrameworkPackage(dep)) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
let depRoot = resolvePackageRootFrom(pkgRoot, dep);
|
|
304
|
+
if (depRoot !== undefined) {
|
|
305
|
+
packageDirs[dep] = depRoot;
|
|
306
|
+
pending.push([
|
|
307
|
+
dep,
|
|
308
|
+
depRoot
|
|
309
|
+
]);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
235
316
|
function assertRuntimeExtensionImportsResolvable(extensionPackages, bundledPackages) {
|
|
236
317
|
Stdlib_Dict.forEachWithKey(extensionPackages, (pkgRoot, pkgName) => {
|
|
237
318
|
let assets = {};
|
|
@@ -275,7 +356,10 @@ function buildCodeArchive(entryPointModule, packageDirs, extraStringAssetsOpt, b
|
|
|
275
356
|
}
|
|
276
357
|
if (bundleRuntimeExtensions) {
|
|
277
358
|
let extensionPackages = addRuntimeExtensionPackages(allPackageDirs);
|
|
359
|
+
addImportedPackageClosure(allPackageDirs);
|
|
278
360
|
assertRuntimeExtensionImportsResolvable(extensionPackages, allPackageDirs);
|
|
361
|
+
} else {
|
|
362
|
+
addImportedPackageClosure(allPackageDirs);
|
|
279
363
|
}
|
|
280
364
|
let packageContentHashes = {
|
|
281
365
|
contents: []
|
|
@@ -326,6 +410,12 @@ export {
|
|
|
326
410
|
isBareSpecifier,
|
|
327
411
|
nodeBuiltins,
|
|
328
412
|
isRuntimeProvided,
|
|
413
|
+
frameworkPackageCache,
|
|
414
|
+
isFrameworkPackage,
|
|
415
|
+
importedPackagesCache,
|
|
416
|
+
importedPackages,
|
|
417
|
+
resolvePackageRootFrom,
|
|
418
|
+
addImportedPackageClosure,
|
|
329
419
|
assertRuntimeExtensionImportsResolvable,
|
|
330
420
|
buildCodeArchive,
|
|
331
421
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// A plugin package rides in the code archive, but its own dependencies used to
|
|
4
|
+
// stay behind: the layer carries reventless-aws's closure, and a domain trait
|
|
5
|
+
// (or any other plugin-level library) is outside it. The archive build now
|
|
6
|
+
// walks what a bundled user package actually imports.
|
|
7
|
+
|
|
8
|
+
let mkTmpRoot = (prefix: string) =>
|
|
9
|
+
NodeFs.realpathSync(NodeFs.mkdtempSync(NodePath.join([NodeOs.tmpdir(), prefix])))
|
|
10
|
+
|
|
11
|
+
let writePkg = (~root: string, ~name: string, files: array<(string, string)>): string => {
|
|
12
|
+
NodeFs.mkdirSync(root, {recursive: true})
|
|
13
|
+
NodeFs.writeFileSync(
|
|
14
|
+
NodePath.join([root, "package.json"]),
|
|
15
|
+
`{"name":"${name}","version":"1.0.0"}`,
|
|
16
|
+
)
|
|
17
|
+
files->Array.forEach(((relPath, content)) => {
|
|
18
|
+
let abs = NodePath.join([root, relPath])
|
|
19
|
+
NodeFs.mkdirSync(NodePath.dirname(abs), {recursive: true})
|
|
20
|
+
NodeFs.writeFileSync(abs, content)
|
|
21
|
+
})
|
|
22
|
+
root
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// A user package whose slice imports a trait, the trait imports a rules
|
|
26
|
+
// package, and both are installed only under the user package — the pnpm shape,
|
|
27
|
+
// where nothing but the importer can resolve them.
|
|
28
|
+
let hostName = "@fixture/closure-host"
|
|
29
|
+
let traitName = "@fixture/closure-trait"
|
|
30
|
+
let rulesName = "@fixture/closure-rules"
|
|
31
|
+
|
|
32
|
+
let hostRoot = mkTmpRoot("bundle-closure-")
|
|
33
|
+
let traitRoot = NodePath.join([hostRoot, "node_modules", traitName])
|
|
34
|
+
let rulesRoot = NodePath.join([hostRoot, "node_modules", rulesName])
|
|
35
|
+
|
|
36
|
+
let _ = writePkg(
|
|
37
|
+
~root=hostRoot,
|
|
38
|
+
~name=hostName,
|
|
39
|
+
[
|
|
40
|
+
(
|
|
41
|
+
"src/Slice.res.mjs",
|
|
42
|
+
`import * as Trait from "${traitName}";
|
|
43
|
+
import * as Sury from "sury";
|
|
44
|
+
import * as Fs from "node:fs";
|
|
45
|
+
import * as Path from "path";
|
|
46
|
+
import * as Ddb from "@aws-sdk/client-dynamodb";
|
|
47
|
+
import * as Missing from "@fixture/never-installed";
|
|
48
|
+
export const handle = () => Trait.rule(Sury, Fs, Path, Ddb, Missing);
|
|
49
|
+
`,
|
|
50
|
+
),
|
|
51
|
+
],
|
|
52
|
+
)
|
|
53
|
+
let _ = writePkg(
|
|
54
|
+
~root=traitRoot,
|
|
55
|
+
~name=traitName,
|
|
56
|
+
[
|
|
57
|
+
(
|
|
58
|
+
"src/Trait.res.mjs",
|
|
59
|
+
`import * as Rules from "${rulesName}";
|
|
60
|
+
export const rule = () => Rules.check();
|
|
61
|
+
`,
|
|
62
|
+
),
|
|
63
|
+
],
|
|
64
|
+
)
|
|
65
|
+
let _ = writePkg(~root=rulesRoot, ~name=rulesName, [("src/Rules.res.mjs", `export const check = () => true;
|
|
66
|
+
`)])
|
|
67
|
+
|
|
68
|
+
let closureOf = (packageDirs: dict<string>) => {
|
|
69
|
+
Util_Bundle.addImportedPackageClosure(packageDirs)
|
|
70
|
+
packageDirs
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe("Util_Bundle — bundled user packages carry their imports", () => {
|
|
74
|
+
afterAll(() => NodeFs.rmSync(hostRoot, {recursive: true, force: true}))
|
|
75
|
+
|
|
76
|
+
testSync("a dependency only the importing package can resolve is added", () => {
|
|
77
|
+
let packageDirs = Dict.fromArray([(hostName, hostRoot)])
|
|
78
|
+
expect(closureOf(packageDirs)->Dict.get(traitName))->toEqual(Some(traitRoot))
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
testSync("the walk is transitive — the dependency's own imports come too", () => {
|
|
82
|
+
let packageDirs = Dict.fromArray([(hostName, hostRoot)])
|
|
83
|
+
expect(closureOf(packageDirs)->Dict.get(rulesName))->toEqual(Some(rulesRoot))
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
testSync("packages the deployed runtime provides are left out", () => {
|
|
87
|
+
let packageDirs = Dict.fromArray([(hostName, hostRoot)])
|
|
88
|
+
let bundled = closureOf(packageDirs)->Dict.keysToArray
|
|
89
|
+
// sury rides in the layer (framework-resolvable); node builtins and the
|
|
90
|
+
// @aws-sdk scope resolve from the runtime's own dirs.
|
|
91
|
+
expect(bundled->Array.includes("sury"))->toBe(false)
|
|
92
|
+
expect(bundled->Array.includes("fs"))->toBe(false)
|
|
93
|
+
expect(bundled->Array.includes("path"))->toBe(false)
|
|
94
|
+
expect(bundled->Array.includes("@aws-sdk/client-dynamodb"))->toBe(false)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
testSync("an import that resolves nowhere is skipped, not thrown on", () => {
|
|
98
|
+
let packageDirs = Dict.fromArray([(hostName, hostRoot)])
|
|
99
|
+
let bundled = closureOf(packageDirs)->Dict.keysToArray
|
|
100
|
+
expect(bundled->Array.includes("@fixture/never-installed"))->toBe(false)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
testSync("framework packages are starting points, not walked", () => {
|
|
104
|
+
// Walking reventless-aws would reach its deploy-time-only imports (the
|
|
105
|
+
// @pulumi bindings) and add tens of megabytes to every archive.
|
|
106
|
+
let packageDirs = Dict.fromArray([
|
|
107
|
+
("@reventlessdev/reventless-aws", Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws")),
|
|
108
|
+
])
|
|
109
|
+
expect(closureOf(packageDirs)->Dict.keysToArray->Array.length)->toBe(1)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Nodefs from "node:fs";
|
|
4
|
+
import * as Nodeos from "node:os";
|
|
5
|
+
import * as Nodepath from "node:path";
|
|
6
|
+
import * as Util_Bundle$ReventlessAws from "../src/util/Util_Bundle.res.mjs";
|
|
7
|
+
|
|
8
|
+
function mkTmpRoot(prefix) {
|
|
9
|
+
return Nodefs.realpathSync(Nodefs.mkdtempSync(Nodepath.join(Nodeos.tmpdir(), prefix)));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function writePkg(root, name, files) {
|
|
13
|
+
Nodefs.mkdirSync(root, {
|
|
14
|
+
recursive: true
|
|
15
|
+
});
|
|
16
|
+
Nodefs.writeFileSync(Nodepath.join(root, "package.json"), `{"name":"` + name + `","version":"1.0.0"}`, "utf8");
|
|
17
|
+
files.forEach(param => {
|
|
18
|
+
let abs = Nodepath.join(root, param[0]);
|
|
19
|
+
Nodefs.mkdirSync(Nodepath.dirname(abs), {
|
|
20
|
+
recursive: true
|
|
21
|
+
});
|
|
22
|
+
Nodefs.writeFileSync(abs, param[1], "utf8");
|
|
23
|
+
});
|
|
24
|
+
return root;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let hostName = "@fixture/closure-host";
|
|
28
|
+
|
|
29
|
+
let traitName = "@fixture/closure-trait";
|
|
30
|
+
|
|
31
|
+
let rulesName = "@fixture/closure-rules";
|
|
32
|
+
|
|
33
|
+
let hostRoot = mkTmpRoot("bundle-closure-");
|
|
34
|
+
|
|
35
|
+
let traitRoot = Nodepath.join(hostRoot, "node_modules", traitName);
|
|
36
|
+
|
|
37
|
+
let rulesRoot = Nodepath.join(hostRoot, "node_modules", rulesName);
|
|
38
|
+
|
|
39
|
+
writePkg(hostRoot, hostName, [[
|
|
40
|
+
"src/Slice.res.mjs",
|
|
41
|
+
`import * as Trait from "` + traitName + `";
|
|
42
|
+
import * as Sury from "sury";
|
|
43
|
+
import * as Fs from "node:fs";
|
|
44
|
+
import * as Path from "path";
|
|
45
|
+
import * as Ddb from "@aws-sdk/client-dynamodb";
|
|
46
|
+
import * as Missing from "@fixture/never-installed";
|
|
47
|
+
export const handle = () => Trait.rule(Sury, Fs, Path, Ddb, Missing);
|
|
48
|
+
`
|
|
49
|
+
]]);
|
|
50
|
+
|
|
51
|
+
writePkg(traitRoot, traitName, [[
|
|
52
|
+
"src/Trait.res.mjs",
|
|
53
|
+
`import * as Rules from "` + rulesName + `";
|
|
54
|
+
export const rule = () => Rules.check();
|
|
55
|
+
`
|
|
56
|
+
]]);
|
|
57
|
+
|
|
58
|
+
writePkg(rulesRoot, rulesName, [[
|
|
59
|
+
"src/Rules.res.mjs",
|
|
60
|
+
`export const check = () => true;
|
|
61
|
+
`
|
|
62
|
+
]]);
|
|
63
|
+
|
|
64
|
+
function closureOf(packageDirs) {
|
|
65
|
+
Util_Bundle$ReventlessAws.addImportedPackageClosure(packageDirs);
|
|
66
|
+
return packageDirs;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
globalThis.describe("Util_Bundle — bundled user packages carry their imports", () => {
|
|
70
|
+
globalThis.afterAll(() => {
|
|
71
|
+
Nodefs.rmSync(hostRoot, {
|
|
72
|
+
recursive: true,
|
|
73
|
+
force: true
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
globalThis.test("a dependency only the importing package can resolve is added", () => {
|
|
77
|
+
let packageDirs = Object.fromEntries([[
|
|
78
|
+
hostName,
|
|
79
|
+
hostRoot
|
|
80
|
+
]]);
|
|
81
|
+
globalThis.expect((Util_Bundle$ReventlessAws.addImportedPackageClosure(packageDirs), packageDirs)[traitName]).toEqual(traitRoot);
|
|
82
|
+
});
|
|
83
|
+
globalThis.test("the walk is transitive — the dependency's own imports come too", () => {
|
|
84
|
+
let packageDirs = Object.fromEntries([[
|
|
85
|
+
hostName,
|
|
86
|
+
hostRoot
|
|
87
|
+
]]);
|
|
88
|
+
globalThis.expect((Util_Bundle$ReventlessAws.addImportedPackageClosure(packageDirs), packageDirs)[rulesName]).toEqual(rulesRoot);
|
|
89
|
+
});
|
|
90
|
+
globalThis.test("packages the deployed runtime provides are left out", () => {
|
|
91
|
+
let packageDirs = Object.fromEntries([[
|
|
92
|
+
hostName,
|
|
93
|
+
hostRoot
|
|
94
|
+
]]);
|
|
95
|
+
let bundled = Object.keys((Util_Bundle$ReventlessAws.addImportedPackageClosure(packageDirs), packageDirs));
|
|
96
|
+
globalThis.expect(bundled.includes("sury")).toBe(false);
|
|
97
|
+
globalThis.expect(bundled.includes("fs")).toBe(false);
|
|
98
|
+
globalThis.expect(bundled.includes("path")).toBe(false);
|
|
99
|
+
globalThis.expect(bundled.includes("@aws-sdk/client-dynamodb")).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
globalThis.test("an import that resolves nowhere is skipped, not thrown on", () => {
|
|
102
|
+
let packageDirs = Object.fromEntries([[
|
|
103
|
+
hostName,
|
|
104
|
+
hostRoot
|
|
105
|
+
]]);
|
|
106
|
+
let bundled = Object.keys((Util_Bundle$ReventlessAws.addImportedPackageClosure(packageDirs), packageDirs));
|
|
107
|
+
globalThis.expect(bundled.includes("@fixture/never-installed")).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
globalThis.test("framework packages are starting points, not walked", () => {
|
|
110
|
+
let packageDirs = Object.fromEntries([[
|
|
111
|
+
"@reventlessdev/reventless-aws",
|
|
112
|
+
Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, "@reventlessdev/reventless-aws")
|
|
113
|
+
]]);
|
|
114
|
+
globalThis.expect(Object.keys((Util_Bundle$ReventlessAws.addImportedPackageClosure(packageDirs), packageDirs)).length).toBe(1);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
export {
|
|
119
|
+
mkTmpRoot,
|
|
120
|
+
writePkg,
|
|
121
|
+
hostName,
|
|
122
|
+
traitName,
|
|
123
|
+
rulesName,
|
|
124
|
+
hostRoot,
|
|
125
|
+
traitRoot,
|
|
126
|
+
rulesRoot,
|
|
127
|
+
closureOf,
|
|
128
|
+
}
|
|
129
|
+
/* hostRoot Not a pure module */
|