@backstage/cli-module-build 0.0.0-nightly-20260317031259
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 +18 -0
- package/README.md +23 -0
- package/bin/backstage-cli-module-build +32 -0
- package/config/webpack-public-path.js +31 -0
- package/dist/commands/buildWorkspace.cjs.js +53 -0
- package/dist/commands/buildWorkspace.cjs.js.map +1 -0
- package/dist/commands/package/build/command.cjs.js +136 -0
- package/dist/commands/package/build/command.cjs.js.map +1 -0
- package/dist/commands/package/build/index.cjs.js +10 -0
- package/dist/commands/package/build/index.cjs.js.map +1 -0
- package/dist/commands/package/bundle/command.cjs.js +691 -0
- package/dist/commands/package/bundle/command.cjs.js.map +1 -0
- package/dist/commands/package/bundle/index.cjs.js +10 -0
- package/dist/commands/package/bundle/index.cjs.js.map +1 -0
- package/dist/commands/package/clean.cjs.js +21 -0
- package/dist/commands/package/clean.cjs.js.map +1 -0
- package/dist/commands/package/postpack.cjs.js +15 -0
- package/dist/commands/package/postpack.cjs.js.map +1 -0
- package/dist/commands/package/prepack.cjs.js +29 -0
- package/dist/commands/package/prepack.cjs.js.map +1 -0
- package/dist/commands/package/start/command.cjs.js +80 -0
- package/dist/commands/package/start/command.cjs.js.map +1 -0
- package/dist/commands/package/start/index.cjs.js +10 -0
- package/dist/commands/package/start/index.cjs.js.map +1 -0
- package/dist/commands/package/start/resolveLinkedWorkspace.cjs.js +34 -0
- package/dist/commands/package/start/resolveLinkedWorkspace.cjs.js.map +1 -0
- package/dist/commands/package/start/startBackend.cjs.js +46 -0
- package/dist/commands/package/start/startBackend.cjs.js.map +1 -0
- package/dist/commands/package/start/startFrontend.cjs.js +49 -0
- package/dist/commands/package/start/startFrontend.cjs.js.map +1 -0
- package/dist/commands/package/start/startPackage.cjs.js +53 -0
- package/dist/commands/package/start/startPackage.cjs.js.map +1 -0
- package/dist/commands/repo/build.cjs.js +149 -0
- package/dist/commands/repo/build.cjs.js.map +1 -0
- package/dist/commands/repo/clean.cjs.js +41 -0
- package/dist/commands/repo/clean.cjs.js.map +1 -0
- package/dist/commands/repo/start.cjs.js +199 -0
- package/dist/commands/repo/start.cjs.js.map +1 -0
- package/dist/index.cjs.js +74 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/lib/buildBackend.cjs.js +81 -0
- package/dist/lib/buildBackend.cjs.js.map +1 -0
- package/dist/lib/buildFrontend.cjs.js +49 -0
- package/dist/lib/buildFrontend.cjs.js.map +1 -0
- package/dist/lib/builder/config.cjs.js +257 -0
- package/dist/lib/builder/config.cjs.js.map +1 -0
- package/dist/lib/builder/packager.cjs.js +131 -0
- package/dist/lib/builder/packager.cjs.js.map +1 -0
- package/dist/lib/builder/plugins.cjs.js +125 -0
- package/dist/lib/builder/plugins.cjs.js.map +1 -0
- package/dist/lib/builder/types.cjs.js +10 -0
- package/dist/lib/builder/types.cjs.js.map +1 -0
- package/dist/lib/bundler/ConfigInjectingHtmlWebpackPlugin.cjs.js +43 -0
- package/dist/lib/bundler/ConfigInjectingHtmlWebpackPlugin.cjs.js.map +1 -0
- package/dist/lib/bundler/bundle.cjs.js +189 -0
- package/dist/lib/bundler/bundle.cjs.js.map +1 -0
- package/dist/lib/bundler/config.cjs.js +309 -0
- package/dist/lib/bundler/config.cjs.js.map +1 -0
- package/dist/lib/bundler/hasReactDomClient.cjs.js +17 -0
- package/dist/lib/bundler/hasReactDomClient.cjs.js.map +1 -0
- package/dist/lib/bundler/linkWorkspaces.cjs.js +34 -0
- package/dist/lib/bundler/linkWorkspaces.cjs.js.map +1 -0
- package/dist/lib/bundler/moduleFederation.cjs.js +135 -0
- package/dist/lib/bundler/moduleFederation.cjs.js.map +1 -0
- package/dist/lib/bundler/optimization.cjs.js +68 -0
- package/dist/lib/bundler/optimization.cjs.js.map +1 -0
- package/dist/lib/bundler/packageDetection.cjs.js +124 -0
- package/dist/lib/bundler/packageDetection.cjs.js.map +1 -0
- package/dist/lib/bundler/paths.cjs.js +62 -0
- package/dist/lib/bundler/paths.cjs.js.map +1 -0
- package/dist/lib/bundler/server.cjs.js +231 -0
- package/dist/lib/bundler/server.cjs.js.map +1 -0
- package/dist/lib/bundler/transforms.cjs.js +145 -0
- package/dist/lib/bundler/transforms.cjs.js.map +1 -0
- package/dist/lib/config.cjs.js +94 -0
- package/dist/lib/config.cjs.js.map +1 -0
- package/dist/lib/entryPoints.cjs.js +49 -0
- package/dist/lib/entryPoints.cjs.js.map +1 -0
- package/dist/lib/ipc/IpcServer.cjs.js +60 -0
- package/dist/lib/ipc/IpcServer.cjs.js.map +1 -0
- package/dist/lib/ipc/ServerDataStore.cjs.js +36 -0
- package/dist/lib/ipc/ServerDataStore.cjs.js.map +1 -0
- package/dist/lib/optionsParser.cjs.js +22 -0
- package/dist/lib/optionsParser.cjs.js.map +1 -0
- package/dist/lib/packager/createDistWorkspace.cjs.js +252 -0
- package/dist/lib/packager/createDistWorkspace.cjs.js.map +1 -0
- package/dist/lib/packager/productionPack.cjs.js +160 -0
- package/dist/lib/packager/productionPack.cjs.js.map +1 -0
- package/dist/lib/publishing.cjs.js +40 -0
- package/dist/lib/publishing.cjs.js.map +1 -0
- package/dist/lib/role.cjs.js +24 -0
- package/dist/lib/role.cjs.js.map +1 -0
- package/dist/lib/runner/runBackend.cjs.js +136 -0
- package/dist/lib/runner/runBackend.cjs.js.map +1 -0
- package/dist/lib/typeDistProject.cjs.js +89 -0
- package/dist/lib/typeDistProject.cjs.js.map +1 -0
- package/dist/lib/urls.cjs.js +13 -0
- package/dist/lib/urls.cjs.js.map +1 -0
- package/dist/package.json.cjs.js +143 -0
- package/dist/package.json.cjs.js.map +1 -0
- package/package.json +113 -0
- package/templates/serve_index.html +27 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var cliNode = require('@backstage/cli-node');
|
|
6
|
+
var cliCommon = require('@backstage/cli-common');
|
|
7
|
+
var chalk = require('chalk');
|
|
8
|
+
var cleye = require('cleye');
|
|
9
|
+
var fs = require('fs-extra');
|
|
10
|
+
var node_module = require('node:module');
|
|
11
|
+
var os = require('node:os');
|
|
12
|
+
var node_path = require('node:path');
|
|
13
|
+
var configLoader = require('@backstage/config-loader');
|
|
14
|
+
var buildFrontend = require('../../../lib/buildFrontend.cjs.js');
|
|
15
|
+
var createDistWorkspace = require('../../../lib/packager/createDistWorkspace.cjs.js');
|
|
16
|
+
|
|
17
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
18
|
+
|
|
19
|
+
var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
|
|
20
|
+
var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
|
|
21
|
+
|
|
22
|
+
async function bundleCommand(opts) {
|
|
23
|
+
const pkgJsonPath = cliCommon.targetPaths.resolve("package.json");
|
|
24
|
+
const pkg = await fs__default.default.readJson(pkgJsonPath);
|
|
25
|
+
const outputDestination = opts.outputDestination ? node_path.resolve(opts.outputDestination) : cliCommon.targetPaths.dir;
|
|
26
|
+
const mangledName = pkg.name.replace(/^@/, "").replace(/\//, "-");
|
|
27
|
+
let bundleName = "bundle";
|
|
28
|
+
if (opts.outputName) {
|
|
29
|
+
bundleName = opts.outputName;
|
|
30
|
+
} else if (opts.outputDestination) {
|
|
31
|
+
bundleName = mangledName;
|
|
32
|
+
}
|
|
33
|
+
const target = node_path.join(outputDestination, bundleName);
|
|
34
|
+
const role = pkg.backstage?.role;
|
|
35
|
+
if (!role) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Package ${chalk__default.default.cyan(
|
|
38
|
+
pkg.name
|
|
39
|
+
)} does not have a backstage.role defined in package.json`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
const validRoles = [
|
|
43
|
+
"backend-plugin",
|
|
44
|
+
"backend-plugin-module",
|
|
45
|
+
"frontend-plugin",
|
|
46
|
+
"frontend-plugin-module"
|
|
47
|
+
];
|
|
48
|
+
if (!validRoles.includes(role)) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Package ${chalk__default.default.cyan(pkg.name)} has role ${chalk__default.default.cyan(
|
|
51
|
+
role
|
|
52
|
+
)}, but bundle command only supports: ${validRoles.map((r) => chalk__default.default.cyan(r)).join(", ")}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const pluginType = role === "frontend-plugin" || role === "frontend-plugin-module" ? "frontend" : "backend";
|
|
56
|
+
if (pkg.bundled) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Package ${chalk__default.default.cyan(pkg.name)} has ${chalk__default.default.cyan(
|
|
59
|
+
"bundled: true"
|
|
60
|
+
)} which is not compatible with dynamic plugin bundling.`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
console.log(
|
|
64
|
+
chalk__default.default.blue(`Bundling ${chalk__default.default.cyan(pkg.name)} for dynamic loading...`)
|
|
65
|
+
);
|
|
66
|
+
console.log(`${chalk__default.default.dim("Output:")} ${chalk__default.default.cyan(target)}`);
|
|
67
|
+
if (opts.clean) {
|
|
68
|
+
console.log(chalk__default.default.blue(`Cleaning ${chalk__default.default.cyan(target)}`));
|
|
69
|
+
await fs__default.default.remove(target);
|
|
70
|
+
}
|
|
71
|
+
await fs__default.default.mkdirs(target);
|
|
72
|
+
await fs__default.default.writeFile(node_path.join(target, ".gitignore"), "*\n");
|
|
73
|
+
await fs__default.default.writeFile(node_path.join(target, ".bundle-output"), "");
|
|
74
|
+
const rootPkg = await fs__default.default.readJson(
|
|
75
|
+
node_path.resolve(cliCommon.targetPaths.rootDir, "package.json")
|
|
76
|
+
);
|
|
77
|
+
const needsDependencies = pluginType === "backend" || !!opts.prePackedDir;
|
|
78
|
+
if (needsDependencies) {
|
|
79
|
+
const yarnrcLines = ["nodeLinker: node-modules"];
|
|
80
|
+
try {
|
|
81
|
+
const resolved = await cliCommon.runOutput(["yarn", "config", "get", "yarnPath"], {
|
|
82
|
+
cwd: cliCommon.targetPaths.rootDir
|
|
83
|
+
});
|
|
84
|
+
const yarnPathSentinels = /* @__PURE__ */ new Set(["undefined", "null"]);
|
|
85
|
+
if (resolved && !yarnPathSentinels.has(resolved)) {
|
|
86
|
+
yarnrcLines.push(`yarnPath: ${resolved}`);
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
if (!rootPkg.packageManager) {
|
|
90
|
+
console.warn(
|
|
91
|
+
chalk__default.default.yellow(
|
|
92
|
+
"No yarnPath configured and no packageManager field found. The Yarn version in PATH will be used for lockfile operations."
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
await fs__default.default.writeFile(
|
|
98
|
+
node_path.join(target, ".yarnrc.yml"),
|
|
99
|
+
`${yarnrcLines.join("\n")}
|
|
100
|
+
`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
if (pluginType === "frontend" && opts.build) {
|
|
104
|
+
console.log(chalk__default.default.blue("Building module federation remote..."));
|
|
105
|
+
await buildFrontend.buildFrontend({
|
|
106
|
+
targetDir: cliCommon.targetPaths.dir,
|
|
107
|
+
configPaths: [],
|
|
108
|
+
writeStats: false,
|
|
109
|
+
isModuleFederationRemote: true
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const embeddedResolutions = {};
|
|
113
|
+
const bundleOutputDirs = [];
|
|
114
|
+
try {
|
|
115
|
+
const entries = await fs__default.default.readdir(cliCommon.targetPaths.dir, { withFileTypes: true });
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
if (entry.isDirectory()) {
|
|
118
|
+
if (await fs__default.default.pathExists(
|
|
119
|
+
node_path.join(cliCommon.targetPaths.dir, entry.name, ".bundle-output")
|
|
120
|
+
)) {
|
|
121
|
+
bundleOutputDirs.push(entry.name);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
if (needsDependencies) {
|
|
128
|
+
const embeddedDir = node_path.join(target, "embedded");
|
|
129
|
+
let targets;
|
|
130
|
+
if (opts.prePackedDir) {
|
|
131
|
+
const prePackedDir = node_path.resolve(opts.prePackedDir);
|
|
132
|
+
console.log(
|
|
133
|
+
chalk__default.default.blue(
|
|
134
|
+
`Using pre-packed workspace at ${chalk__default.default.cyan(prePackedDir)}...`
|
|
135
|
+
)
|
|
136
|
+
);
|
|
137
|
+
const packages = await cliNode.PackageGraph.listTargetPackages();
|
|
138
|
+
targets = await createDistWorkspace.resolveLocalDependencies([pkg.name], packages);
|
|
139
|
+
await fs__default.default.ensureDir(embeddedDir);
|
|
140
|
+
for (const dep of targets) {
|
|
141
|
+
const relDir = node_path.relative(cliCommon.targetPaths.rootDir, dep.dir);
|
|
142
|
+
const srcDir = node_path.resolve(prePackedDir, relDir);
|
|
143
|
+
const destDir = node_path.resolve(embeddedDir, relDir);
|
|
144
|
+
if (await fs__default.default.pathExists(srcDir)) {
|
|
145
|
+
await fs__default.default.copy(srcDir, destDir);
|
|
146
|
+
} else {
|
|
147
|
+
console.warn(
|
|
148
|
+
chalk__default.default.yellow(
|
|
149
|
+
` Package ${chalk__default.default.cyan(dep.name)} not found in pre-packed dir (expected at ${chalk__default.default.cyan(relDir)})`
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
const tempWorkspaceDir = await fs__default.default.mkdtemp(
|
|
156
|
+
node_path.join(os.tmpdir(), "bundle-workspace-")
|
|
157
|
+
);
|
|
158
|
+
console.log(chalk__default.default.blue("Packing local dependencies..."));
|
|
159
|
+
const distLog = createStepLogger(
|
|
160
|
+
node_path.join(target, "dist-workspace.log"),
|
|
161
|
+
opts.verbose
|
|
162
|
+
);
|
|
163
|
+
const packingPattern = /^(?:Moving|Repacking) (.+) into dist workspace$/;
|
|
164
|
+
const distLogger = {
|
|
165
|
+
log(msg) {
|
|
166
|
+
const match = msg.match(packingPattern);
|
|
167
|
+
if (match) {
|
|
168
|
+
console.log(` ${chalk__default.default.dim("Packing")} ${chalk__default.default.cyan(match[1])}`);
|
|
169
|
+
}
|
|
170
|
+
distLog.logger.log(msg);
|
|
171
|
+
},
|
|
172
|
+
warn(msg) {
|
|
173
|
+
console.warn(` ${chalk__default.default.yellow(msg)}`);
|
|
174
|
+
distLog.logger.warn(msg);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
try {
|
|
178
|
+
({ targets } = await createDistWorkspace.createDistWorkspace([pkg.name], {
|
|
179
|
+
targetDir: tempWorkspaceDir,
|
|
180
|
+
files: [],
|
|
181
|
+
alwaysPack: true,
|
|
182
|
+
buildDependencies: opts.build,
|
|
183
|
+
buildExcludes: opts.build ? [] : void 0,
|
|
184
|
+
logger: distLogger
|
|
185
|
+
}));
|
|
186
|
+
await fs__default.default.remove(embeddedDir);
|
|
187
|
+
await fs__default.default.move(tempWorkspaceDir, embeddedDir);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
await distLog.close();
|
|
190
|
+
await showLogOnError(distLog.path, opts.verbose);
|
|
191
|
+
throw err;
|
|
192
|
+
} finally {
|
|
193
|
+
if (await fs__default.default.pathExists(tempWorkspaceDir)) {
|
|
194
|
+
await fs__default.default.remove(tempWorkspaceDir);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
await distLog.close();
|
|
198
|
+
await fs__default.default.remove(distLog.path);
|
|
199
|
+
}
|
|
200
|
+
const mainPluginRelDir = node_path.relative(cliCommon.targetPaths.rootDir, cliCommon.targetPaths.dir);
|
|
201
|
+
const mainPluginEmbeddedDir = node_path.resolve(embeddedDir, mainPluginRelDir);
|
|
202
|
+
console.log(
|
|
203
|
+
chalk__default.default.blue(
|
|
204
|
+
`Moving main plugin ${chalk__default.default.cyan(pkg.name)} to bundle root...`
|
|
205
|
+
)
|
|
206
|
+
);
|
|
207
|
+
if (!await fs__default.default.pathExists(mainPluginEmbeddedDir)) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`Main plugin ${chalk__default.default.cyan(pkg.name)} was not found in the embedded workspace at ${chalk__default.default.cyan(mainPluginRelDir)}. Ensure the pre-packed workspace includes this plugin.`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
const mainPluginEntries = await fs__default.default.readdir(mainPluginEmbeddedDir);
|
|
213
|
+
for (const entry of mainPluginEntries) {
|
|
214
|
+
await fs__default.default.move(
|
|
215
|
+
node_path.join(mainPluginEmbeddedDir, entry),
|
|
216
|
+
node_path.join(target, entry),
|
|
217
|
+
{ overwrite: true }
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
await fs__default.default.remove(mainPluginEmbeddedDir);
|
|
221
|
+
if (pluginType === "frontend") {
|
|
222
|
+
const sourceDist = node_path.resolve(cliCommon.targetPaths.dir, "dist");
|
|
223
|
+
if (await fs__default.default.pathExists(sourceDist)) {
|
|
224
|
+
await fs__default.default.copy(sourceDist, node_path.join(target, "dist"), {
|
|
225
|
+
overwrite: true
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const localDeps = targets.filter((t) => t.name !== pkg.name);
|
|
230
|
+
for (const dep of localDeps) {
|
|
231
|
+
const depRelDir = node_path.relative(cliCommon.targetPaths.rootDir, dep.dir);
|
|
232
|
+
const depEmbeddedDir = node_path.resolve(embeddedDir, depRelDir);
|
|
233
|
+
if (!await fs__default.default.pathExists(depEmbeddedDir)) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
embeddedResolutions[dep.name] = `file:./embedded/${depRelDir}`;
|
|
237
|
+
}
|
|
238
|
+
if (Object.keys(embeddedResolutions).length === 0) {
|
|
239
|
+
await fs__default.default.remove(embeddedDir);
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
console.log(chalk__default.default.blue(`Packing main plugin ${chalk__default.default.cyan(pkg.name)}...`));
|
|
243
|
+
const distLog = createStepLogger(
|
|
244
|
+
node_path.join(target, "dist-workspace.log"),
|
|
245
|
+
opts.verbose
|
|
246
|
+
);
|
|
247
|
+
try {
|
|
248
|
+
await createDistWorkspace.packToDirectory({
|
|
249
|
+
packageDir: cliCommon.targetPaths.dir,
|
|
250
|
+
packageName: pkg.name,
|
|
251
|
+
targetDir: target,
|
|
252
|
+
logger: distLog.logger
|
|
253
|
+
});
|
|
254
|
+
} catch (err) {
|
|
255
|
+
await distLog.close();
|
|
256
|
+
await showLogOnError(distLog.path, opts.verbose);
|
|
257
|
+
throw err;
|
|
258
|
+
}
|
|
259
|
+
await distLog.close();
|
|
260
|
+
await fs__default.default.remove(distLog.path);
|
|
261
|
+
}
|
|
262
|
+
for (const dir of bundleOutputDirs) {
|
|
263
|
+
const nestedPath = node_path.join(target, dir);
|
|
264
|
+
if (await fs__default.default.pathExists(nestedPath)) {
|
|
265
|
+
await fs__default.default.remove(nestedPath);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (pluginType === "frontend") {
|
|
269
|
+
const srcExplicitlyIncluded = (pkg.files ?? []).some(
|
|
270
|
+
(f) => f === "src" || f.startsWith("src/")
|
|
271
|
+
);
|
|
272
|
+
if (!srcExplicitlyIncluded) {
|
|
273
|
+
const srcPath = node_path.join(target, "src");
|
|
274
|
+
if (await fs__default.default.pathExists(srcPath)) {
|
|
275
|
+
await fs__default.default.remove(srcPath);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
console.log(chalk__default.default.blue("Filtering config schema..."));
|
|
280
|
+
const schemaPath = node_path.resolve(target, "dist", ".config-schema.json");
|
|
281
|
+
let schemas = [];
|
|
282
|
+
if (pluginType === "frontend" && await fs__default.default.pathExists(schemaPath)) {
|
|
283
|
+
const existing = await fs__default.default.readJson(schemaPath);
|
|
284
|
+
schemas = existing.schemas ?? [];
|
|
285
|
+
} else {
|
|
286
|
+
const configSchema = await configLoader.loadConfigSchema({
|
|
287
|
+
dependencies: [],
|
|
288
|
+
packagePaths: ["package.json"]
|
|
289
|
+
});
|
|
290
|
+
const serialized = configSchema.serialize();
|
|
291
|
+
schemas = serialized.schemas ?? [];
|
|
292
|
+
}
|
|
293
|
+
let schemaWritten = false;
|
|
294
|
+
if (schemas.length > 0) {
|
|
295
|
+
const filtered = filterBundleConfigSchemas(schemas, cliCommon.targetPaths.dir);
|
|
296
|
+
if (filtered.length > 0) {
|
|
297
|
+
await fs__default.default.ensureDir(node_path.resolve(target, "dist"));
|
|
298
|
+
await fs__default.default.writeJson(
|
|
299
|
+
schemaPath,
|
|
300
|
+
{ backstageConfigSchemaVersion: 1, schemas: filtered },
|
|
301
|
+
{ spaces: 2 }
|
|
302
|
+
);
|
|
303
|
+
schemaWritten = true;
|
|
304
|
+
} else {
|
|
305
|
+
console.log(
|
|
306
|
+
chalk__default.default.dim(" No config schemas found for this plugin bundle")
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
console.log(chalk__default.default.dim(" No config schemas found for this plugin bundle"));
|
|
311
|
+
}
|
|
312
|
+
console.log(
|
|
313
|
+
chalk__default.default.blue(
|
|
314
|
+
`Customizing ${chalk__default.default.cyan("package.json")} for dynamic loading...`
|
|
315
|
+
)
|
|
316
|
+
);
|
|
317
|
+
const targetPkgPath = node_path.resolve(target, "package.json");
|
|
318
|
+
const targetPkg = await fs__default.default.readJson(targetPkgPath);
|
|
319
|
+
postProcessBundlePackageJson(
|
|
320
|
+
targetPkg,
|
|
321
|
+
target,
|
|
322
|
+
pluginType,
|
|
323
|
+
needsDependencies ? rootPkg?.resolutions : void 0,
|
|
324
|
+
embeddedResolutions,
|
|
325
|
+
needsDependencies
|
|
326
|
+
);
|
|
327
|
+
if (schemaWritten) {
|
|
328
|
+
targetPkg.configSchema = "dist/.config-schema.json";
|
|
329
|
+
}
|
|
330
|
+
await fs__default.default.writeJson(targetPkgPath, targetPkg, { spaces: 2 });
|
|
331
|
+
if (needsDependencies) {
|
|
332
|
+
await seedBundleLockfile(target, cliCommon.targetPaths.dir, cliCommon.targetPaths.rootDir);
|
|
333
|
+
const sourceCacheFolder = await cliCommon.runOutput(
|
|
334
|
+
["yarn", "config", "get", "cacheFolder"],
|
|
335
|
+
{ cwd: cliCommon.targetPaths.rootDir }
|
|
336
|
+
);
|
|
337
|
+
await pruneBundleLockfile(target, opts.verbose, sourceCacheFolder);
|
|
338
|
+
if (pluginType === "backend") {
|
|
339
|
+
if (opts.install) {
|
|
340
|
+
await installBundleDependencies(target, opts.verbose);
|
|
341
|
+
const yarnDir = node_path.join(target, ".yarn");
|
|
342
|
+
if (await fs__default.default.pathExists(yarnDir)) {
|
|
343
|
+
await fs__default.default.remove(yarnDir);
|
|
344
|
+
}
|
|
345
|
+
console.log(chalk__default.default.blue("Validating plugin entry points..."));
|
|
346
|
+
const NodeModule = require("node:module");
|
|
347
|
+
const origResolveFilename = NodeModule._resolveFilename;
|
|
348
|
+
NodeModule._resolveFilename = (request, ...args) => {
|
|
349
|
+
if (request === `${targetPkg.name}/package.json`) {
|
|
350
|
+
return node_path.resolve(target, "package.json");
|
|
351
|
+
}
|
|
352
|
+
return origResolveFilename(request, ...args);
|
|
353
|
+
};
|
|
354
|
+
try {
|
|
355
|
+
const pluginRequire = node_module.createRequire(`${target}/package.json`);
|
|
356
|
+
const mainModule = pluginRequire(target);
|
|
357
|
+
const alphaPath = node_path.resolve(target, "alpha");
|
|
358
|
+
const alphaModule = await fs__default.default.pathExists(alphaPath) ? pluginRequire(alphaPath) : void 0;
|
|
359
|
+
const isBackendFeature = (v) => !!v && (typeof v === "object" || typeof v === "function") && v.$$type === "@backstage/BackendFeature";
|
|
360
|
+
const hasValidExport = [mainModule, alphaModule].filter(Boolean).some((m) => isBackendFeature(m?.default));
|
|
361
|
+
if (!hasValidExport) {
|
|
362
|
+
throw new Error(
|
|
363
|
+
`Backend plugin is not valid for dynamic loading: it must export a ${chalk__default.default.cyan(
|
|
364
|
+
"BackendFeature"
|
|
365
|
+
)} as default export`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
} finally {
|
|
369
|
+
NodeModule._resolveFilename = origResolveFilename;
|
|
370
|
+
}
|
|
371
|
+
} else {
|
|
372
|
+
console.log(
|
|
373
|
+
chalk__default.default.yellow(
|
|
374
|
+
"Skipping dependency installation and validation. Run without --no-install to validate the bundle."
|
|
375
|
+
)
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
console.log(chalk__default.default.green(`Bundle created at ${chalk__default.default.cyan(target)}`));
|
|
381
|
+
}
|
|
382
|
+
function postProcessBundlePackageJson(targetPkg, targetDir, pluginType, rootResolutions, embeddedResolutions, needsDependencies) {
|
|
383
|
+
targetPkg.scripts = {};
|
|
384
|
+
targetPkg.devDependencies = {};
|
|
385
|
+
if (pluginType === "frontend") {
|
|
386
|
+
targetPkg.main = "./dist/remoteEntry.js";
|
|
387
|
+
const mfTypesIndex = node_path.resolve(
|
|
388
|
+
targetDir,
|
|
389
|
+
"dist",
|
|
390
|
+
"@mf-types",
|
|
391
|
+
"index.d.ts"
|
|
392
|
+
);
|
|
393
|
+
if (fs__default.default.pathExistsSync(mfTypesIndex)) {
|
|
394
|
+
targetPkg.types = "./dist/@mf-types/index.d.ts";
|
|
395
|
+
} else {
|
|
396
|
+
delete targetPkg.types;
|
|
397
|
+
}
|
|
398
|
+
delete targetPkg.exports;
|
|
399
|
+
delete targetPkg.module;
|
|
400
|
+
delete targetPkg.typesVersions;
|
|
401
|
+
} else if (pluginType === "backend") {
|
|
402
|
+
targetPkg.bundleDependencies = true;
|
|
403
|
+
}
|
|
404
|
+
if (needsDependencies) {
|
|
405
|
+
const patchVersionPattern = /^patch:.+@npm%3A([^#]+)#/;
|
|
406
|
+
const stripped = {};
|
|
407
|
+
if (rootResolutions) {
|
|
408
|
+
for (const [key, value] of Object.entries(rootResolutions)) {
|
|
409
|
+
if (typeof value !== "string") {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
const patchMatch = value.match(patchVersionPattern);
|
|
413
|
+
stripped[key] = patchMatch ? patchMatch[1] : value;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
targetPkg.resolutions = {
|
|
417
|
+
...stripped,
|
|
418
|
+
...targetPkg.resolutions,
|
|
419
|
+
...embeddedResolutions
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
async function seedBundleLockfile(targetDir, pluginDir, monorepoRoot) {
|
|
424
|
+
let sourceYarnLock;
|
|
425
|
+
if (await fs__default.default.pathExists(node_path.join(pluginDir, "yarn.lock"))) {
|
|
426
|
+
sourceYarnLock = node_path.join(pluginDir, "yarn.lock");
|
|
427
|
+
} else if (await fs__default.default.pathExists(node_path.join(monorepoRoot, "yarn.lock"))) {
|
|
428
|
+
sourceYarnLock = node_path.join(monorepoRoot, "yarn.lock");
|
|
429
|
+
}
|
|
430
|
+
if (!sourceYarnLock) {
|
|
431
|
+
throw new Error(
|
|
432
|
+
`Could not find a ${chalk__default.default.cyan(
|
|
433
|
+
"yarn.lock"
|
|
434
|
+
)} file in either the plugin directory or the monorepo root (${chalk__default.default.cyan(
|
|
435
|
+
monorepoRoot
|
|
436
|
+
)})`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
const isMonorepoLock = sourceYarnLock === node_path.join(monorepoRoot, "yarn.lock");
|
|
440
|
+
console.log(
|
|
441
|
+
chalk__default.default.blue(
|
|
442
|
+
`Seeding bundle ${chalk__default.default.cyan("yarn.lock")} from source plugin${isMonorepoLock ? " monorepo" : ""} lockfile...`
|
|
443
|
+
)
|
|
444
|
+
);
|
|
445
|
+
await fs__default.default.copyFile(sourceYarnLock, node_path.resolve(targetDir, "yarn.lock"));
|
|
446
|
+
}
|
|
447
|
+
async function pruneBundleLockfile(targetDir, verbose, sourceCacheFolder) {
|
|
448
|
+
console.log(
|
|
449
|
+
chalk__default.default.blue(
|
|
450
|
+
`Pruning bundle ${chalk__default.default.cyan(
|
|
451
|
+
"yarn.lock"
|
|
452
|
+
)} to remove unused dependencies...`
|
|
453
|
+
)
|
|
454
|
+
);
|
|
455
|
+
const pruneLog = createStepLogger(
|
|
456
|
+
node_path.join(targetDir, "lockfile-prune.log"),
|
|
457
|
+
verbose,
|
|
458
|
+
"[lockfile-prune] "
|
|
459
|
+
);
|
|
460
|
+
try {
|
|
461
|
+
await cliCommon.run(
|
|
462
|
+
["yarn", "install", "--no-immutable", "--mode", "update-lockfile"],
|
|
463
|
+
{
|
|
464
|
+
cwd: targetDir,
|
|
465
|
+
env: {
|
|
466
|
+
YARN_ENABLE_GLOBAL_CACHE: "false",
|
|
467
|
+
YARN_ENABLE_NETWORK: "0",
|
|
468
|
+
YARN_ENABLE_MIRROR: "false",
|
|
469
|
+
YARN_CACHE_FOLDER: sourceCacheFolder
|
|
470
|
+
},
|
|
471
|
+
onStdout: pruneLog.logRunOutput("out"),
|
|
472
|
+
onStderr: pruneLog.logRunOutput("err")
|
|
473
|
+
}
|
|
474
|
+
).waitForExit();
|
|
475
|
+
} catch (err) {
|
|
476
|
+
await pruneLog.close();
|
|
477
|
+
await showLogOnError(pruneLog.path, verbose);
|
|
478
|
+
throw err;
|
|
479
|
+
}
|
|
480
|
+
await pruneLog.close();
|
|
481
|
+
await fs__default.default.remove(pruneLog.path);
|
|
482
|
+
}
|
|
483
|
+
async function installBundleDependencies(targetDir, verbose) {
|
|
484
|
+
console.log(chalk__default.default.blue("Installing private dependencies..."));
|
|
485
|
+
const installLog = createStepLogger(
|
|
486
|
+
node_path.join(targetDir, "yarn-install.log"),
|
|
487
|
+
verbose,
|
|
488
|
+
"[yarn-install] "
|
|
489
|
+
);
|
|
490
|
+
try {
|
|
491
|
+
await cliCommon.run(["yarn", "install", "--immutable"], {
|
|
492
|
+
cwd: targetDir,
|
|
493
|
+
onStdout: installLog.logRunOutput("out"),
|
|
494
|
+
onStderr: installLog.logRunOutput("err")
|
|
495
|
+
}).waitForExit();
|
|
496
|
+
} catch (err) {
|
|
497
|
+
await installLog.close();
|
|
498
|
+
await showLogOnError(installLog.path, verbose);
|
|
499
|
+
throw err;
|
|
500
|
+
}
|
|
501
|
+
await installLog.close();
|
|
502
|
+
await fs__default.default.remove(installLog.path);
|
|
503
|
+
}
|
|
504
|
+
function filterBundleConfigSchemas(schemas, pluginDir) {
|
|
505
|
+
const PLUGIN_OR_MODULE_ROLES = /* @__PURE__ */ new Set([
|
|
506
|
+
"backend-plugin",
|
|
507
|
+
"backend-plugin-module",
|
|
508
|
+
"frontend-plugin",
|
|
509
|
+
"frontend-plugin-module"
|
|
510
|
+
]);
|
|
511
|
+
const LIBRARY_ROLES = /* @__PURE__ */ new Set([
|
|
512
|
+
"node-library",
|
|
513
|
+
"common-library",
|
|
514
|
+
"web-library"
|
|
515
|
+
]);
|
|
516
|
+
const allowed = /* @__PURE__ */ new Set();
|
|
517
|
+
const visited = /* @__PURE__ */ new Set();
|
|
518
|
+
const localRequire = node_module.createRequire(node_path.resolve(pluginDir, "package.json"));
|
|
519
|
+
function walk(pkg) {
|
|
520
|
+
if (visited.has(pkg.name)) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
visited.add(pkg.name);
|
|
524
|
+
allowed.add(pkg.name);
|
|
525
|
+
const pluginId = pkg.backstage?.pluginId;
|
|
526
|
+
for (const depName of Object.keys(pkg.dependencies ?? {})) {
|
|
527
|
+
let depPkgPath;
|
|
528
|
+
try {
|
|
529
|
+
depPkgPath = localRequire.resolve(`${depName}/package.json`);
|
|
530
|
+
} catch {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
let depPkg;
|
|
534
|
+
try {
|
|
535
|
+
depPkg = fs__default.default.readJsonSync(depPkgPath);
|
|
536
|
+
} catch {
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
const depRole = depPkg.backstage?.role;
|
|
540
|
+
if (!depPkg.backstage) {
|
|
541
|
+
allowed.add(depName);
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
if (depRole && PLUGIN_OR_MODULE_ROLES.has(depRole)) {
|
|
545
|
+
walk(depPkg);
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
if (depRole && LIBRARY_ROLES.has(depRole) && pluginId && depPkg.backstage?.pluginId === pluginId) {
|
|
549
|
+
allowed.add(depName);
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
let rootPkg;
|
|
555
|
+
try {
|
|
556
|
+
rootPkg = fs__default.default.readJsonSync(node_path.resolve(pluginDir, "package.json"));
|
|
557
|
+
} catch {
|
|
558
|
+
return [];
|
|
559
|
+
}
|
|
560
|
+
walk(rootPkg);
|
|
561
|
+
return schemas.filter((s) => allowed.has(s.packageName));
|
|
562
|
+
}
|
|
563
|
+
const ansiPattern = (
|
|
564
|
+
// eslint-disable-next-line no-control-regex
|
|
565
|
+
/[\x1b\x9b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><~]|\x1b]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/g
|
|
566
|
+
);
|
|
567
|
+
function stripAnsi(str) {
|
|
568
|
+
return str.replace(ansiPattern, "");
|
|
569
|
+
}
|
|
570
|
+
function createStepLogger(logFilePath, verbose, prefix) {
|
|
571
|
+
const logStream = fs__default.default.createWriteStream(logFilePath);
|
|
572
|
+
const writeLine = (line, stream) => {
|
|
573
|
+
const prefixed = prefix ? `${prefix}${line}` : line;
|
|
574
|
+
logStream.write(
|
|
575
|
+
`${stream === "err" ? "[WARN] " : ""}${stripAnsi(prefixed)}
|
|
576
|
+
`
|
|
577
|
+
);
|
|
578
|
+
if (verbose) {
|
|
579
|
+
const writer = stream === "err" ? console.warn : console.log;
|
|
580
|
+
writer(chalk__default.default.dim(prefixed));
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
const logger = {
|
|
584
|
+
log(msg) {
|
|
585
|
+
writeLine(msg, "out");
|
|
586
|
+
},
|
|
587
|
+
warn(msg) {
|
|
588
|
+
writeLine(msg, "err");
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
const logRunOutput = (stream) => (data) => {
|
|
592
|
+
if (prefix) {
|
|
593
|
+
for (const line of data.toString("utf8").split(/\r?\n/)) {
|
|
594
|
+
if (line) writeLine(line, stream);
|
|
595
|
+
}
|
|
596
|
+
} else {
|
|
597
|
+
logStream.write(
|
|
598
|
+
`${stream === "err" ? "[WARN] " : ""}${stripAnsi(
|
|
599
|
+
data.toString("utf8")
|
|
600
|
+
)}`
|
|
601
|
+
);
|
|
602
|
+
if (verbose) {
|
|
603
|
+
const writer = stream === "err" ? console.warn : console.log;
|
|
604
|
+
writer(chalk__default.default.dim(data.toString("utf8")));
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
const close = () => new Promise((r) => logStream.end(r));
|
|
609
|
+
return { logger, logRunOutput, close, path: logFilePath };
|
|
610
|
+
}
|
|
611
|
+
async function showLogOnError(logFilePath, verbose) {
|
|
612
|
+
console.error(
|
|
613
|
+
chalk__default.default.red(`
|
|
614
|
+
Full log available at: ${chalk__default.default.cyan(logFilePath)}`)
|
|
615
|
+
);
|
|
616
|
+
if (!verbose) {
|
|
617
|
+
try {
|
|
618
|
+
const content = await fs__default.default.readFile(logFilePath, "utf8");
|
|
619
|
+
const tail = content.split("\n").slice(-20).join("\n");
|
|
620
|
+
if (tail) {
|
|
621
|
+
console.error(chalk__default.default.dim("\n--- last 20 lines ---"));
|
|
622
|
+
console.error(tail);
|
|
623
|
+
}
|
|
624
|
+
} catch {
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
var command = async ({ args, info }) => {
|
|
629
|
+
const {
|
|
630
|
+
flags: {
|
|
631
|
+
outputDestination,
|
|
632
|
+
outputName,
|
|
633
|
+
clean,
|
|
634
|
+
noBuild,
|
|
635
|
+
noInstall,
|
|
636
|
+
verbose,
|
|
637
|
+
prePackedDir
|
|
638
|
+
}
|
|
639
|
+
} = cleye.cli(
|
|
640
|
+
{
|
|
641
|
+
help: info,
|
|
642
|
+
flags: {
|
|
643
|
+
outputDestination: {
|
|
644
|
+
type: String,
|
|
645
|
+
description: "Directory in which the bundle subdirectory is created. Defaults to the current package directory."
|
|
646
|
+
},
|
|
647
|
+
outputName: {
|
|
648
|
+
type: String,
|
|
649
|
+
description: 'Name of the bundle subdirectory. Defaults to "bundle" when output stays in the package directory, or to the mangled package name (e.g. myorg-plugin-foo) when --output-destination is specified.'
|
|
650
|
+
},
|
|
651
|
+
clean: {
|
|
652
|
+
type: Boolean,
|
|
653
|
+
description: "Clean the output directory before bundling"
|
|
654
|
+
},
|
|
655
|
+
noBuild: {
|
|
656
|
+
type: Boolean,
|
|
657
|
+
description: "Skip building packages (assumes they are already built)"
|
|
658
|
+
},
|
|
659
|
+
noInstall: {
|
|
660
|
+
type: Boolean,
|
|
661
|
+
description: "Skip dependency installation and entrypoint validation."
|
|
662
|
+
},
|
|
663
|
+
verbose: {
|
|
664
|
+
type: Boolean,
|
|
665
|
+
description: "Stream detailed output from internal steps (build, pack, install) to the console. Without this flag, output is captured to per-step log files and only shown on error."
|
|
666
|
+
},
|
|
667
|
+
prePackedDir: {
|
|
668
|
+
type: String,
|
|
669
|
+
description: "Path to a pre-built dist workspace (from build-workspace --alwaysPack). Skips local dependency packing and uses pre-packed packages directly. For frontend plugins, this also enables yarn.lock generation for SBOM."
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
void 0,
|
|
674
|
+
args
|
|
675
|
+
);
|
|
676
|
+
return bundleCommand({
|
|
677
|
+
build: !noBuild,
|
|
678
|
+
install: !noInstall,
|
|
679
|
+
clean: Boolean(clean),
|
|
680
|
+
verbose: Boolean(verbose),
|
|
681
|
+
outputDestination: outputDestination ?? void 0,
|
|
682
|
+
outputName: outputName ?? void 0,
|
|
683
|
+
prePackedDir: prePackedDir ?? void 0
|
|
684
|
+
});
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
exports.bundleCommand = bundleCommand;
|
|
688
|
+
exports.default = command;
|
|
689
|
+
exports.filterBundleConfigSchemas = filterBundleConfigSchemas;
|
|
690
|
+
exports.postProcessBundlePackageJson = postProcessBundlePackageJson;
|
|
691
|
+
//# sourceMappingURL=command.cjs.js.map
|