From bb9b471bd342e12790bf1aa3756b579484ff5e91 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Sun, 25 Jan 2026 16:55:44 +0100 Subject: [PATCH] add plugin id format warning on frontend framework Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/odd-lemons-occur.md | 5 ++++ .changeset/salty-frogs-cry.md | 5 ++++ ...y-sites-cheer.md => slow-numbers-study.md} | 3 -- .../architecture/08-naming-patterns.md | 8 ++--- .../src/wiring/constants.ts | 3 ++ .../src/wiring/createBackendModule.ts | 22 +++++++------- .../src/wiring/createBackendPlugin.ts | 22 +++++++------- .../src/wiring/constants.ts | 30 +++++++++++++++++++ .../src/wiring/createFrontendPlugin.ts | 8 +++++ 9 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 .changeset/odd-lemons-occur.md create mode 100644 .changeset/salty-frogs-cry.md rename .changeset/{itchy-sites-cheer.md => slow-numbers-study.md} (57%) create mode 100644 packages/frontend-plugin-api/src/wiring/constants.ts diff --git a/.changeset/odd-lemons-occur.md b/.changeset/odd-lemons-occur.md new file mode 100644 index 0000000000..8f97ad0bd8 --- /dev/null +++ b/.changeset/odd-lemons-occur.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +new deprecation warninf will be throw by the framework pluginId should have letters, digits,and dashes, starting with a letter diff --git a/.changeset/salty-frogs-cry.md b/.changeset/salty-frogs-cry.md new file mode 100644 index 0000000000..b0f3a66ffa --- /dev/null +++ b/.changeset/salty-frogs-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +add test for new pluginId and moduleId format validations diff --git a/.changeset/itchy-sites-cheer.md b/.changeset/slow-numbers-study.md similarity index 57% rename from .changeset/itchy-sites-cheer.md rename to .changeset/slow-numbers-study.md index 54f5f2fadf..e53009bffa 100644 --- a/.changeset/itchy-sites-cheer.md +++ b/.changeset/slow-numbers-study.md @@ -1,8 +1,5 @@ --- '@backstage/backend-plugin-api': minor -'@backstage/backend-app-api': minor -'@backstage/config-loader': patch -'@backstage/config': patch --- The backend will now throw an error if a plugin or a module doesn't have a valid ID diff --git a/docs/backend-system/architecture/08-naming-patterns.md b/docs/backend-system/architecture/08-naming-patterns.md index b06d52c287..d073f3382c 100644 --- a/docs/backend-system/architecture/08-naming-patterns.md +++ b/docs/backend-system/architecture/08-naming-patterns.md @@ -11,10 +11,10 @@ As a rule, all names should be camel case, with the exceptions of plugin and mod ### Plugins -| Description | Pattern | Examples | Notes | -| ----------- | ----------------- | ------------------------------------- | -------------------------------------------------- | -| export | `Plugin` | `catalogPlugin`, `userSettingsPlugin` | | -| ID | `''` | `'catalog'`, `'user-settings'` | letters, digits,and dashes, starting with a letter | +| Description | Pattern | Examples | Notes | +| ----------- | ----------------- | ------------------------------------- | --------------------------------------------------- | +| export | `Plugin` | `catalogPlugin`, `userSettingsPlugin` | | +| ID | `''` | `'catalog'`, `'user-settings'` | letters, digits, and dashes, starting with a letter | Example: diff --git a/packages/backend-plugin-api/src/wiring/constants.ts b/packages/backend-plugin-api/src/wiring/constants.ts index 8527802b99..48f8e5a5c7 100644 --- a/packages/backend-plugin-api/src/wiring/constants.ts +++ b/packages/backend-plugin-api/src/wiring/constants.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +// NOTE: changing any of these constants need to be reflected in +// @backstage/frontend-plugin-api/src/wiring/constants.ts as well + /** * The pattern that IDs must match. * diff --git a/packages/backend-plugin-api/src/wiring/createBackendModule.ts b/packages/backend-plugin-api/src/wiring/createBackendModule.ts index f65e11acae..6dcc88eb3f 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendModule.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendModule.ts @@ -55,18 +55,18 @@ export interface CreateBackendModuleOptions { export function createBackendModule( options: CreateBackendModuleOptions, ): BackendFeature { - function getRegistrations() { - if (!ID_PATTERN.test(options.moduleId)) { - console.warn( - `WARNING: The moduleId '${options.moduleId}' for plugin '${options.pluginId}', will be invalid soon please must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - } - if (!ID_PATTERN_OLD.test(options.moduleId)) { - throw new Error( - `Invalid moduleId '${options.moduleId}' for plugin '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - } + if (!ID_PATTERN.test(options.moduleId)) { + console.warn( + `WARNING: The moduleId '${options.moduleId}' for plugin '${options.pluginId}', will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + if (!ID_PATTERN_OLD.test(options.moduleId)) { + throw new Error( + `Invalid moduleId '${options.moduleId}' for plugin '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + function getRegistrations() { const extensionPoints: InternalBackendPluginRegistration['extensionPoints'] = []; let init: InternalBackendModuleRegistration['init'] | undefined = undefined; diff --git a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts index 0116f885d1..1a3d3d4a68 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts @@ -49,18 +49,18 @@ export interface CreateBackendPluginOptions { export function createBackendPlugin( options: CreateBackendPluginOptions, ): BackendFeature { - function getRegistrations() { - if (!ID_PATTERN.test(options.pluginId)) { - console.warn( - `WARNING: The pluginId '${options.pluginId}' will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - } - if (!ID_PATTERN_OLD.test(options.pluginId)) { - throw new Error( - `Invalid pluginId '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - } + if (!ID_PATTERN.test(options.pluginId)) { + console.warn( + `WARNING: The pluginId '${options.pluginId}' will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + if (!ID_PATTERN_OLD.test(options.pluginId)) { + throw new Error( + `Invalid pluginId '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + function getRegistrations() { const extensionPoints: InternalBackendPluginRegistration['extensionPoints'] = []; let init: InternalBackendPluginRegistration['init'] | undefined = undefined; diff --git a/packages/frontend-plugin-api/src/wiring/constants.ts b/packages/frontend-plugin-api/src/wiring/constants.ts new file mode 100644 index 0000000000..5300cfc27f --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/constants.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2025 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. + */ + +// NOTE: changing any of these constants need to be reflected in +// @backstage/backend-plugin-api/src/wiring/constants.ts as well + +/** + * The pattern that IDs must match. + * + * @remarks + * ids must only contain the letters `a` through `z` and digits, in groups separated by + * dashes. Additionally, the very first character of the first group + * must be a letter, not a digit + * + * @public + */ +export const ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/i; diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts index 68b5826baa..ebd4142db3 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts @@ -30,6 +30,7 @@ import { FeatureFlagConfig } from './types'; import { MakeSortedExtensionsMap } from './MakeSortedExtensionsMap'; import { JsonObject } from '@backstage/types'; import { RouteRef, SubRouteRef, ExternalRouteRef } from '../routing'; +import { ID_PATTERN } from './constants'; /** * Information about the plugin. @@ -199,6 +200,13 @@ export function createFrontendPlugin< > { const pluginId = options.pluginId; + if (!ID_PATTERN.test(pluginId)) { + // eslint-disable-next-line no-console + console.warn( + `WARNING: The pluginId '${pluginId}' will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + const extensions = new Array>(); const extensionDefinitionsById = new Map< string,