@skuba-lib/api 2.3.0-rolldown-cdk-bundle-plugin-20260804050549 → 2.3.0-rolldown-cdk-bundle-plugin-20260804234000
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/lib/{index-J1Qqgryi.d.cts → index-BEmN8nSJ.d.cts} +9 -6
- package/lib/{index-J1Qqgryi.d.mts → index-BEmN8nSJ.d.mts} +9 -6
- package/lib/index.cjs +1 -1
- package/lib/index.d.cts +1 -1
- package/lib/index.d.mts +1 -1
- package/lib/index.mjs +1 -1
- package/lib/rolldown/index.cjs +1 -1
- package/lib/rolldown/index.d.cts +1 -1
- package/lib/rolldown/index.d.mts +1 -1
- package/lib/rolldown/index.mjs +1 -1
- package/lib/{rolldown-08iefo1V.mjs → rolldown--bMMtGC0.mjs} +91 -49
- package/lib/{rolldown-CbJyS-Uy.cjs → rolldown-CN3DH_Tw.cjs} +90 -48
- package/package.json +1 -1
|
@@ -40,7 +40,8 @@ interface LambdaAssetOptions {
|
|
|
40
40
|
* This is the directory holding the `package.json` that depends on them; in a
|
|
41
41
|
* workspace, that is the individual package rather than the workspace root.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
43
|
+
* Relative paths resolve against rolldown's `cwd`, which defaults to
|
|
44
|
+
* `process.cwd()`.
|
|
44
45
|
*/
|
|
45
46
|
projectRoot?: string;
|
|
46
47
|
/**
|
|
@@ -49,7 +50,8 @@ interface LambdaAssetOptions {
|
|
|
49
50
|
* pnpm keeps this at the workspace root, so its directory is also where the
|
|
50
51
|
* workspace config and patches are staged from.
|
|
51
52
|
*
|
|
52
|
-
* Defaults to the nearest lock file, walking up from `
|
|
53
|
+
* Defaults to the nearest lock file, walking up from rolldown's `cwd`, which
|
|
54
|
+
* defaults to `process.cwd()`. A relative path resolves against that `cwd`.
|
|
53
55
|
*/
|
|
54
56
|
depsLockFilePath?: string;
|
|
55
57
|
}
|
|
@@ -59,12 +61,13 @@ interface LambdaAssetOptions {
|
|
|
59
61
|
* The plugin supports ESM output and pnpm only. It makes no assumptions about
|
|
60
62
|
* how you bundle; it only augments what rolldown has already written:
|
|
61
63
|
*
|
|
62
|
-
* 1.
|
|
63
|
-
*
|
|
64
|
+
* 1. Emits any extra `assets` into the build output alongside the bundle, via
|
|
65
|
+
* rolldown's `emitFile`.
|
|
66
|
+
* 2. Writes a `package.json` of `type: 'module'`.
|
|
67
|
+
* 3. Installs `nodeModules` into the output directory with pnpm, staging the
|
|
64
68
|
* workspace config, `.npmrc`, lock file and patches to do so.
|
|
65
|
-
*
|
|
69
|
+
* 4. Strips those install-only files back out, leaving the bundle, the
|
|
66
70
|
* generated `package.json` and `node_modules`.
|
|
67
|
-
* 4. Copies any extra `assets` into the output directory alongside the bundle.
|
|
68
71
|
*
|
|
69
72
|
* The result can be handed to CDK as `aws_lambda.Code.fromAsset(outputDir)`.
|
|
70
73
|
*
|
|
@@ -40,7 +40,8 @@ interface LambdaAssetOptions {
|
|
|
40
40
|
* This is the directory holding the `package.json` that depends on them; in a
|
|
41
41
|
* workspace, that is the individual package rather than the workspace root.
|
|
42
42
|
*
|
|
43
|
-
*
|
|
43
|
+
* Relative paths resolve against rolldown's `cwd`, which defaults to
|
|
44
|
+
* `process.cwd()`.
|
|
44
45
|
*/
|
|
45
46
|
projectRoot?: string;
|
|
46
47
|
/**
|
|
@@ -49,7 +50,8 @@ interface LambdaAssetOptions {
|
|
|
49
50
|
* pnpm keeps this at the workspace root, so its directory is also where the
|
|
50
51
|
* workspace config and patches are staged from.
|
|
51
52
|
*
|
|
52
|
-
* Defaults to the nearest lock file, walking up from `
|
|
53
|
+
* Defaults to the nearest lock file, walking up from rolldown's `cwd`, which
|
|
54
|
+
* defaults to `process.cwd()`. A relative path resolves against that `cwd`.
|
|
53
55
|
*/
|
|
54
56
|
depsLockFilePath?: string;
|
|
55
57
|
}
|
|
@@ -59,12 +61,13 @@ interface LambdaAssetOptions {
|
|
|
59
61
|
* The plugin supports ESM output and pnpm only. It makes no assumptions about
|
|
60
62
|
* how you bundle; it only augments what rolldown has already written:
|
|
61
63
|
*
|
|
62
|
-
* 1.
|
|
63
|
-
*
|
|
64
|
+
* 1. Emits any extra `assets` into the build output alongside the bundle, via
|
|
65
|
+
* rolldown's `emitFile`.
|
|
66
|
+
* 2. Writes a `package.json` of `type: 'module'`.
|
|
67
|
+
* 3. Installs `nodeModules` into the output directory with pnpm, staging the
|
|
64
68
|
* workspace config, `.npmrc`, lock file and patches to do so.
|
|
65
|
-
*
|
|
69
|
+
* 4. Strips those install-only files back out, leaving the bundle, the
|
|
66
70
|
* generated `package.json` and `node_modules`.
|
|
67
|
-
* 4. Copies any extra `assets` into the output directory alongside the bundle.
|
|
68
71
|
*
|
|
69
72
|
* The result can be handed to CDK as `aws_lambda.Code.fromAsset(outputDir)`.
|
|
70
73
|
*
|
package/lib/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ const require_cdk_index = require("./cdk/index.cjs");
|
|
|
4
4
|
const require_git = require("./git-BGcWTYZJ.cjs");
|
|
5
5
|
const require_github = require("./github-B2fQutQz.cjs");
|
|
6
6
|
const require_net = require("./net-zrteIHld.cjs");
|
|
7
|
-
const require_rolldown = require("./rolldown-
|
|
7
|
+
const require_rolldown = require("./rolldown-CN3DH_Tw.cjs");
|
|
8
8
|
Object.defineProperty(exports, "Buildkite", {
|
|
9
9
|
enumerable: true,
|
|
10
10
|
get: function() {
|
package/lib/index.d.cts
CHANGED
|
@@ -3,5 +3,5 @@ import { t as index_d_exports$1 } from "./cdk/index.cjs";
|
|
|
3
3
|
import { t as index_d_exports$2 } from "./index-CLEYcnJY.cjs";
|
|
4
4
|
import { t as index_d_exports$3 } from "./index-CPm5rAMy.cjs";
|
|
5
5
|
import { t as index_d_exports$4 } from "./index-C9lVo3vU.cjs";
|
|
6
|
-
import { t as index_d_exports$5 } from "./index-
|
|
6
|
+
import { t as index_d_exports$5 } from "./index-BEmN8nSJ.cjs";
|
|
7
7
|
export { index_d_exports as Buildkite, index_d_exports$1 as Cdk, index_d_exports$2 as Git, index_d_exports$3 as GitHub, index_d_exports$4 as Net, index_d_exports$5 as Rolldown };
|
package/lib/index.d.mts
CHANGED
|
@@ -3,5 +3,5 @@ import { t as index_d_exports$1 } from "./cdk/index.mjs";
|
|
|
3
3
|
import { t as index_d_exports$2 } from "./index-CLEYcnJY.mjs";
|
|
4
4
|
import { t as index_d_exports$3 } from "./index-CwdiFn_m.mjs";
|
|
5
5
|
import { t as index_d_exports$4 } from "./index-C9lVo3vU.mjs";
|
|
6
|
-
import { t as index_d_exports$5 } from "./index-
|
|
6
|
+
import { t as index_d_exports$5 } from "./index-BEmN8nSJ.mjs";
|
|
7
7
|
export { index_d_exports as Buildkite, index_d_exports$1 as Cdk, index_d_exports$2 as Git, index_d_exports$3 as GitHub, index_d_exports$4 as Net, index_d_exports$5 as Rolldown };
|
package/lib/index.mjs
CHANGED
|
@@ -3,5 +3,5 @@ import { t as cdk_exports } from "./cdk/index.mjs";
|
|
|
3
3
|
import { t as git_exports } from "./git-DKqd7R1V.mjs";
|
|
4
4
|
import { t as github_exports } from "./github-EDhl9ON9.mjs";
|
|
5
5
|
import { t as net_exports } from "./net-97EvMPiP.mjs";
|
|
6
|
-
import { t as rolldown_exports } from "./rolldown
|
|
6
|
+
import { t as rolldown_exports } from "./rolldown--bMMtGC0.mjs";
|
|
7
7
|
export { buildkite_exports as Buildkite, cdk_exports as Cdk, git_exports as Git, github_exports as GitHub, net_exports as Net, rolldown_exports as Rolldown };
|
package/lib/rolldown/index.cjs
CHANGED
package/lib/rolldown/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as lambdaAsset, n as LambdaAssetFile, r as LambdaAssetOptions } from "../index-
|
|
1
|
+
import { i as lambdaAsset, n as LambdaAssetFile, r as LambdaAssetOptions } from "../index-BEmN8nSJ.cjs";
|
|
2
2
|
export { type LambdaAssetFile, type LambdaAssetOptions, lambdaAsset };
|
package/lib/rolldown/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as lambdaAsset, n as LambdaAssetFile, r as LambdaAssetOptions } from "../index-
|
|
1
|
+
import { i as lambdaAsset, n as LambdaAssetFile, r as LambdaAssetOptions } from "../index-BEmN8nSJ.mjs";
|
|
2
2
|
export { type LambdaAssetFile, type LambdaAssetOptions, lambdaAsset };
|
package/lib/rolldown/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as lambdaAsset } from "../rolldown
|
|
1
|
+
import { n as lambdaAsset } from "../rolldown--bMMtGC0.mjs";
|
|
2
2
|
export { lambdaAsset };
|
|
@@ -7,7 +7,7 @@ import fs from "fs-extra";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { findUp } from "find-up";
|
|
9
9
|
import { pathToFileURL } from "node:url";
|
|
10
|
-
import {
|
|
10
|
+
import { isMap, parseDocument } from "yaml";
|
|
11
11
|
//#region src/rolldown/pnpm.ts
|
|
12
12
|
const PNPM_LOCK = "pnpm-lock.yaml";
|
|
13
13
|
const PNPM_WORKSPACE_YAML = "pnpm-workspace.yaml";
|
|
@@ -31,24 +31,38 @@ const PNPM_INSTALL_COMMAND = [
|
|
|
31
31
|
"--no-frozen-lockfile"
|
|
32
32
|
];
|
|
33
33
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34
|
-
const
|
|
34
|
+
const readJsonFile = async (filePath) => {
|
|
35
35
|
try {
|
|
36
|
-
return
|
|
36
|
+
return await fs.readJson(filePath);
|
|
37
37
|
} catch (err) {
|
|
38
38
|
throw new Error(`Failed to parse ${filePath} as JSON: ${err.message}`);
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
|
-
* Reads
|
|
42
|
+
* Reads a `name@version` package manager pin from a manifest, preferring the
|
|
43
|
+
* `packageManager` field and falling back to `devEngines.packageManager`.
|
|
44
|
+
*
|
|
45
|
+
* `devEngines.packageManager` is either an object `{ name, version }` or an
|
|
46
|
+
* array of them; a pin is only returned when both `name` and `version` are
|
|
47
|
+
* present, since corepack needs the version to activate.
|
|
48
|
+
*/
|
|
49
|
+
const readPin = (parsed) => {
|
|
50
|
+
if (typeof parsed.packageManager === "string") return parsed.packageManager;
|
|
51
|
+
const raw = (isRecord(parsed.devEngines) ? parsed.devEngines : void 0)?.packageManager;
|
|
52
|
+
const packageManager = Array.isArray(raw) ? raw[0] : raw;
|
|
53
|
+
if (isRecord(packageManager) && typeof packageManager.name === "string" && typeof packageManager.version === "string") return `${packageManager.name}@${packageManager.version}`;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Reads the package manager pin from the workspace root manifest.
|
|
43
57
|
*
|
|
44
58
|
* Forwarding it to the output directory keeps the staged install on the same
|
|
45
59
|
* pnpm version as the project.
|
|
46
60
|
*/
|
|
47
|
-
const
|
|
61
|
+
const readPackageManagerPin = async (workspaceRoot) => {
|
|
48
62
|
const pkgPath = path.join(workspaceRoot, "package.json");
|
|
49
63
|
if (!await pathExists(pkgPath)) return;
|
|
50
|
-
const parsed = await
|
|
51
|
-
return isRecord(parsed)
|
|
64
|
+
const parsed = await readJsonFile(pkgPath);
|
|
65
|
+
return isRecord(parsed) ? readPin(parsed) : void 0;
|
|
52
66
|
};
|
|
53
67
|
/**
|
|
54
68
|
* The output directory installs a subset of the workspace's dependencies, so
|
|
@@ -56,15 +70,12 @@ const readPackageManagerField = async (workspaceRoot) => {
|
|
|
56
70
|
* install on an unused patch unless this is set.
|
|
57
71
|
*/
|
|
58
72
|
const allowUnusedPatches = (content) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!isRecord(doc)) return content;
|
|
66
|
-
doc.allowUnusedPatches = true;
|
|
67
|
-
return stringify(doc);
|
|
73
|
+
const doc = parseDocument(content);
|
|
74
|
+
const [error] = doc.errors;
|
|
75
|
+
if (error) throw new Error(`Failed to parse ${PNPM_WORKSPACE_YAML}: ${error.message}`);
|
|
76
|
+
if (!isMap(doc.contents)) return content;
|
|
77
|
+
doc.set("allowUnusedPatches", true);
|
|
78
|
+
return doc.toString();
|
|
68
79
|
};
|
|
69
80
|
/**
|
|
70
81
|
* Stages the pnpm config that an install in `outputDir` needs.
|
|
@@ -76,19 +87,24 @@ const allowUnusedPatches = (content) => {
|
|
|
76
87
|
* credentials that must not ship in the asset.
|
|
77
88
|
*/
|
|
78
89
|
const stageWorkspaceFiles = async (workspaceRoot, outputDir, stagedFiles = []) => {
|
|
79
|
-
|
|
90
|
+
const present = (await Promise.all(PNPM_WORKSPACE_FILES.map(async (file) => {
|
|
80
91
|
const src = path.join(workspaceRoot, file);
|
|
81
|
-
|
|
92
|
+
return await pathExists(src) ? {
|
|
93
|
+
file,
|
|
94
|
+
src
|
|
95
|
+
} : void 0;
|
|
96
|
+
}))).filter((entry) => entry !== void 0);
|
|
97
|
+
for (const { file } of present) stagedFiles.push(path.join(outputDir, file));
|
|
98
|
+
await Promise.all(present.map(async ({ file, src }) => {
|
|
82
99
|
const dest = path.join(outputDir, file);
|
|
83
100
|
if (file === PNPM_WORKSPACE_YAML) await fs.promises.writeFile(dest, allowUnusedPatches(await fs.promises.readFile(src, "utf8")));
|
|
84
101
|
else await fs.promises.copyFile(src, dest);
|
|
85
|
-
|
|
86
|
-
}
|
|
102
|
+
}));
|
|
87
103
|
const patchesSrc = path.join(workspaceRoot, PNPM_PATCHES_DIR);
|
|
88
104
|
if (await pathExists(patchesSrc)) {
|
|
89
105
|
const patchesDest = path.join(outputDir, PNPM_PATCHES_DIR);
|
|
90
|
-
await fs.copy(patchesSrc, patchesDest);
|
|
91
106
|
stagedFiles.push(patchesDest);
|
|
107
|
+
await fs.copy(patchesSrc, patchesDest);
|
|
92
108
|
}
|
|
93
109
|
return stagedFiles;
|
|
94
110
|
};
|
|
@@ -105,7 +121,7 @@ const extractDependencies = async (resolveFrom, modules) => {
|
|
|
105
121
|
} catch {
|
|
106
122
|
modPkgPath = void 0;
|
|
107
123
|
}
|
|
108
|
-
const parsed = modPkgPath ? await
|
|
124
|
+
const parsed = modPkgPath ? await readJsonFile(modPkgPath) : void 0;
|
|
109
125
|
const version = isRecord(parsed) && typeof parsed.version === "string" ? parsed.version.trim() : void 0;
|
|
110
126
|
if (!version) throw new Error(`Cannot extract version for module '${mod}'. Check that it's referenced in your package.json or installed.`);
|
|
111
127
|
result[mod] = version;
|
|
@@ -115,30 +131,46 @@ const extractDependencies = async (resolveFrom, modules) => {
|
|
|
115
131
|
//#endregion
|
|
116
132
|
//#region src/rolldown/lambdaAsset.ts
|
|
117
133
|
const PLUGIN_NAME = "skuba:lambda-asset";
|
|
118
|
-
const resolveOutputDir = ({ dir, file }) => {
|
|
134
|
+
const resolveOutputDir = ({ dir, file }, cwd) => {
|
|
119
135
|
if (!dir) throw new Error(`${PLUGIN_NAME} requires \`output.dir\`; it prepares a deployable directory, so a single \`output.file\`${file ? ` (${file})` : ""} has nowhere to install into.`);
|
|
120
|
-
return path.resolve(dir);
|
|
136
|
+
return path.resolve(cwd, dir);
|
|
121
137
|
};
|
|
122
|
-
const resolveLockFile = async (depsLockFilePath) => {
|
|
123
|
-
if (!depsLockFilePath) return findUp(PNPM_LOCK);
|
|
124
|
-
const resolved = path.resolve(depsLockFilePath);
|
|
138
|
+
const resolveLockFile = async (depsLockFilePath, cwd) => {
|
|
139
|
+
if (!depsLockFilePath) return findUp(PNPM_LOCK, { cwd });
|
|
140
|
+
const resolved = path.resolve(cwd, depsLockFilePath);
|
|
125
141
|
if (!await pathExists(resolved)) throw new Error(`${PLUGIN_NAME} cannot find a ${PNPM_LOCK} at '${depsLockFilePath}'.`);
|
|
126
142
|
return resolved;
|
|
127
143
|
};
|
|
144
|
+
const toPosix = (p) => p.split(path.sep).join("/");
|
|
128
145
|
/**
|
|
129
|
-
*
|
|
146
|
+
* Emits extra `assets` into the build output alongside the bundle.
|
|
130
147
|
*
|
|
131
|
-
* `from` is resolved relative to `projectRoot
|
|
132
|
-
*
|
|
133
|
-
*
|
|
148
|
+
* `from` is resolved relative to `projectRoot`; `to` becomes the emitted
|
|
149
|
+
* `fileName`, which rolldown writes relative to the output directory, creating
|
|
150
|
+
* parent directories as needed. Directories are emitted recursively, one asset
|
|
151
|
+
* per file. `originalFileName` points rolldown at each source file so watch mode
|
|
152
|
+
* rebuilds when it changes.
|
|
134
153
|
*/
|
|
135
|
-
const
|
|
136
|
-
|
|
154
|
+
const emitAssets = async (emit, projectRoot, assets) => {
|
|
155
|
+
await Promise.all(assets.map(async (asset) => {
|
|
137
156
|
const from = path.resolve(projectRoot, asset.from);
|
|
138
|
-
const to =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
157
|
+
const to = asset.to ?? path.basename(asset.from);
|
|
158
|
+
const normalized = path.normalize(to);
|
|
159
|
+
if (path.isAbsolute(normalized) || normalized === "." || normalized === ".." || normalized.startsWith(`..${path.sep}`)) throw new Error(`${PLUGIN_NAME} refuses to copy asset '${asset.from}' to '${asset.to}': it escapes the output directory.`);
|
|
160
|
+
const files = (await fs.promises.stat(from)).isDirectory() ? (await fs.promises.readdir(from, {
|
|
161
|
+
recursive: true,
|
|
162
|
+
withFileTypes: true
|
|
163
|
+
})).filter((entry) => entry.isFile()).map((entry) => path.join(entry.parentPath, entry.name)) : [from];
|
|
164
|
+
await Promise.all(files.map(async (absFile) => {
|
|
165
|
+
const rel = path.relative(from, absFile);
|
|
166
|
+
emit({
|
|
167
|
+
type: "asset",
|
|
168
|
+
fileName: toPosix(rel ? path.join(to, rel) : to),
|
|
169
|
+
originalFileName: absFile,
|
|
170
|
+
source: await fs.promises.readFile(absFile)
|
|
171
|
+
});
|
|
172
|
+
}));
|
|
173
|
+
}));
|
|
142
174
|
};
|
|
143
175
|
const writeOutputPackageJson = (outputDir, fields = {}) => fs.promises.writeFile(path.join(outputDir, "package.json"), JSON.stringify({
|
|
144
176
|
type: "module",
|
|
@@ -160,12 +192,13 @@ const stripInstallFiles = async (outputDir, stagedFiles, cause) => {
|
|
|
160
192
|
* The plugin supports ESM output and pnpm only. It makes no assumptions about
|
|
161
193
|
* how you bundle; it only augments what rolldown has already written:
|
|
162
194
|
*
|
|
163
|
-
* 1.
|
|
164
|
-
*
|
|
195
|
+
* 1. Emits any extra `assets` into the build output alongside the bundle, via
|
|
196
|
+
* rolldown's `emitFile`.
|
|
197
|
+
* 2. Writes a `package.json` of `type: 'module'`.
|
|
198
|
+
* 3. Installs `nodeModules` into the output directory with pnpm, staging the
|
|
165
199
|
* workspace config, `.npmrc`, lock file and patches to do so.
|
|
166
|
-
*
|
|
200
|
+
* 4. Strips those install-only files back out, leaving the bundle, the
|
|
167
201
|
* generated `package.json` and `node_modules`.
|
|
168
|
-
* 4. Copies any extra `assets` into the output directory alongside the bundle.
|
|
169
202
|
*
|
|
170
203
|
* The result can be handed to CDK as `aws_lambda.Code.fromAsset(outputDir)`.
|
|
171
204
|
*
|
|
@@ -189,41 +222,50 @@ const stripInstallFiles = async (outputDir, stagedFiles, cause) => {
|
|
|
189
222
|
*/
|
|
190
223
|
const lambdaAsset = ({ nodeModules = [], assets = [], projectRoot: projectRootOption, depsLockFilePath: depsLockFilePathOption } = {}) => {
|
|
191
224
|
const prepared = /* @__PURE__ */ new Set();
|
|
225
|
+
let cwd = process.cwd();
|
|
192
226
|
return {
|
|
193
227
|
name: PLUGIN_NAME,
|
|
194
|
-
buildStart() {
|
|
228
|
+
async buildStart(options) {
|
|
229
|
+
cwd = options.cwd;
|
|
195
230
|
prepared.clear();
|
|
231
|
+
if (assets.length) {
|
|
232
|
+
const projectRoot = path.resolve(cwd, projectRootOption ?? ".");
|
|
233
|
+
await emitAssets((file) => this.emitFile(file), projectRoot, assets);
|
|
234
|
+
}
|
|
196
235
|
},
|
|
197
236
|
async writeBundle(outputOptions) {
|
|
198
|
-
const outputDir = resolveOutputDir(outputOptions);
|
|
237
|
+
const outputDir = resolveOutputDir(outputOptions, cwd);
|
|
199
238
|
if (prepared.has(outputDir)) return;
|
|
200
239
|
prepared.add(outputDir);
|
|
201
|
-
const projectRoot = path.resolve(projectRootOption ??
|
|
202
|
-
await copyAssets(outputDir, projectRoot, assets);
|
|
240
|
+
const projectRoot = path.resolve(cwd, projectRootOption ?? ".");
|
|
203
241
|
if (!nodeModules.length) {
|
|
204
242
|
await writeOutputPackageJson(outputDir);
|
|
205
243
|
return;
|
|
206
244
|
}
|
|
207
|
-
const depsLockFilePath = await resolveLockFile(depsLockFilePathOption);
|
|
245
|
+
const depsLockFilePath = await resolveLockFile(depsLockFilePathOption, cwd);
|
|
208
246
|
if (!depsLockFilePath) throw new Error(`${PLUGIN_NAME} cannot find a ${PNPM_LOCK}, which \`nodeModules\` requires. Specify one with \`depsLockFilePath\`.`);
|
|
209
247
|
const workspaceRoot = path.dirname(depsLockFilePath);
|
|
210
248
|
const projectPackageJson = path.join(projectRoot, "package.json");
|
|
211
249
|
if (!await pathExists(projectPackageJson)) throw new Error(`${PLUGIN_NAME} cannot find a package.json in '${projectRoot}', which \`nodeModules\` requires. Specify the directory with \`projectRoot\`.`);
|
|
212
250
|
await writeOutputPackageJson(outputDir, {
|
|
213
251
|
dependencies: await extractDependencies(projectPackageJson, nodeModules),
|
|
214
|
-
packageManager: await
|
|
252
|
+
packageManager: await readPackageManagerPin(workspaceRoot)
|
|
215
253
|
});
|
|
216
254
|
const stagedFiles = [];
|
|
217
255
|
try {
|
|
218
256
|
await stageWorkspaceFiles(workspaceRoot, outputDir, stagedFiles);
|
|
219
257
|
const lockDest = path.join(outputDir, PNPM_LOCK);
|
|
220
|
-
await fs.promises.copyFile(depsLockFilePath, lockDest);
|
|
221
258
|
stagedFiles.push(lockDest);
|
|
259
|
+
await fs.promises.copyFile(depsLockFilePath, lockDest);
|
|
222
260
|
log.plain(log.bold(PLUGIN_NAME), "installing", log.bold(nodeModules.join(", ")));
|
|
223
261
|
const [bin, ...args] = PNPM_INSTALL_COMMAND;
|
|
224
262
|
await createExec({ cwd: outputDir })(bin, ...args);
|
|
225
263
|
} catch (err) {
|
|
226
|
-
await stripInstallFiles(outputDir,
|
|
264
|
+
await stripInstallFiles(outputDir, [
|
|
265
|
+
...stagedFiles,
|
|
266
|
+
path.join(outputDir, "package.json"),
|
|
267
|
+
path.join(outputDir, "node_modules")
|
|
268
|
+
], err);
|
|
227
269
|
throw err;
|
|
228
270
|
}
|
|
229
271
|
await stripInstallFiles(outputDir, stagedFiles);
|
|
@@ -33,24 +33,38 @@ const PNPM_INSTALL_COMMAND = [
|
|
|
33
33
|
"--no-frozen-lockfile"
|
|
34
34
|
];
|
|
35
35
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36
|
-
const
|
|
36
|
+
const readJsonFile = async (filePath) => {
|
|
37
37
|
try {
|
|
38
|
-
return
|
|
38
|
+
return await fs_extra.default.readJson(filePath);
|
|
39
39
|
} catch (err) {
|
|
40
40
|
throw new Error(`Failed to parse ${filePath} as JSON: ${err.message}`);
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
43
|
/**
|
|
44
|
-
* Reads
|
|
44
|
+
* Reads a `name@version` package manager pin from a manifest, preferring the
|
|
45
|
+
* `packageManager` field and falling back to `devEngines.packageManager`.
|
|
46
|
+
*
|
|
47
|
+
* `devEngines.packageManager` is either an object `{ name, version }` or an
|
|
48
|
+
* array of them; a pin is only returned when both `name` and `version` are
|
|
49
|
+
* present, since corepack needs the version to activate.
|
|
50
|
+
*/
|
|
51
|
+
const readPin = (parsed) => {
|
|
52
|
+
if (typeof parsed.packageManager === "string") return parsed.packageManager;
|
|
53
|
+
const raw = (isRecord(parsed.devEngines) ? parsed.devEngines : void 0)?.packageManager;
|
|
54
|
+
const packageManager = Array.isArray(raw) ? raw[0] : raw;
|
|
55
|
+
if (isRecord(packageManager) && typeof packageManager.name === "string" && typeof packageManager.version === "string") return `${packageManager.name}@${packageManager.version}`;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Reads the package manager pin from the workspace root manifest.
|
|
45
59
|
*
|
|
46
60
|
* Forwarding it to the output directory keeps the staged install on the same
|
|
47
61
|
* pnpm version as the project.
|
|
48
62
|
*/
|
|
49
|
-
const
|
|
63
|
+
const readPackageManagerPin = async (workspaceRoot) => {
|
|
50
64
|
const pkgPath = node_path.join(workspaceRoot, "package.json");
|
|
51
65
|
if (!await require_fs.pathExists(pkgPath)) return;
|
|
52
|
-
const parsed = await
|
|
53
|
-
return isRecord(parsed)
|
|
66
|
+
const parsed = await readJsonFile(pkgPath);
|
|
67
|
+
return isRecord(parsed) ? readPin(parsed) : void 0;
|
|
54
68
|
};
|
|
55
69
|
/**
|
|
56
70
|
* The output directory installs a subset of the workspace's dependencies, so
|
|
@@ -58,15 +72,12 @@ const readPackageManagerField = async (workspaceRoot) => {
|
|
|
58
72
|
* install on an unused patch unless this is set.
|
|
59
73
|
*/
|
|
60
74
|
const allowUnusedPatches = (content) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (!isRecord(doc)) return content;
|
|
68
|
-
doc.allowUnusedPatches = true;
|
|
69
|
-
return (0, yaml.stringify)(doc);
|
|
75
|
+
const doc = (0, yaml.parseDocument)(content);
|
|
76
|
+
const [error] = doc.errors;
|
|
77
|
+
if (error) throw new Error(`Failed to parse ${PNPM_WORKSPACE_YAML}: ${error.message}`);
|
|
78
|
+
if (!(0, yaml.isMap)(doc.contents)) return content;
|
|
79
|
+
doc.set("allowUnusedPatches", true);
|
|
80
|
+
return doc.toString();
|
|
70
81
|
};
|
|
71
82
|
/**
|
|
72
83
|
* Stages the pnpm config that an install in `outputDir` needs.
|
|
@@ -78,19 +89,24 @@ const allowUnusedPatches = (content) => {
|
|
|
78
89
|
* credentials that must not ship in the asset.
|
|
79
90
|
*/
|
|
80
91
|
const stageWorkspaceFiles = async (workspaceRoot, outputDir, stagedFiles = []) => {
|
|
81
|
-
|
|
92
|
+
const present = (await Promise.all(PNPM_WORKSPACE_FILES.map(async (file) => {
|
|
82
93
|
const src = node_path.join(workspaceRoot, file);
|
|
83
|
-
|
|
94
|
+
return await require_fs.pathExists(src) ? {
|
|
95
|
+
file,
|
|
96
|
+
src
|
|
97
|
+
} : void 0;
|
|
98
|
+
}))).filter((entry) => entry !== void 0);
|
|
99
|
+
for (const { file } of present) stagedFiles.push(node_path.join(outputDir, file));
|
|
100
|
+
await Promise.all(present.map(async ({ file, src }) => {
|
|
84
101
|
const dest = node_path.join(outputDir, file);
|
|
85
102
|
if (file === PNPM_WORKSPACE_YAML) await fs_extra.default.promises.writeFile(dest, allowUnusedPatches(await fs_extra.default.promises.readFile(src, "utf8")));
|
|
86
103
|
else await fs_extra.default.promises.copyFile(src, dest);
|
|
87
|
-
|
|
88
|
-
}
|
|
104
|
+
}));
|
|
89
105
|
const patchesSrc = node_path.join(workspaceRoot, PNPM_PATCHES_DIR);
|
|
90
106
|
if (await require_fs.pathExists(patchesSrc)) {
|
|
91
107
|
const patchesDest = node_path.join(outputDir, PNPM_PATCHES_DIR);
|
|
92
|
-
await fs_extra.default.copy(patchesSrc, patchesDest);
|
|
93
108
|
stagedFiles.push(patchesDest);
|
|
109
|
+
await fs_extra.default.copy(patchesSrc, patchesDest);
|
|
94
110
|
}
|
|
95
111
|
return stagedFiles;
|
|
96
112
|
};
|
|
@@ -107,7 +123,7 @@ const extractDependencies = async (resolveFrom, modules) => {
|
|
|
107
123
|
} catch {
|
|
108
124
|
modPkgPath = void 0;
|
|
109
125
|
}
|
|
110
|
-
const parsed = modPkgPath ? await
|
|
126
|
+
const parsed = modPkgPath ? await readJsonFile(modPkgPath) : void 0;
|
|
111
127
|
const version = isRecord(parsed) && typeof parsed.version === "string" ? parsed.version.trim() : void 0;
|
|
112
128
|
if (!version) throw new Error(`Cannot extract version for module '${mod}'. Check that it's referenced in your package.json or installed.`);
|
|
113
129
|
result[mod] = version;
|
|
@@ -117,30 +133,46 @@ const extractDependencies = async (resolveFrom, modules) => {
|
|
|
117
133
|
//#endregion
|
|
118
134
|
//#region src/rolldown/lambdaAsset.ts
|
|
119
135
|
const PLUGIN_NAME = "skuba:lambda-asset";
|
|
120
|
-
const resolveOutputDir = ({ dir, file }) => {
|
|
136
|
+
const resolveOutputDir = ({ dir, file }, cwd) => {
|
|
121
137
|
if (!dir) throw new Error(`${PLUGIN_NAME} requires \`output.dir\`; it prepares a deployable directory, so a single \`output.file\`${file ? ` (${file})` : ""} has nowhere to install into.`);
|
|
122
|
-
return node_path.resolve(dir);
|
|
138
|
+
return node_path.resolve(cwd, dir);
|
|
123
139
|
};
|
|
124
|
-
const resolveLockFile = async (depsLockFilePath) => {
|
|
125
|
-
if (!depsLockFilePath) return (0, find_up.findUp)(PNPM_LOCK);
|
|
126
|
-
const resolved = node_path.resolve(depsLockFilePath);
|
|
140
|
+
const resolveLockFile = async (depsLockFilePath, cwd) => {
|
|
141
|
+
if (!depsLockFilePath) return (0, find_up.findUp)(PNPM_LOCK, { cwd });
|
|
142
|
+
const resolved = node_path.resolve(cwd, depsLockFilePath);
|
|
127
143
|
if (!await require_fs.pathExists(resolved)) throw new Error(`${PLUGIN_NAME} cannot find a ${PNPM_LOCK} at '${depsLockFilePath}'.`);
|
|
128
144
|
return resolved;
|
|
129
145
|
};
|
|
146
|
+
const toPosix = (p) => p.split(node_path.sep).join("/");
|
|
130
147
|
/**
|
|
131
|
-
*
|
|
148
|
+
* Emits extra `assets` into the build output alongside the bundle.
|
|
132
149
|
*
|
|
133
|
-
* `from` is resolved relative to `projectRoot
|
|
134
|
-
*
|
|
135
|
-
*
|
|
150
|
+
* `from` is resolved relative to `projectRoot`; `to` becomes the emitted
|
|
151
|
+
* `fileName`, which rolldown writes relative to the output directory, creating
|
|
152
|
+
* parent directories as needed. Directories are emitted recursively, one asset
|
|
153
|
+
* per file. `originalFileName` points rolldown at each source file so watch mode
|
|
154
|
+
* rebuilds when it changes.
|
|
136
155
|
*/
|
|
137
|
-
const
|
|
138
|
-
|
|
156
|
+
const emitAssets = async (emit, projectRoot, assets) => {
|
|
157
|
+
await Promise.all(assets.map(async (asset) => {
|
|
139
158
|
const from = node_path.resolve(projectRoot, asset.from);
|
|
140
|
-
const to =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
159
|
+
const to = asset.to ?? node_path.basename(asset.from);
|
|
160
|
+
const normalized = node_path.normalize(to);
|
|
161
|
+
if (node_path.isAbsolute(normalized) || normalized === "." || normalized === ".." || normalized.startsWith(`..${node_path.sep}`)) throw new Error(`${PLUGIN_NAME} refuses to copy asset '${asset.from}' to '${asset.to}': it escapes the output directory.`);
|
|
162
|
+
const files = (await fs_extra.default.promises.stat(from)).isDirectory() ? (await fs_extra.default.promises.readdir(from, {
|
|
163
|
+
recursive: true,
|
|
164
|
+
withFileTypes: true
|
|
165
|
+
})).filter((entry) => entry.isFile()).map((entry) => node_path.join(entry.parentPath, entry.name)) : [from];
|
|
166
|
+
await Promise.all(files.map(async (absFile) => {
|
|
167
|
+
const rel = node_path.relative(from, absFile);
|
|
168
|
+
emit({
|
|
169
|
+
type: "asset",
|
|
170
|
+
fileName: toPosix(rel ? node_path.join(to, rel) : to),
|
|
171
|
+
originalFileName: absFile,
|
|
172
|
+
source: await fs_extra.default.promises.readFile(absFile)
|
|
173
|
+
});
|
|
174
|
+
}));
|
|
175
|
+
}));
|
|
144
176
|
};
|
|
145
177
|
const writeOutputPackageJson = (outputDir, fields = {}) => fs_extra.default.promises.writeFile(node_path.join(outputDir, "package.json"), JSON.stringify({
|
|
146
178
|
type: "module",
|
|
@@ -162,12 +194,13 @@ const stripInstallFiles = async (outputDir, stagedFiles, cause) => {
|
|
|
162
194
|
* The plugin supports ESM output and pnpm only. It makes no assumptions about
|
|
163
195
|
* how you bundle; it only augments what rolldown has already written:
|
|
164
196
|
*
|
|
165
|
-
* 1.
|
|
166
|
-
*
|
|
197
|
+
* 1. Emits any extra `assets` into the build output alongside the bundle, via
|
|
198
|
+
* rolldown's `emitFile`.
|
|
199
|
+
* 2. Writes a `package.json` of `type: 'module'`.
|
|
200
|
+
* 3. Installs `nodeModules` into the output directory with pnpm, staging the
|
|
167
201
|
* workspace config, `.npmrc`, lock file and patches to do so.
|
|
168
|
-
*
|
|
202
|
+
* 4. Strips those install-only files back out, leaving the bundle, the
|
|
169
203
|
* generated `package.json` and `node_modules`.
|
|
170
|
-
* 4. Copies any extra `assets` into the output directory alongside the bundle.
|
|
171
204
|
*
|
|
172
205
|
* The result can be handed to CDK as `aws_lambda.Code.fromAsset(outputDir)`.
|
|
173
206
|
*
|
|
@@ -191,41 +224,50 @@ const stripInstallFiles = async (outputDir, stagedFiles, cause) => {
|
|
|
191
224
|
*/
|
|
192
225
|
const lambdaAsset = ({ nodeModules = [], assets = [], projectRoot: projectRootOption, depsLockFilePath: depsLockFilePathOption } = {}) => {
|
|
193
226
|
const prepared = /* @__PURE__ */ new Set();
|
|
227
|
+
let cwd = process.cwd();
|
|
194
228
|
return {
|
|
195
229
|
name: PLUGIN_NAME,
|
|
196
|
-
buildStart() {
|
|
230
|
+
async buildStart(options) {
|
|
231
|
+
cwd = options.cwd;
|
|
197
232
|
prepared.clear();
|
|
233
|
+
if (assets.length) {
|
|
234
|
+
const projectRoot = node_path.resolve(cwd, projectRootOption ?? ".");
|
|
235
|
+
await emitAssets((file) => this.emitFile(file), projectRoot, assets);
|
|
236
|
+
}
|
|
198
237
|
},
|
|
199
238
|
async writeBundle(outputOptions) {
|
|
200
|
-
const outputDir = resolveOutputDir(outputOptions);
|
|
239
|
+
const outputDir = resolveOutputDir(outputOptions, cwd);
|
|
201
240
|
if (prepared.has(outputDir)) return;
|
|
202
241
|
prepared.add(outputDir);
|
|
203
|
-
const projectRoot = node_path.resolve(projectRootOption ??
|
|
204
|
-
await copyAssets(outputDir, projectRoot, assets);
|
|
242
|
+
const projectRoot = node_path.resolve(cwd, projectRootOption ?? ".");
|
|
205
243
|
if (!nodeModules.length) {
|
|
206
244
|
await writeOutputPackageJson(outputDir);
|
|
207
245
|
return;
|
|
208
246
|
}
|
|
209
|
-
const depsLockFilePath = await resolveLockFile(depsLockFilePathOption);
|
|
247
|
+
const depsLockFilePath = await resolveLockFile(depsLockFilePathOption, cwd);
|
|
210
248
|
if (!depsLockFilePath) throw new Error(`${PLUGIN_NAME} cannot find a ${PNPM_LOCK}, which \`nodeModules\` requires. Specify one with \`depsLockFilePath\`.`);
|
|
211
249
|
const workspaceRoot = node_path.dirname(depsLockFilePath);
|
|
212
250
|
const projectPackageJson = node_path.join(projectRoot, "package.json");
|
|
213
251
|
if (!await require_fs.pathExists(projectPackageJson)) throw new Error(`${PLUGIN_NAME} cannot find a package.json in '${projectRoot}', which \`nodeModules\` requires. Specify the directory with \`projectRoot\`.`);
|
|
214
252
|
await writeOutputPackageJson(outputDir, {
|
|
215
253
|
dependencies: await extractDependencies(projectPackageJson, nodeModules),
|
|
216
|
-
packageManager: await
|
|
254
|
+
packageManager: await readPackageManagerPin(workspaceRoot)
|
|
217
255
|
});
|
|
218
256
|
const stagedFiles = [];
|
|
219
257
|
try {
|
|
220
258
|
await stageWorkspaceFiles(workspaceRoot, outputDir, stagedFiles);
|
|
221
259
|
const lockDest = node_path.join(outputDir, PNPM_LOCK);
|
|
222
|
-
await fs_extra.default.promises.copyFile(depsLockFilePath, lockDest);
|
|
223
260
|
stagedFiles.push(lockDest);
|
|
261
|
+
await fs_extra.default.promises.copyFile(depsLockFilePath, lockDest);
|
|
224
262
|
require_logging.log.plain(require_logging.log.bold(PLUGIN_NAME), "installing", require_logging.log.bold(nodeModules.join(", ")));
|
|
225
263
|
const [bin, ...args] = PNPM_INSTALL_COMMAND;
|
|
226
264
|
await require_exec.createExec({ cwd: outputDir })(bin, ...args);
|
|
227
265
|
} catch (err) {
|
|
228
|
-
await stripInstallFiles(outputDir,
|
|
266
|
+
await stripInstallFiles(outputDir, [
|
|
267
|
+
...stagedFiles,
|
|
268
|
+
node_path.join(outputDir, "package.json"),
|
|
269
|
+
node_path.join(outputDir, "node_modules")
|
|
270
|
+
], err);
|
|
229
271
|
throw err;
|
|
230
272
|
}
|
|
231
273
|
await stripInstallFiles(outputDir, stagedFiles);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skuba-lib/api",
|
|
3
|
-
"version": "2.3.0-rolldown-cdk-bundle-plugin-
|
|
3
|
+
"version": "2.3.0-rolldown-cdk-bundle-plugin-20260804234000",
|
|
4
4
|
"description": "Node.js development API for skuba",
|
|
5
5
|
"homepage": "https://github.com/seek-oss/skuba#readme",
|
|
6
6
|
"bugs": {
|