diff --git a/packages/cli/src/commands/new/additionalActions.ts b/packages/cli/src/commands/new/additionalActions.ts
deleted file mode 100644
index a2dce11d25..0000000000
--- a/packages/cli/src/commands/new/additionalActions.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2024 The Backstage Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import fs from 'fs-extra';
-import { paths } from '../../lib/paths';
-import { addPackageDependency, addToBackend, Task } from '../../lib/tasks';
-
-export interface AdditionalActionsOptions {
- name: string;
- version: string;
- id: string;
- extensionName: string;
-}
-
-export async function installFrontend(options: AdditionalActionsOptions) {
- if (await fs.pathExists(paths.resolveTargetRoot('packages/app'))) {
- await Task.forItem('app', 'adding dependency', async () => {
- await addPackageDependency(
- paths.resolveTargetRoot('packages/app/package.json'),
- {
- dependencies: {
- [options.name]: `^${options.version}`,
- },
- },
- );
- });
- }
-}
-
-export async function addFrontendLegacy(options: AdditionalActionsOptions) {
- await Task.forItem('app', 'adding import', async () => {
- const pluginsFilePath = paths.resolveTargetRoot('packages/app/src/App.tsx');
- if (!(await fs.pathExists(pluginsFilePath))) {
- return;
- }
-
- const content = await fs.readFile(pluginsFilePath, 'utf8');
- const revLines = content.split('\n').reverse();
-
- const lastImportIndex = revLines.findIndex(line =>
- line.match(/ from ("|').*("|')/),
- );
- const lastRouteIndex = revLines.findIndex(line =>
- line.match(/<\/FlatRoutes/),
- );
-
- if (lastImportIndex !== -1 && lastRouteIndex !== -1) {
- const importLine = `import { ${options.extensionName} } from '${options.name}';`;
- if (!content.includes(importLine)) {
- revLines.splice(lastImportIndex, 0, importLine);
- }
-
- const componentLine = `} />`;
- if (!content.includes(componentLine)) {
- const [indentation] = revLines[lastRouteIndex + 1].match(/^\s*/) ?? [];
- revLines.splice(lastRouteIndex + 1, 0, indentation + componentLine);
- }
-
- const newContent = revLines.reverse().join('\n');
- await fs.writeFile(pluginsFilePath, newContent, 'utf8');
- }
- });
-}
-
-export async function installBackend(options: AdditionalActionsOptions) {
- if (await fs.pathExists(paths.resolveTargetRoot('packages/backend'))) {
- await Task.forItem('backend', 'adding dependency', async () => {
- await addPackageDependency(
- paths.resolveTargetRoot('packages/backend/package.json'),
- {
- dependencies: {
- [options.name]: `^${options.version}`,
- },
- },
- );
- });
- }
-}
-
-export async function addBackend(options: AdditionalActionsOptions) {
- await addToBackend(options.name);
-}
diff --git a/packages/cli/src/commands/new/new.ts b/packages/cli/src/commands/new/new.ts
index 5ce51e5b68..8acb93327e 100644
--- a/packages/cli/src/commands/new/new.ts
+++ b/packages/cli/src/commands/new/new.ts
@@ -30,7 +30,7 @@ import {
} from '../../lib/codeowners';
import { resolvePackageName } from '../../lib/new/util';
-import { executePluginPackageTemplate } from '../../lib/new/tasks';
+import { executePluginPackageTemplate } from '../../lib/new/executePluginPackageTemplate';
import {
readCliConfig,
templateSelector,
@@ -38,8 +38,8 @@ import {
promptOptions,
populateOptions,
createDirName,
- runAdditionalActions,
-} from './util';
+} from '../../lib/new/utils';
+import { runAdditionalActions } from '../../lib/new/additionalActions';
export default async () => {
const pkgJson = await fs.readJson(paths.resolveTargetRoot('package.json'));
diff --git a/packages/cli/src/commands/new/types.ts b/packages/cli/src/commands/new/types.ts
deleted file mode 100644
index 2cbca32e16..0000000000
--- a/packages/cli/src/commands/new/types.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2024 The Backstage Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-export type ConfigurablePrompt =
- | {
- id: string;
- prompt: string;
- validate?: string;
- default?: string | boolean;
- }
- | string;
-
-export interface Template {
- id: string;
- description?: string;
- template: string;
- templatePath: string;
- targetPath: string;
- plugin?: boolean;
- backendModulePrefix?: boolean;
- suffix?: string;
- prompts?: ConfigurablePrompt[];
- additionalActions?: string[];
-}
-
-export interface TemplateLocation {
- id: string;
- target: string;
-}
diff --git a/packages/cli/src/lib/new/additionalActions.ts b/packages/cli/src/lib/new/additionalActions.ts
new file mode 100644
index 0000000000..52aaf0369b
--- /dev/null
+++ b/packages/cli/src/lib/new/additionalActions.ts
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2024 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import fs from 'fs-extra';
+import { paths } from '../paths';
+import { Task } from '../tasks';
+
+interface AdditionalActionsOptions {
+ name: string;
+ version: string;
+ id: string;
+ extensionName: string;
+}
+
+export async function runAdditionalActions(
+ additionalActions: string[],
+ options: AdditionalActionsOptions,
+) {
+ for (const action of additionalActions) {
+ switch (action) {
+ case 'install-frontend':
+ await installFrontend(options);
+ break;
+ case 'add-frontend-legacy':
+ await addFrontendLegacy(options);
+ break;
+ case 'install-backend':
+ await installBackend(options);
+ break;
+ case 'add-backend':
+ await addBackend(options);
+ break;
+ default:
+ throw new Error(`${action} is not a valid additional action`);
+ }
+ }
+}
+
+async function addPackageDependency(
+ path: string,
+ options: {
+ dependencies?: Record;
+ devDependencies?: Record;
+ peerDependencies?: Record;
+ },
+) {
+ try {
+ const pkgJson = await fs.readJson(path);
+
+ const normalize = (obj: Record) => {
+ if (Object.keys(obj).length === 0) {
+ return undefined;
+ }
+ return Object.fromEntries(
+ Object.keys(obj)
+ .sort()
+ .map(key => [key, obj[key]]),
+ );
+ };
+
+ pkgJson.dependencies = normalize({
+ ...pkgJson.dependencies,
+ ...options.dependencies,
+ });
+ pkgJson.devDependencies = normalize({
+ ...pkgJson.devDependencies,
+ ...options.devDependencies,
+ });
+ pkgJson.peerDependencies = normalize({
+ ...pkgJson.peerDependencies,
+ ...options.peerDependencies,
+ });
+
+ await fs.writeJson(path, pkgJson, { spaces: 2 });
+ } catch (error) {
+ throw new Error(`Failed to add package dependencies, ${error}`);
+ }
+}
+
+async function installFrontend(options: AdditionalActionsOptions) {
+ if (await fs.pathExists(paths.resolveTargetRoot('packages/app'))) {
+ await Task.forItem('app', 'adding dependency', async () => {
+ await addPackageDependency(
+ paths.resolveTargetRoot('packages/app/package.json'),
+ {
+ dependencies: {
+ [options.name]: `^${options.version}`,
+ },
+ },
+ );
+ });
+ }
+}
+
+async function addFrontendLegacy(options: AdditionalActionsOptions) {
+ await Task.forItem('app', 'adding import', async () => {
+ const pluginsFilePath = paths.resolveTargetRoot('packages/app/src/App.tsx');
+ if (!(await fs.pathExists(pluginsFilePath))) {
+ return;
+ }
+
+ const content = await fs.readFile(pluginsFilePath, 'utf8');
+ const revLines = content.split('\n').reverse();
+
+ const lastImportIndex = revLines.findIndex(line =>
+ line.match(/ from ("|').*("|')/),
+ );
+ const lastRouteIndex = revLines.findIndex(line =>
+ line.match(/<\/FlatRoutes/),
+ );
+
+ if (lastImportIndex !== -1 && lastRouteIndex !== -1) {
+ const importLine = `import { ${options.extensionName} } from '${options.name}';`;
+ if (!content.includes(importLine)) {
+ revLines.splice(lastImportIndex, 0, importLine);
+ }
+
+ const componentLine = `} />`;
+ if (!content.includes(componentLine)) {
+ const [indentation] = revLines[lastRouteIndex + 1].match(/^\s*/) ?? [];
+ revLines.splice(lastRouteIndex + 1, 0, indentation + componentLine);
+ }
+
+ const newContent = revLines.reverse().join('\n');
+ await fs.writeFile(pluginsFilePath, newContent, 'utf8');
+ }
+ });
+}
+
+async function installBackend(options: AdditionalActionsOptions) {
+ if (await fs.pathExists(paths.resolveTargetRoot('packages/backend'))) {
+ await Task.forItem('backend', 'adding dependency', async () => {
+ await addPackageDependency(
+ paths.resolveTargetRoot('packages/backend/package.json'),
+ {
+ dependencies: {
+ [options.name]: `^${options.version}`,
+ },
+ },
+ );
+ });
+ }
+}
+
+async function addBackend({ name }: AdditionalActionsOptions) {
+ if (await fs.pathExists(paths.resolveTargetRoot('packages/backend'))) {
+ await Task.forItem('backend', `adding ${name}`, async () => {
+ const backendFilePath = paths.resolveTargetRoot(
+ 'packages/backend/src/index.ts',
+ );
+ if (!(await fs.pathExists(backendFilePath))) {
+ return;
+ }
+
+ const content = await fs.readFile(backendFilePath, 'utf8');
+ const lines = content.split('\n');
+ const backendAddLine = `backend.add(import('${name}'));`;
+
+ const backendStartIndex = lines.findIndex(line =>
+ line.match(/backend.start/),
+ );
+
+ if (backendStartIndex !== -1) {
+ const [indentation] = lines[backendStartIndex].match(/^\s*/)!;
+ lines.splice(backendStartIndex, 0, `${indentation}${backendAddLine}`);
+
+ const newContent = lines.join('\n');
+ await fs.writeFile(backendFilePath, newContent, 'utf8');
+ }
+ });
+ }
+}
diff --git a/packages/cli/src/lib/new/tasks.test.ts b/packages/cli/src/lib/new/executePluginPackageTemplate.test.ts
similarity index 97%
rename from packages/cli/src/lib/new/tasks.test.ts
rename to packages/cli/src/lib/new/executePluginPackageTemplate.test.ts
index 3e6423c531..f43601450a 100644
--- a/packages/cli/src/lib/new/tasks.test.ts
+++ b/packages/cli/src/lib/new/executePluginPackageTemplate.test.ts
@@ -22,7 +22,7 @@ import {
mockPaths,
} from './testUtils';
import { CreateContext } from './types';
-import { executePluginPackageTemplate } from './tasks';
+import { executePluginPackageTemplate } from './executePluginPackageTemplate';
import { createMockDirectory } from '@backstage/backend-test-utils';
const mockDir = createMockDirectory();
diff --git a/packages/cli/src/lib/new/tasks.ts b/packages/cli/src/lib/new/executePluginPackageTemplate.ts
similarity index 100%
rename from packages/cli/src/lib/new/tasks.ts
rename to packages/cli/src/lib/new/executePluginPackageTemplate.ts
diff --git a/packages/cli/src/lib/new/types.ts b/packages/cli/src/lib/new/types.ts
index fc9d520a74..bd11702c4f 100644
--- a/packages/cli/src/lib/new/types.ts
+++ b/packages/cli/src/lib/new/types.ts
@@ -40,3 +40,30 @@ export interface CreateContext {
export type Prompt = DistinctQuestion & {
name: string;
};
+
+export type ConfigurablePrompt =
+ | {
+ id: string;
+ prompt: string;
+ validate?: string;
+ default?: string | boolean;
+ }
+ | string;
+
+export interface Template {
+ id: string;
+ description?: string;
+ template: string;
+ templatePath: string;
+ targetPath: string;
+ plugin?: boolean;
+ backendModulePrefix?: boolean;
+ suffix?: string;
+ prompts?: ConfigurablePrompt[];
+ additionalActions?: string[];
+}
+
+export interface TemplateLocation {
+ id: string;
+ target: string;
+}
diff --git a/packages/cli/src/commands/new/util.ts b/packages/cli/src/lib/new/utils.ts
similarity index 87%
rename from packages/cli/src/commands/new/util.ts
rename to packages/cli/src/lib/new/utils.ts
index e6a5ec3fda..cc1cdab90a 100644
--- a/packages/cli/src/commands/new/util.ts
+++ b/packages/cli/src/lib/new/utils.ts
@@ -18,22 +18,15 @@ import { dirname } from 'path';
import { parse } from 'yaml';
import fs from 'fs-extra';
-import { paths } from '../../lib/paths';
+import { paths } from '../paths';
import {
pluginIdPrompt,
moduleIdIdPrompt,
npmRegistryPrompt,
ownerPrompt,
-} from '../../lib/new/prompts';
+} from './prompts';
import defaultTemplates from '../../../templates/all-default-templates';
-import {
- installFrontend,
- addFrontendLegacy,
- installBackend,
- addBackend,
- AdditionalActionsOptions,
-} from './additionalActions';
import { Template, TemplateLocation, ConfigurablePrompt } from './types';
@@ -227,27 +220,3 @@ export function createDirName(template: Template, options: Options) {
}
return options.id;
}
-
-export async function runAdditionalActions(
- additionalActions: string[],
- options: AdditionalActionsOptions,
-) {
- for (const action of additionalActions) {
- switch (action) {
- case 'install-frontend':
- await installFrontend(options);
- break;
- case 'add-frontend-legacy':
- await addFrontendLegacy(options);
- break;
- case 'install-backend':
- await installBackend(options);
- break;
- case 'add-backend':
- await addBackend(options);
- break;
- default:
- throw new Error(`${action} is not a valid additional action`);
- }
- }
-}
diff --git a/packages/cli/src/lib/tasks.ts b/packages/cli/src/lib/tasks.ts
index 4fd7238a81..47a2a98af0 100644
--- a/packages/cli/src/lib/tasks.ts
+++ b/packages/cli/src/lib/tasks.ts
@@ -23,7 +23,6 @@ import { basename, dirname } from 'path';
import recursive from 'recursive-readdir';
import { exec as execCb } from 'child_process';
import { assertError } from '@backstage/errors';
-import { paths } from './paths';
const exec = promisify(execCb);
@@ -156,73 +155,3 @@ export async function templatingTask(
}
}
}
-
-export async function addPackageDependency(
- path: string,
- options: {
- dependencies?: Record;
- devDependencies?: Record;
- peerDependencies?: Record;
- },
-) {
- try {
- const pkgJson = await fs.readJson(path);
-
- const normalize = (obj: Record) => {
- if (Object.keys(obj).length === 0) {
- return undefined;
- }
- return Object.fromEntries(
- Object.keys(obj)
- .sort()
- .map(key => [key, obj[key]]),
- );
- };
-
- pkgJson.dependencies = normalize({
- ...pkgJson.dependencies,
- ...options.dependencies,
- });
- pkgJson.devDependencies = normalize({
- ...pkgJson.devDependencies,
- ...options.devDependencies,
- });
- pkgJson.peerDependencies = normalize({
- ...pkgJson.peerDependencies,
- ...options.peerDependencies,
- });
-
- await fs.writeJson(path, pkgJson, { spaces: 2 });
- } catch (error) {
- throw new Error(`Failed to add package dependencies, ${error}`);
- }
-}
-
-export async function addToBackend(name: string) {
- if (await fs.pathExists(paths.resolveTargetRoot('packages/backend'))) {
- await Task.forItem('backend', `adding ${name}`, async () => {
- const backendFilePath = paths.resolveTargetRoot(
- 'packages/backend/src/index.ts',
- );
- if (!(await fs.pathExists(backendFilePath))) {
- return;
- }
-
- const content = await fs.readFile(backendFilePath, 'utf8');
- const lines = content.split('\n');
- const backendAddLine = `backend.add(import('${name}'));`;
-
- const backendStartIndex = lines.findIndex(line =>
- line.match(/backend.start/),
- );
-
- if (backendStartIndex !== -1) {
- const [indentation] = lines[backendStartIndex].match(/^\s*/)!;
- lines.splice(backendStartIndex, 0, `${indentation}${backendAddLine}`);
-
- const newContent = lines.join('\n');
- await fs.writeFile(backendFilePath, newContent, 'utf8');
- }
- });
- }
-}