@backstage/cli
Version:
CLI for developing Backstage plugins and apps
569 lines (558 loc) • 19.2 kB
JavaScript
;
var chalk = require('chalk');
var fs = require('fs-extra');
var path = require('path');
var os = require('os');
var tar = require('tar');
var partition = require('lodash/partition');
var index = require('./index-3decf946.cjs.js');
var run = require('./run-168542e8.cjs.js');
var rollup = require('rollup');
var commonjs = require('@rollup/plugin-commonjs');
var resolve = require('@rollup/plugin-node-resolve');
var postcss = require('rollup-plugin-postcss');
var esbuild = require('rollup-plugin-esbuild');
var svgr = require('@svgr/rollup');
var dts = require('rollup-plugin-dts');
var json = require('@rollup/plugin-json');
var yaml = require('@rollup/plugin-yaml');
var rollupPluginutils = require('rollup-pluginutils');
var svgrTemplate = require('./svgrTemplate-3549ea1c.cjs.js');
var entryPoints = require('./entryPoints-0cc55995.cjs.js');
var cliNode = require('@backstage/cli-node');
var parallel = require('./parallel-2d9d247e.cjs.js');
var productionPack = require('./productionPack-8b9ac9b7.cjs.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var tar__default = /*#__PURE__*/_interopDefaultLegacy(tar);
var partition__default = /*#__PURE__*/_interopDefaultLegacy(partition);
var commonjs__default = /*#__PURE__*/_interopDefaultLegacy(commonjs);
var resolve__default = /*#__PURE__*/_interopDefaultLegacy(resolve);
var postcss__default = /*#__PURE__*/_interopDefaultLegacy(postcss);
var esbuild__default = /*#__PURE__*/_interopDefaultLegacy(esbuild);
var svgr__default = /*#__PURE__*/_interopDefaultLegacy(svgr);
var dts__default = /*#__PURE__*/_interopDefaultLegacy(dts);
var json__default = /*#__PURE__*/_interopDefaultLegacy(json);
var yaml__default = /*#__PURE__*/_interopDefaultLegacy(yaml);
function forwardFileImports(options) {
const filter = rollupPluginutils.createFilter(options.include, options.exclude);
const exportedFiles = /* @__PURE__ */ new Set();
const generatedFor = /* @__PURE__ */ new Set();
return {
name: "forward-file-imports",
async generateBundle(outputOptions, bundle, isWrite) {
if (!isWrite) {
return;
}
const dir = outputOptions.dir || path.dirname(outputOptions.file);
if (generatedFor.has(dir)) {
return;
}
for (const output of Object.values(bundle)) {
if (output.type !== "chunk") {
continue;
}
const chunk = output;
if (!chunk.facadeModuleId) {
continue;
}
generatedFor.add(dir);
const srcRoot = path.dirname(chunk.facadeModuleId);
await Promise.all(
Array.from(exportedFiles).map(async (exportedFile) => {
const outputPath = path.relative(srcRoot, exportedFile);
const targetFile = path.resolve(dir, outputPath);
await fs__default["default"].ensureDir(path.dirname(targetFile));
await fs__default["default"].copyFile(exportedFile, targetFile);
})
);
return;
}
},
options(inputOptions) {
const origExternal = inputOptions.external;
const external = (id, importer, isResolved) => {
if (typeof origExternal === "function" && origExternal(id, importer, isResolved)) {
return true;
}
if (Array.isArray(origExternal) && origExternal.includes(id)) {
return true;
}
if (!filter(id)) {
return false;
}
if (!importer) {
throw new Error(`Unknown importer of file module ${id}`);
}
const fullId = isResolved ? id : path.resolve(path.dirname(importer), id);
exportedFiles.add(fullId);
return true;
};
return { ...inputOptions, external };
}
};
}
var Output = /* @__PURE__ */ ((Output2) => {
Output2[Output2["esm"] = 0] = "esm";
Output2[Output2["cjs"] = 1] = "cjs";
Output2[Output2["types"] = 2] = "types";
return Output2;
})(Output || {});
const SCRIPT_EXTS = [".js", ".jsx", ".ts", ".tsx"];
function isFileImport(source) {
if (source.startsWith(".")) {
return true;
}
if (source.startsWith("/")) {
return true;
}
if (source.match(/[a-z]:/i)) {
return true;
}
return false;
}
async function makeRollupConfigs(options) {
var _a;
const configs = new Array();
const targetDir = (_a = options.targetDir) != null ? _a : index.paths.targetDir;
let targetPkg = options.packageJson;
if (!targetPkg) {
const packagePath = path.resolve(targetDir, "package.json");
targetPkg = await fs__default["default"].readJson(packagePath);
}
const onwarn = ({ code, message }) => {
if (code === "EMPTY_BUNDLE") {
return;
}
if (options.logPrefix) {
console.log(options.logPrefix + message);
} else {
console.log(message);
}
};
const distDir = path.resolve(targetDir, "dist");
const entryPoints$1 = entryPoints.readEntryPoints(targetPkg);
const scriptEntryPoints = entryPoints$1.filter(
(e) => SCRIPT_EXTS.includes(e.ext)
);
if (options.outputs.has(Output.cjs) || options.outputs.has(Output.esm)) {
const output = new Array();
const mainFields = ["module", "main"];
if (options.outputs.has(Output.cjs)) {
output.push({
dir: distDir,
entryFileNames: `[name].cjs.js`,
chunkFileNames: `cjs/[name]-[hash].cjs.js`,
format: "commonjs",
sourcemap: true,
exports: "named"
});
}
if (options.outputs.has(Output.esm)) {
output.push({
dir: distDir,
entryFileNames: `[name].esm.js`,
chunkFileNames: `esm/[name]-[hash].esm.js`,
format: "module",
sourcemap: true
});
mainFields.unshift("browser");
}
configs.push({
input: Object.fromEntries(
scriptEntryPoints.map((e) => [e.name, path.resolve(targetDir, e.path)])
),
output,
onwarn,
preserveEntrySignatures: "strict",
// All module imports are always marked as external
external: (source, importer, isResolved) => Boolean(importer && !isResolved && !isFileImport(source)),
plugins: [
resolve__default["default"]({ mainFields }),
commonjs__default["default"]({
include: /node_modules/,
exclude: [/\/[^/]+\.(?:stories|test)\.[^/]+$/]
}),
postcss__default["default"](),
forwardFileImports({
exclude: /\.icon\.svg$/,
include: [
/\.svg$/,
/\.png$/,
/\.gif$/,
/\.jpg$/,
/\.jpeg$/,
/\.eot$/,
/\.woff$/,
/\.woff2$/,
/\.ttf$/,
/\.md$/
]
}),
json__default["default"](),
yaml__default["default"](),
svgr__default["default"]({
include: /\.icon\.svg$/,
template: svgrTemplate.svgrTemplate
}),
esbuild__default["default"]({
target: "es2019",
minify: options.minify
})
]
});
}
if (options.outputs.has(Output.types)) {
const input = Object.fromEntries(
scriptEntryPoints.map((e) => [
e.name,
index.paths.resolveTargetRoot(
"dist-types",
path.relative(index.paths.targetRoot, targetDir),
e.path.replace(/\.(?:ts|tsx)$/, ".d.ts")
)
])
);
for (const path$1 of Object.values(input)) {
const declarationsExist = await fs__default["default"].pathExists(path$1);
if (!declarationsExist) {
const declarationPath = path.relative(targetDir, path$1);
throw new Error(
`No declaration files found at ${declarationPath}, be sure to run ${chalk__default["default"].bgRed.white(
"yarn tsc"
)} to generate .d.ts files before packaging`
);
}
}
configs.push({
input,
output: {
dir: distDir,
entryFileNames: `[name].d.ts`,
chunkFileNames: `types/[name]-[hash].d.ts`,
format: "es"
},
external: [
/\.css$/,
/\.scss$/,
/\.sass$/,
/\.svg$/,
/\.eot$/,
/\.woff$/,
/\.woff2$/,
/\.ttf$/
],
onwarn,
plugins: [dts__default["default"]()]
});
}
return configs;
}
function formatErrorMessage(error) {
var _a;
let msg = "";
if (error.code === "PLUGIN_ERROR") {
if (error.plugin === "esbuild") {
msg += `${error.message}`;
if ((_a = error.errors) == null ? void 0 : _a.length) {
msg += `
`;
for (const { text, location } of error.errors) {
const { line, column } = location;
const path$1 = path.relative(index.paths.targetDir, error.id);
const loc = chalk__default["default"].cyan(`${path$1}:${line}:${column}`);
if (text === 'Unexpected "<"' && error.id.endsWith(".js")) {
msg += `${loc}: ${text}, JavaScript files with JSX should use a .jsx extension`;
} else {
msg += `${loc}: ${text}`;
}
}
}
} else {
msg += `(plugin ${error.plugin}) ${error}
`;
}
} else {
if (error.loc) {
const file = `${index.paths.resolveTarget(error.loc.file || error.id)}`;
const pos = `${error.loc.line}:${error.loc.column}`;
msg += `${file} [${pos}]
`;
} else if (error.id) {
msg += `${index.paths.resolveTarget(error.id)}
`;
}
msg += `${error}
`;
if (error.url) {
msg += `${chalk__default["default"].cyan(error.url)}
`;
}
if (error.frame) {
msg += `${chalk__default["default"].dim(error.frame)}
`;
}
}
return msg;
}
async function rollupBuild(config) {
try {
const bundle = await rollup.rollup(config);
if (config.output) {
for (const output of [config.output].flat()) {
await bundle.generate(output);
await bundle.write(output);
}
}
} catch (error) {
throw new Error(formatErrorMessage(error));
}
}
const buildPackage = async (options) => {
try {
const { resolutions } = await fs__default["default"].readJson(
index.paths.resolveTargetRoot("package.json")
);
if (resolutions == null ? void 0 : resolutions.esbuild) {
console.warn(
chalk__default["default"].red(
'Your root package.json contains a "resolutions" entry for "esbuild". This was included in older @backstage/create-app templates in order to work around build issues that have since been fixed. Please remove the entry and run `yarn install`'
)
);
}
} catch {
}
const rollupConfigs = await makeRollupConfigs(options);
await fs__default["default"].remove(index.paths.resolveTarget("dist"));
const buildTasks = rollupConfigs.map(rollupBuild);
await Promise.all(buildTasks);
};
const buildPackages = async (options) => {
if (options.some((opt) => !opt.targetDir)) {
throw new Error("targetDir must be set for all build options");
}
const rollupConfigs = await Promise.all(options.map(makeRollupConfigs));
await Promise.all(
options.map(({ targetDir }) => fs__default["default"].remove(path.resolve(targetDir, "dist")))
);
const buildTasks = rollupConfigs.flat().map((opts) => () => rollupBuild(opts));
await parallel.runParallelWorkers({
items: buildTasks,
worker: async (task) => task()
});
};
function getOutputsForRole(role) {
const outputs = /* @__PURE__ */ new Set();
for (const output of cliNode.PackageRoles.getRoleInfo(role).output) {
if (output === "cjs") {
outputs.add(Output.cjs);
}
if (output === "esm") {
outputs.add(Output.esm);
}
if (output === "types") {
outputs.add(Output.types);
}
}
return outputs;
}
const UNSAFE_PACKAGES = [
...Object.keys(index.dependencies),
...Object.keys(index.devDependencies)
];
function prefixLogFunc(prefix, out) {
return (data) => {
for (const line of data.toString("utf8").split(/\r?\n/)) {
process[out].write(`${prefix} ${line}
`);
}
};
}
async function createDistWorkspace(packageNames, options = {}) {
var _a, _b, _c, _d, _e, _f;
const targetDir = (_a = options.targetDir) != null ? _a : await fs__default["default"].mkdtemp(path.resolve(os.tmpdir(), "dist-workspace"));
const packages = await cliNode.PackageGraph.listTargetPackages();
const packageGraph = cliNode.PackageGraph.fromPackages(packages);
const targetNames = packageGraph.collectPackageNames(packageNames, (node) => {
if (node.packageJson.bundled) {
return void 0;
}
return node.publishedLocalDependencies.keys();
});
const targets = Array.from(targetNames).map((name) => packageGraph.get(name));
if (options.buildDependencies) {
const exclude = (_b = options.buildExcludes) != null ? _b : [];
const configPaths = (_c = options.configPaths) != null ? _c : [];
const toBuild = new Set(
targets.map((_) => _.name).filter((name) => !exclude.includes(name))
);
const standardBuilds = new Array();
const customBuild = new Array();
for (const pkg of packages) {
if (!toBuild.has(pkg.packageJson.name)) {
continue;
}
const role = (_d = pkg.packageJson.backstage) == null ? void 0 : _d.role;
if (!role) {
console.warn(
`Building ${pkg.packageJson.name} separately because it has no role`
);
customBuild.push({ dir: pkg.dir, name: pkg.packageJson.name });
continue;
}
const buildScript = (_e = pkg.packageJson.scripts) == null ? void 0 : _e.build;
if (!buildScript) {
customBuild.push({ dir: pkg.dir, name: pkg.packageJson.name });
continue;
}
if (!buildScript.startsWith("backstage-cli package build")) {
console.warn(
`Building ${pkg.packageJson.name} separately because it has a custom build script, '${buildScript}'`
);
customBuild.push({ dir: pkg.dir, name: pkg.packageJson.name });
continue;
}
if (cliNode.PackageRoles.getRoleInfo(role).output.includes("bundle")) {
console.warn(
`Building ${pkg.packageJson.name} separately because it is a bundled package`
);
const args = buildScript.includes("--config") ? [] : configPaths.map((p) => ["--config", p]).flat();
customBuild.push({ dir: pkg.dir, name: pkg.packageJson.name, args });
continue;
}
const outputs = getOutputsForRole(role);
outputs.delete(Output.types);
if (outputs.size > 0) {
standardBuilds.push({
targetDir: pkg.dir,
packageJson: pkg.packageJson,
outputs,
logPrefix: `${chalk__default["default"].cyan(path.relative(index.paths.targetRoot, pkg.dir))}: `,
// No need to detect these for the backend builds, we assume no minification or types
minify: false
});
}
}
await buildPackages(standardBuilds);
if (customBuild.length > 0) {
await parallel.runParallelWorkers({
items: customBuild,
worker: async ({ name, dir, args }) => {
await run.run("yarn", ["run", "build", ...args || []], {
cwd: dir,
stdoutLogFunc: prefixLogFunc(`${name}: `, "stdout"),
stderrLogFunc: prefixLogFunc(`${name}: `, "stderr")
});
}
});
}
}
await moveToDistWorkspace(
targetDir,
targets,
Boolean(options.alwaysYarnPack)
);
const files = (_f = options.files) != null ? _f : ["yarn.lock", "package.json"];
for (const file of files) {
const src = typeof file === "string" ? file : file.src;
const dest = typeof file === "string" ? file : file.dest;
await fs__default["default"].copy(index.paths.resolveTargetRoot(src), path.resolve(targetDir, dest));
}
if (options.skeleton) {
const skeletonFiles = targets.map((target) => {
const dir = path.relative(index.paths.targetRoot, target.dir);
return path.join(dir, "package.json");
}).sort();
await tar__default["default"].create(
{
file: path.resolve(targetDir, options.skeleton),
cwd: targetDir,
portable: true,
noMtime: true,
gzip: options.skeleton.endsWith(".gz")
},
skeletonFiles
);
}
return targetDir;
}
const FAST_PACK_SCRIPTS = [
void 0,
"backstage-cli prepack",
"backstage-cli package prepack"
];
async function moveToDistWorkspace(workspaceDir, localPackages, alwaysYarnPack) {
const [fastPackPackages, slowPackPackages] = partition__default["default"](
localPackages,
(pkg) => {
var _a;
return !alwaysYarnPack && FAST_PACK_SCRIPTS.includes((_a = pkg.packageJson.scripts) == null ? void 0 : _a.prepack);
}
);
await Promise.all(
fastPackPackages.map(async (target) => {
console.log(`Moving ${target.name} into dist workspace`);
const outputDir = path.relative(index.paths.targetRoot, target.dir);
const absoluteOutputPath = path.resolve(workspaceDir, outputDir);
await productionPack.productionPack({
packageDir: target.dir,
targetDir: absoluteOutputPath
});
})
);
async function pack(target, archive) {
var _a, _b;
console.log(`Repacking ${target.name} into dist workspace`);
const archivePath = path.resolve(workspaceDir, archive);
await run.run("yarn", ["pack", "--filename", archivePath], {
cwd: target.dir
});
if ((_b = (_a = target.packageJson) == null ? void 0 : _a.scripts) == null ? void 0 : _b.postpack) {
await run.run("yarn", ["postpack"], { cwd: target.dir });
}
const outputDir = path.relative(index.paths.targetRoot, target.dir);
const absoluteOutputPath = path.resolve(workspaceDir, outputDir);
await fs__default["default"].ensureDir(absoluteOutputPath);
await tar__default["default"].extract({
file: archivePath,
cwd: absoluteOutputPath,
strip: 1
});
await fs__default["default"].remove(archivePath);
if (target.packageJson.bundled) {
const pkgJson = await fs__default["default"].readJson(
path.resolve(absoluteOutputPath, "package.json")
);
delete pkgJson.dependencies;
delete pkgJson.devDependencies;
delete pkgJson.peerDependencies;
delete pkgJson.optionalDependencies;
await fs__default["default"].writeJson(
path.resolve(absoluteOutputPath, "package.json"),
pkgJson,
{
spaces: 2
}
);
}
}
const [unsafePackages, safePackages] = partition__default["default"](
slowPackPackages,
(p) => UNSAFE_PACKAGES.includes(p.name)
);
for (const target of unsafePackages) {
await pack(target, `temp-package.tgz`);
}
await Promise.all(
safePackages.map(
async (target, index) => pack(target, `temp-package-${index}.tgz`)
)
);
}
exports.Output = Output;
exports.buildPackage = buildPackage;
exports.buildPackages = buildPackages;
exports.createDistWorkspace = createDistWorkspace;
exports.getOutputsForRole = getOutputsForRole;
//# sourceMappingURL=createDistWorkspace-1a69be91.cjs.js.map