UNPKG

8.32 kBJavaScriptView Raw
1'use strict';
2
3var fs = require('fs-extra');
4var path = require('path');
5var index = require('./index-09611511.cjs.js');
6var chalk = require('chalk');
7var handlebars = require('handlebars');
8var ora = require('ora');
9var util = require('util');
10var recursive = require('recursive-readdir');
11var child_process = require('child_process');
12var errors = require('@backstage/errors');
13
14function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
15
16var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
17var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
18var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
19var handlebars__default = /*#__PURE__*/_interopDefaultLegacy(handlebars);
20var ora__default = /*#__PURE__*/_interopDefaultLegacy(ora);
21var recursive__default = /*#__PURE__*/_interopDefaultLegacy(recursive);
22
23const TEAM_ID_RE = /^@[-\w]+\/[-\w]+$/;
24const USER_ID_RE = /^@[-\w]+$/;
25const EMAIL_RE = /^[^@]+@[-.\w]+\.[-\w]+$/i;
26const DEFAULT_OWNER = "@backstage/maintainers";
27async function getCodeownersFilePath(rootDir) {
28 const possiblePaths = [
29 path__default["default"].join(rootDir, ".github", "CODEOWNERS"),
30 path__default["default"].join(rootDir, ".gitlab", "CODEOWNERS"),
31 path__default["default"].join(rootDir, "docs", "CODEOWNERS"),
32 path__default["default"].join(rootDir, "CODEOWNERS")
33 ];
34 for (const p of possiblePaths) {
35 if (await fs__default["default"].pathExists(p)) {
36 return p;
37 }
38 }
39 return void 0;
40}
41function isValidSingleOwnerId(id) {
42 if (!id || typeof id !== "string") {
43 return false;
44 }
45 return TEAM_ID_RE.test(id) || USER_ID_RE.test(id) || EMAIL_RE.test(id);
46}
47function parseOwnerIds(spaceSeparatedOwnerIds) {
48 if (!spaceSeparatedOwnerIds || typeof spaceSeparatedOwnerIds !== "string") {
49 return void 0;
50 }
51 const ids = spaceSeparatedOwnerIds.split(" ").filter(Boolean);
52 if (!ids.every(isValidSingleOwnerId)) {
53 return void 0;
54 }
55 return ids;
56}
57async function addCodeownersEntry(ownedPath, ownerStr, codeownersFilePath) {
58 const ownerIds = parseOwnerIds(ownerStr);
59 if (!ownerIds || ownerIds.length === 0) {
60 return false;
61 }
62 let filePath = codeownersFilePath;
63 if (!filePath) {
64 filePath = await getCodeownersFilePath(index.paths.targetRoot);
65 if (!filePath) {
66 return false;
67 }
68 }
69 const allLines = (await fs__default["default"].readFile(filePath, "utf8")).split("\n");
70 const commentLines = [];
71 for (const line of allLines) {
72 if (line[0] !== "#") {
73 break;
74 }
75 commentLines.push(line);
76 }
77 const oldDeclarationEntries = allLines.filter((line) => line[0] !== "#").map((line) => line.split(/\s+/).filter(Boolean)).filter((tokens) => tokens.length >= 2).map((tokens) => ({
78 ownedPath: tokens[0],
79 ownerIds: tokens.slice(1)
80 }));
81 const newDeclarationEntries = oldDeclarationEntries.filter((entry) => entry.ownedPath !== "*").concat([{ ownedPath, ownerIds }]).sort((l1, l2) => l1.ownedPath.localeCompare(l2.ownedPath));
82 newDeclarationEntries.unshift({
83 ownedPath: "*",
84 ownerIds: [DEFAULT_OWNER]
85 });
86 const longestOwnedPath = newDeclarationEntries.reduce((length, entry) => Math.max(length, entry.ownedPath.length), 0);
87 const newDeclarationLines = newDeclarationEntries.map((entry) => {
88 const entryPath = entry.ownedPath + " ".repeat(longestOwnedPath - entry.ownedPath.length);
89 return [entryPath, ...entry.ownerIds].join(" ");
90 });
91 const newLines = [...commentLines, "", ...newDeclarationLines, ""];
92 await fs__default["default"].writeFile(filePath, newLines.join("\n"), "utf8");
93 return true;
94}
95
96const exec = util.promisify(child_process.exec);
97const TASK_NAME_MAX_LENGTH = 14;
98class Task {
99 static log(name = "") {
100 process.stderr.write(`${chalk__default["default"].green(name)}
101`);
102 }
103 static error(message = "") {
104 process.stderr.write(`
105${chalk__default["default"].red(message)}
106
107`);
108 }
109 static section(name) {
110 const title = chalk__default["default"].green(`${name}:`);
111 process.stderr.write(`
112 ${title}
113`);
114 }
115 static exit(code = 0) {
116 process.exit(code);
117 }
118 static async forItem(task, item, taskFunc) {
119 const paddedTask = chalk__default["default"].green(task.padEnd(TASK_NAME_MAX_LENGTH));
120 const spinner = ora__default["default"]({
121 prefixText: chalk__default["default"].green(` ${paddedTask}${chalk__default["default"].cyan(item)}`),
122 spinner: "arc",
123 color: "green"
124 }).start();
125 try {
126 const result = await taskFunc();
127 spinner.succeed();
128 return result;
129 } catch (error) {
130 spinner.fail();
131 throw error;
132 }
133 }
134 static async forCommand(command, options) {
135 try {
136 await Task.forItem("executing", command, async () => {
137 await exec(command, { cwd: options == null ? void 0 : options.cwd });
138 });
139 } catch (error) {
140 errors.assertError(error);
141 if (error.stderr) {
142 process.stderr.write(error.stderr);
143 }
144 if (error.stdout) {
145 process.stdout.write(error.stdout);
146 }
147 if (options == null ? void 0 : options.optional) {
148 Task.error(`Warning: Failed to execute command ${chalk__default["default"].cyan(command)}`);
149 } else {
150 throw new Error(`Failed to execute command '${chalk__default["default"].cyan(command)}', ${error}`);
151 }
152 }
153 }
154}
155async function templatingTask(templateDir, destinationDir, context, versionProvider) {
156 const files = await recursive__default["default"](templateDir).catch((error) => {
157 throw new Error(`Failed to read template directory: ${error.message}`);
158 });
159 const isMonoRepo = await fs__default["default"].pathExists(index.paths.resolveTargetRoot("lerna.json"));
160 for (const file of files) {
161 const destinationFile = file.replace(templateDir, destinationDir);
162 await fs__default["default"].ensureDir(path.dirname(destinationFile));
163 if (file.endsWith(".hbs")) {
164 await Task.forItem("templating", path.basename(file), async () => {
165 const destination = destinationFile.replace(/\.hbs$/, "");
166 const template = await fs__default["default"].readFile(file);
167 const compiled = handlebars__default["default"].compile(template.toString(), {
168 strict: true
169 });
170 const contents = compiled({ name: path.basename(destination), ...context }, {
171 helpers: {
172 versionQuery(name, versionHint) {
173 return versionProvider(name, typeof versionHint === "string" ? versionHint : void 0);
174 }
175 }
176 });
177 await fs__default["default"].writeFile(destination, contents).catch((error) => {
178 throw new Error(`Failed to create file: ${destination}: ${error.message}`);
179 });
180 });
181 } else {
182 if (isMonoRepo && file.match("tsconfig.json")) {
183 continue;
184 }
185 await Task.forItem("copying", path.basename(file), async () => {
186 await fs__default["default"].copyFile(file, destinationFile).catch((error) => {
187 const destination = destinationFile;
188 throw new Error(`Failed to copy file to ${destination} : ${error.message}`);
189 });
190 });
191 }
192 }
193}
194async function addPackageDependency(path, options) {
195 try {
196 const pkgJson = await fs__default["default"].readJson(path);
197 const normalize = (obj) => {
198 if (Object.keys(obj).length === 0) {
199 return void 0;
200 }
201 return Object.fromEntries(Object.keys(obj).sort().map((key) => [key, obj[key]]));
202 };
203 pkgJson.dependencies = normalize({
204 ...pkgJson.dependencies,
205 ...options.dependencies
206 });
207 pkgJson.devDependencies = normalize({
208 ...pkgJson.devDependencies,
209 ...options.devDependencies
210 });
211 pkgJson.peerDependencies = normalize({
212 ...pkgJson.peerDependencies,
213 ...options.peerDependencies
214 });
215 await fs__default["default"].writeJson(path, pkgJson, { spaces: 2 });
216 } catch (error) {
217 throw new Error(`Failed to add package dependencies, ${error}`);
218 }
219}
220
221exports.Task = Task;
222exports.addCodeownersEntry = addCodeownersEntry;
223exports.addPackageDependency = addPackageDependency;
224exports.getCodeownersFilePath = getCodeownersFilePath;
225exports.parseOwnerIds = parseOwnerIds;
226exports.templatingTask = templatingTask;
227//# sourceMappingURL=tasks-c7f94acd.cjs.js.map