1 | 'use strict';
|
2 |
|
3 | var fs = require('fs-extra');
|
4 | require('semver');
|
5 | require('@yarnpkg/parsers');
|
6 | require('@yarnpkg/lockfile');
|
7 | var packages = require('./packages-f8fc8f67.cjs.js');
|
8 | var index = require('./index-09611511.cjs.js');
|
9 | var chalk = require('chalk');
|
10 | var sortBy = require('lodash/sortBy');
|
11 | var groupBy = require('lodash/groupBy');
|
12 | var run = require('./run-a95417b1.cjs.js');
|
13 | require('minimatch');
|
14 | require('@manypkg/get-packages');
|
15 | require('commander');
|
16 | require('@backstage/cli-common');
|
17 | require('@backstage/errors');
|
18 | require('child_process');
|
19 | require('util');
|
20 |
|
21 | function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
22 |
|
23 | var fs__default = _interopDefaultLegacy(fs);
|
24 | var chalk__default = _interopDefaultLegacy(chalk);
|
25 | var sortBy__default = _interopDefaultLegacy(sortBy);
|
26 | var groupBy__default = _interopDefaultLegacy(groupBy);
|
27 |
|
28 | function createStepDefinition(config) {
|
29 | return config;
|
30 | }
|
31 |
|
32 | class AppRouteStep {
|
33 | constructor(data) {
|
34 | this.data = data;
|
35 | }
|
36 | async run() {
|
37 | var _a;
|
38 | const { path, element, packageName } = this.data;
|
39 | const appTsxPath = index.paths.resolveTargetRoot("packages/app/src/App.tsx");
|
40 | const contents = await fs__default["default"].readFile(appTsxPath, "utf-8");
|
41 | let failed = false;
|
42 | const contentsWithRoute = contents.replace(/(\s*)<\/FlatRoutes>/, `$1 <Route path="${path}" element={${element}} />$1</FlatRoutes>`);
|
43 | if (contentsWithRoute === contents) {
|
44 | failed = true;
|
45 | }
|
46 | const componentName = (_a = element.match(/[A-Za-z0-9]+/)) == null ? void 0 : _a[0];
|
47 | if (!componentName) {
|
48 | throw new Error(`Could not find component name in ${element}`);
|
49 | }
|
50 | const contentsWithImport = contentsWithRoute.replace(/^import /m, `import { ${componentName} } from '${packageName}';
|
51 | import `);
|
52 | if (contentsWithImport === contentsWithRoute) {
|
53 | failed = true;
|
54 | }
|
55 | if (failed) {
|
56 | console.log("Failed to automatically add a route to package/app/src/App.tsx");
|
57 | console.log(`Action needed, add the following:`);
|
58 | console.log(`1. import { ${componentName} } from '${packageName}';`);
|
59 | console.log(`2. <Route path="${path}" element={${element}} />`);
|
60 | } else {
|
61 | await fs__default["default"].writeFile(appTsxPath, contentsWithImport);
|
62 | }
|
63 | }
|
64 | }
|
65 | const appRoute = createStepDefinition({
|
66 | type: "app-route",
|
67 | deserialize(obj, pkg) {
|
68 | const { path, element } = obj;
|
69 | if (!path || typeof path !== "string") {
|
70 | throw new Error("Invalid install step, 'path' must be a string");
|
71 | }
|
72 | if (!element || typeof element !== "string") {
|
73 | throw new Error("Invalid install step, 'element' must be a string");
|
74 | }
|
75 | return new AppRouteStep({ path, element, packageName: pkg.name });
|
76 | },
|
77 | create(data) {
|
78 | return new AppRouteStep(data);
|
79 | }
|
80 | });
|
81 |
|
82 | class DependenciesStep {
|
83 | constructor(data) {
|
84 | this.data = data;
|
85 | }
|
86 | async run() {
|
87 | const { dependencies: dependencies2 } = this.data;
|
88 | const byTarget = groupBy__default["default"](dependencies2, "target");
|
89 | for (const [target, deps] of Object.entries(byTarget)) {
|
90 | const pkgPath = index.paths.resolveTargetRoot(target, "package.json");
|
91 | const pkgJson = await fs__default["default"].readJson(pkgPath);
|
92 | const depTypes = new Set();
|
93 | for (const dep of deps) {
|
94 | depTypes.add(dep.type);
|
95 | pkgJson[dep.type][dep.name] = dep.query;
|
96 | }
|
97 | for (const depType of depTypes) {
|
98 | pkgJson[depType] = Object.fromEntries(sortBy__default["default"](Object.entries(pkgJson[depType]), ([key]) => key));
|
99 | }
|
100 | await fs__default["default"].writeJson(pkgPath, pkgJson, { spaces: 2 });
|
101 | }
|
102 | console.log();
|
103 | console.log(`Running ${chalk__default["default"].blue("yarn install")} to install new versions`);
|
104 | console.log();
|
105 | await run.run("yarn", ["install"]);
|
106 | }
|
107 | }
|
108 | const dependencies = createStepDefinition({
|
109 | type: "dependencies",
|
110 | deserialize() {
|
111 | throw new Error("The dependency step may not be defined in JSON");
|
112 | },
|
113 | create(data) {
|
114 | return new DependenciesStep(data);
|
115 | }
|
116 | });
|
117 |
|
118 | class MessageStep {
|
119 | constructor(data) {
|
120 | this.data = data;
|
121 | }
|
122 | async run() {
|
123 | console.log(this.data.message);
|
124 | }
|
125 | }
|
126 | const message = createStepDefinition({
|
127 | type: "message",
|
128 | deserialize(obj) {
|
129 | const { message: msg } = obj;
|
130 | if (!msg || typeof msg !== "string" && !Array.isArray(msg)) {
|
131 | throw new Error("Invalid install step, 'message' must be a string or array");
|
132 | }
|
133 | return new MessageStep({ message: [msg].flat().join("") });
|
134 | },
|
135 | create(data) {
|
136 | return new MessageStep(data);
|
137 | }
|
138 | });
|
139 |
|
140 | var stepDefinitionMap = Object.freeze({
|
141 | __proto__: null,
|
142 | appRoute: appRoute,
|
143 | dependencies: dependencies,
|
144 | message: message
|
145 | });
|
146 |
|
147 | const stepDefinitions = Object.values(stepDefinitionMap);
|
148 | async function fetchPluginPackage(id) {
|
149 | const searchNames = [`@backstage/plugin-${id}`, `backstage-plugin-${id}`, id];
|
150 | for (const name of searchNames) {
|
151 | try {
|
152 | const packageInfo = await packages.fetchPackageInfo(name);
|
153 | return packageInfo;
|
154 | } catch (error) {
|
155 | if (error.name !== "NotFoundError") {
|
156 | throw error;
|
157 | }
|
158 | }
|
159 | }
|
160 | throw new index.NotFoundError(`No matching package found for '${id}', tried ${searchNames.join(", ")}`);
|
161 | }
|
162 | class PluginInstaller {
|
163 | constructor(steps) {
|
164 | this.steps = steps;
|
165 | }
|
166 | static async resolveSteps(pkg, versionToInstall) {
|
167 | var _a, _b;
|
168 | const steps = [];
|
169 | const dependencies$1 = [];
|
170 | dependencies$1.push({
|
171 | target: "packages/app",
|
172 | type: "dependencies",
|
173 | name: pkg.name,
|
174 | query: versionToInstall || `^${pkg.version}`
|
175 | });
|
176 | steps.push({
|
177 | type: "dependencies",
|
178 | step: dependencies.create({ dependencies: dependencies$1 })
|
179 | });
|
180 | for (const step of (_b = (_a = pkg.experimentalInstallationRecipe) == null ? void 0 : _a.steps) != null ? _b : []) {
|
181 | const { type } = step;
|
182 | const definition = stepDefinitions.find((d) => d.type === type);
|
183 | if (definition) {
|
184 | steps.push({
|
185 | type,
|
186 | step: definition.deserialize(step, pkg)
|
187 | });
|
188 | } else {
|
189 | throw new Error(`Unsupported step type: ${type}`);
|
190 | }
|
191 | }
|
192 | return steps;
|
193 | }
|
194 | async run() {
|
195 | for (const { type, step } of this.steps) {
|
196 | console.log(`Running step ${type}`);
|
197 | await step.run();
|
198 | }
|
199 | }
|
200 | }
|
201 | async function installPluginAndPeerPlugins(pkg) {
|
202 | const pluginDeps = new Map();
|
203 | pluginDeps.set(pkg.name, { pkg });
|
204 | await loadPeerPluginDeps(pkg, pluginDeps);
|
205 | console.log(`Installing ${pkg.name} AND any peer plugin dependencies.`);
|
206 | for (const [_pluginDepName, pluginDep] of pluginDeps.entries()) {
|
207 | const { pkg: pluginDepPkg, versionToInstall } = pluginDep;
|
208 | console.log(`Installing plugin: ${pluginDepPkg.name}: ${versionToInstall || pluginDepPkg.version}`);
|
209 | const steps = await PluginInstaller.resolveSteps(pluginDepPkg, versionToInstall);
|
210 | const installer = new PluginInstaller(steps);
|
211 | await installer.run();
|
212 | }
|
213 | }
|
214 | async function loadPackageJson(plugin) {
|
215 | if (plugin.endsWith("package.json")) {
|
216 | return await fs__default["default"].readJson(plugin);
|
217 | }
|
218 | return await fetchPluginPackage(plugin);
|
219 | }
|
220 | async function loadPeerPluginDeps(pkg, pluginMap) {
|
221 | var _a, _b;
|
222 | for (const [pluginId, pluginVersion] of Object.entries((_b = (_a = pkg.experimentalInstallationRecipe) == null ? void 0 : _a.peerPluginDependencies) != null ? _b : {})) {
|
223 | const depPkg = await loadPackageJson(pluginId);
|
224 | if (!pluginMap.get(depPkg.name)) {
|
225 | pluginMap.set(depPkg.name, {
|
226 | pkg: depPkg,
|
227 | versionToInstall: pluginVersion
|
228 | });
|
229 | await loadPeerPluginDeps(depPkg, pluginMap);
|
230 | }
|
231 | }
|
232 | }
|
233 | var install = async (pluginId, cmd) => {
|
234 | const from = pluginId || (cmd == null ? void 0 : cmd.from);
|
235 | if (!from) {
|
236 | throw new Error("Missing both <plugin-id> or a package.json file path in the --from flag.");
|
237 | }
|
238 | const pkg = await loadPackageJson(from);
|
239 | await installPluginAndPeerPlugins(pkg);
|
240 | };
|
241 |
|
242 | exports["default"] = install;
|
243 |
|