From b324f97150e02612f6273ad5cecdadce1d6cf813 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 15 Aug 2024 14:34:17 +0200 Subject: [PATCH 01/10] docs/frontend-system: initial extension override docs Signed-off-by: Patrik Oldsberg --- .../architecture/25-extension-overrides.md | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index 42a7f365a1..c3fce5098d 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -10,9 +10,25 @@ description: Frontend extension overrides ## Introduction -An extension override is a building block of the frontend system that allows you to programmatically override app or plugin extensions anywhere in your application. Since the entire application is built mostly out of extensions from the bottom up, this is a powerful feature. You can use it for example to provide your own app root layout, to replace the implementation of a Utility API with a custom one, to override how the catalog page renders itself, and much more. +An important customization point in the frontend system is the ability to override existing extensions. It can be used for anything from slight tweaks to the extension logic, to completely replacing an extension with a custom implementation. While extensions are encouraged to make themselves configurable, there are many situations where you need to override an extension to achieve the desired behavior. The ability to override extensions should be kept in mind when building plugins, and can be a powerful tool to allow for deeper customizations without the need to re-implement large parts of the plugin. -In general, most features should have a good level of customization built into them, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has [configuration](../../conf/) settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin, which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should take advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation for guidance. +In general, most features should have a good level of customization built into them, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has [configuration](../../conf/) settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin, which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should take advantage of those when possible in order to reduce the need and size of extension overrides. + +## Overriding an extension + +Every extension created with `createExtension` comes with an `override` method, including those created from an [extension blueprint](./23-extension-blueprints.md). The `override` method **creates a new extension**, it does not mutate the existing extensions. This new extension in created in such a way that if it is installed adjacent to the existing extension, it will take precedence and override the existing extension. While the `override` method does create new extension instances, it is not intended to be used as a way to create multiple new extensions from a base template, for that use-case you will want to use an [extension blueprint](./23-extension-blueprints.md) instead. + +The following is an example of calling the `.override(...)` method on an extension: + +```tsx +const myOverrideExtension = myExtension.override({ + factory(originalFactory) { + return originalFactory(); + }, +}); +``` + +This override is a no-op, it does not change the behavior of the extension, but simply forwards the outputs from the original extension factory. If you are familiar with [extension blueprints](./23-extension-blueprints.md), you will recognize this factory override pattern where we get access to the original factory function. In fact the only difference is that we do not need to pass any parameters to the original factory. The first parameter is now instead the optional factory context overrides, more on that as we dive into each override pattern in the following sections. ## Override App Extensions From 8c21f6ed0b30b640f6a708266622846bfefbd59b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 15 Aug 2024 14:35:54 +0200 Subject: [PATCH 02/10] docs/frontend-system: document how to override factory outputs Signed-off-by: Patrik Oldsberg --- .../architecture/25-extension-overrides.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index c3fce5098d..f988082258 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -30,6 +30,55 @@ const myOverrideExtension = myExtension.override({ This override is a no-op, it does not change the behavior of the extension, but simply forwards the outputs from the original extension factory. If you are familiar with [extension blueprints](./23-extension-blueprints.md), you will recognize this factory override pattern where we get access to the original factory function. In fact the only difference is that we do not need to pass any parameters to the original factory. The first parameter is now instead the optional factory context overrides, more on that as we dive into each override pattern in the following sections. +## Overriding original factory outputs + +When overriding an extension you can choose to forward the existing outputs, or replace them with your own. The override factory has an exception to the rule that extension factories can only return a single value for each declared output. It will instead always use the **last** value provided for each extension data reference. This makes it possible to forward the outputs from the original factory, but also provide your own, for example: + +```tsx +const myOverrideExtension = myExtension.override({ + factory(originalFactory) { + return [ + ...originalFactory(), + coreExtensionData.reactElement(

Hello Override

), + ]; + }, +}); +``` + +You can also access individual data values from the original factory, in order to decorate the output: + +```tsx +const myOverrideExtension = myExtension.override({ + factory(originalFactory) { + const originalOutput = originalFactory(); + const originalElement = originalOutput.get(coreExtensionData.reactElement); + + return [ + ...originalOutput, + coreExtensionData.reactElement( +
+ Show original element + {originalElement} +
, + ), + ]; + }, +}); +``` + +Just as [extension factories can be declared as a generator function](./20-extensions.md#extension-factory-as-a-generator-function), so can the override factory. Using a generator function, the first example above can be written as follows: + +```tsx +const myOverrideExtension = myExtension.override({ + *factory(originalFactory) { + yield* originalFactory(); + yield coreExtensionData.reactElement(

Hello Override

); + }, +}); +``` + +Note the `yield*` expression, which forwards all values from the provided iterable to the generator, in this case the original factory output. + ## Override App Extensions In order to override an app extension, you must create a new extension and add it to the list of overridden features. The steps are: create your extension overrides and use them in Backstage. From 2205653f0b0c71325b7d03c118e2e1a0f7339bc0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 15 Aug 2024 14:55:26 +0200 Subject: [PATCH 03/10] docs/frontend-system: docs for how to override declared outputs Signed-off-by: Patrik Oldsberg --- .../architecture/25-extension-overrides.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index f988082258..0efa578a30 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -79,6 +79,29 @@ const myOverrideExtension = myExtension.override({ Note the `yield*` expression, which forwards all values from the provided iterable to the generator, in this case the original factory output. +## Overriding declared outputs + +When overriding an extension out can provide a new output declaration. This **replaces** any existing output declaration, which means that you want to forward any of the original output you will need to declare it again. The following example shows how to override an extension and replace the output declaration: + +```tsx +// Original extension +const exampleExtension = createExtension({ + name: 'example', + output: [coreExtensionData.reactElement], + factory: () => [coreExtensionData.reactElement(

Example

)], +}); + +// Override extension, with additional outputs +const overrideExtension = exampleExtension.override({ + output: [coreExtensionData.reactElement, coreExtensionData.routePath], + factory(originalFactory) { + return [...originalFactory(), coreExtensionData.routePath('/example')]; + }, +}); +``` + +When overriding the output declaration you don't need to include the original outputs. Just remember that you will no longer be able to directly forward the output from the original factory, and will still need to adhere to the contract of the input that the extension is attached to. + ## Override App Extensions In order to override an app extension, you must create a new extension and add it to the list of overridden features. The steps are: create your extension overrides and use them in Backstage. From a2bb9d2e351d400851fc0d6f59b4d56d9915e723 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 15 Aug 2024 15:15:03 +0200 Subject: [PATCH 04/10] docs/frontend-system: docs for how to override declared inputs Signed-off-by: Patrik Oldsberg --- .../architecture/25-extension-overrides.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index 0efa578a30..8b6d87bc81 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -102,6 +102,40 @@ const overrideExtension = exampleExtension.override({ When overriding the output declaration you don't need to include the original outputs. Just remember that you will no longer be able to directly forward the output from the original factory, and will still need to adhere to the contract of the input that the extension is attached to. +## Overriding declared inputs + +When overriding an extension you can also provide new input declarations. You can define any number of new inputs, but you are **not** able to override the existing inputs declared by the original extension. The new inputs will be merged with the existing ones, giving the override factory access to both. The following example shows how to override an extension and add a new input declaration: + +```tsx +const myOverrideExtension = myExtension.override({ + inputs: { + myOverrideInput: createExtensionInput([coreExtensionData.reactElement]), + }, + factory(originalFactory, { inputs }) { + const originalOutput = originalFactory(); + const originalElement = originalOutput.get(coreExtensionData.reactElement); + + return [ + ...originalOutput, + coreExtensionData.reactElement( +
+

Original element

+ {originalElement} +

Additional inputs

+
    + {inputs.myOverrideInput.map(i => ( +
  • + {i.get(coreExtensionData.reactElement)} +
  • + ))} +
+
, + ), + ]; + }, +}); +``` + ## Override App Extensions In order to override an app extension, you must create a new extension and add it to the list of overridden features. The steps are: create your extension overrides and use them in Backstage. From 648ab6764fc30c0b8daf0d73cdbbdaf106f522e2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 15 Aug 2024 15:23:30 +0200 Subject: [PATCH 05/10] docs/frontend-system: docs for how to override config schema Signed-off-by: Patrik Oldsberg --- .../architecture/25-extension-overrides.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index 8b6d87bc81..5e55ef3d0e 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -136,6 +136,40 @@ const myOverrideExtension = myExtension.override({ }); ``` +## Overriding configuration schema + +Overriding the configuration schema works very similar to overriding the declared inputs. You can define new configuration fields that will be merged with the existing ones, but you can not re-declare existing fields. The following example shows how to override an extension and add a new configuration field: + +```tsx +// Original extension +const exampleExtension = createExtension({ + name: 'example', + config: { + schema: { + layout: z => z.enum(['grid', 'list']).optional(), + }, + }, + output: [coreExtensionData.reactElement], + factory: ({ config }) => [ + coreExtensionData.reactElement(), + ], +}); + +const overrideExtension = exampleExtension.override({ + config: { + schema: { + additionalField: z => z.string().optional(), + }, + }, + factory(originalFactory, { config }) { + console.log( + `additionalField=${config.additionalField} layout=${config.layout}`, + ); + return originalFactory(); + }, +}); +``` + ## Override App Extensions In order to override an app extension, you must create a new extension and add it to the list of overridden features. The steps are: create your extension overrides and use them in Backstage. From c7743442caa09d6b68bff9d25e334cdb941758cc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 15 Aug 2024 16:45:12 +0200 Subject: [PATCH 06/10] docs/frontend-system: tweak config override + input override docs Signed-off-by: Patrik Oldsberg --- .../architecture/25-extension-overrides.md | 111 ++++++++++++++++-- 1 file changed, 100 insertions(+), 11 deletions(-) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index 5e55ef3d0e..3ce74139fa 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -141,7 +141,34 @@ const myOverrideExtension = myExtension.override({ Overriding the configuration schema works very similar to overriding the declared inputs. You can define new configuration fields that will be merged with the existing ones, but you can not re-declare existing fields. The following example shows how to override an extension and add a new configuration field: ```tsx -// Original extension +const exampleExtension = createExtension({ + config: { + schema: { + foo: z => z.string(), + }, + }, + // ... +}); + +const overrideExtension = exampleExtension.override({ + config: { + schema: { + bar: z => z.string(), + }, + }, + factory(originalFactory, { config }) { + // + console.log(`foo=${config.foo} bar=${config.bar}`); + return originalFactory(); + }, +}); +``` + +## Overriding original factory config context + +In all examples so far we have called the `originalFactory` callback without any arguments. It is however possible to override parts of the factory context for the original factory using the first parameter of the original factory. This can be useful if you want to override the provided configuration or change the inputs in some way. Note that if you are implementing a `factory` for a blueprint, the override factory context will instead be the second parameter of the original factory function. The following is an example of how to override the configuration for the original factory: + +```tsx const exampleExtension = createExtension({ name: 'example', config: { @@ -151,21 +178,83 @@ const exampleExtension = createExtension({ }, output: [coreExtensionData.reactElement], factory: ({ config }) => [ - coreExtensionData.reactElement(), + coreExtensionData.reactElement( + , + ), ], }); const overrideExtension = exampleExtension.override({ - config: { - schema: { - additionalField: z => z.string().optional(), - }, - }, factory(originalFactory, { config }) { - console.log( - `additionalField=${config.additionalField} layout=${config.layout}`, - ); - return originalFactory(); + return originalFactory({ + config: { + // Switch default layout from 'list' to 'grid' + layout: config.layout ?? 'grid', + }, + }); + }, +}); +``` + +As can be seen in the above example we can provide a new configuration object in the `originalFactory` call using the `config` property. When providing the `config` property we will completely override the original configuration object that would otherwise have been provided to the original factory. Note that this object must adhere to the output type of the configuration schema, which might not be intuitive. It's due to the configuration having already been processed and validated by Zod at this point, which means that things like defaults in the schema will not be applied again. + +## Overriding original factory inputs context + +In addition to the configuration, you are also able to override the inputs provided to the original factory. Just like when overriding configuration you will completely replace the original inputs with the new ones, but you are able to forward the inputs that you are receiving to the override factory. + +You can override each input in one of two ways, which can not be combined. You can forward (or not forward) the original input, optionally filtering out individual items or reordering them. Or you can provide new values for the input, which will replace the original input. When providing new values you must forward all existing inputs and the inputs can not be reordered, and when forwarding the existing inputs you can not provide new values. + +The following example shows how to override the values provided for each input item: + +```tsx +const exampleExtension = createExtension({ + inputs: { + items: createExtensionInput([coreExtensionData.reactElement]), + }, + // ... +}); + +const overrideExtension = exampleExtension.override({ + factory(originalFactory, { inputs }) { + return originalFactory({ + inputs: { + items: inputs.items.map(i => [ + coreExtensionData.reactElement( + {i.get(coreExtensionData.reactElement)}, + ), + ]), + }, + }); + }, +}); +``` + +In contrast, the following example shows how to forward the original inputs, but in a different order: + +```tsx +const exampleExtension = createExtension({ + inputs: { + content: createExtensionInput([coreExtensionData.reactElement], { + singleton: true, + optional: true, + }), + items: createExtensionInput([coreExtensionData.reactElement]), + }, + // ... +}); + +const overrideExtension = exampleExtension.override({ + factory(originalFactory, { inputs }) { + return originalFactory({ + inputs: { + // We can also skip forwarding the original input, if we want to remove it + content: inputs.content, + // Sort items input by their extension ID + items: inputs.items.toSorted((a, b) => + a.node.spec.id.localCompare(b.node.spec.id), + ), + }, + }); }, }); ``` From 177e362d330cbd944266a745b522026d53bcba93 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 15 Aug 2024 16:59:11 +0200 Subject: [PATCH 07/10] docs/frontend-system: slim down createExtensionOverrides docs Signed-off-by: Patrik Oldsberg --- .../architecture/25-extension-overrides.md | 144 +++--------------- 1 file changed, 18 insertions(+), 126 deletions(-) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index 3ce74139fa..aa83c32e8b 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -259,157 +259,49 @@ const overrideExtension = exampleExtension.override({ }); ``` -## Override App Extensions +## Installing override extension in an app -In order to override an app extension, you must create a new extension and add it to the list of overridden features. The steps are: create your extension overrides and use them in Backstage. +To install extension overrides in a Backstage app you should use `plugin.withOverrides` whenever you are overriding or adding extensions to for a plugin. See the section on [overriding a plugin](./15-plugins.md#overriding-a-plugin) for more information. -### Example +There is also a `createExtensionOverrides` function that can be used to install a collection of standalone extensions in an app. This method will be replaced with a different mechanism in the future, but for now remains the only way to override the built-in extensions in the app or to package extensions for a plugin package separate from the plugin itself. -In the example below, we create a file that exports custom extensions for the app's `light` and `dark` themes: +Note that while using either of these options you don't necessarily need to use the extension `.override(...)` method to create the overrides. You can also create new extensions with `createExtension` or a blueprint that are either completely net-new extensions, or override an existing extension by using the same `kind`, `namespace` and `name` to produce the same extension ID. -```tsx title="packages/app/src/themes.tsx" -import { - createThemeExtension, - createExtensionOverrides, -} from '@backstage/frontend-plugin-api'; -import { apertureThemes } from './themes'; -import { ApertureLightIcon, ApertureDarkIcon } from './icons'; +### Creating a standalone extension bundle -// Creating a light theme extension -const apertureLightTheme = createThemeExtension({ - // highlight-start - namespace: 'app', - name: 'light', - // highlight-end - title: 'Aperture Light Theme', - variant: 'light', - icon: , - Provider: ({ children }) => ( - - ), -}); +The following example shows how to create a standalone extension bundle that overrides the search page from the search plugin: -// Creating a dark theme extension -const apertureDarkTheme = createThemeExtension({ - // highlight-start - namespace: 'app', - name: 'dark', - // highlight-end - title: 'Aperture Dark Theme', - variant: 'dark', - icon: , - Provider: ({ children }) => ( - - ), -}); - -// Creating an extension overrides preset -export default createExtensionOverrides({ - extensions: [apertureLightTheme, apertureDarkTheme], -}); -``` - -Note that we declare `namespace` as `'app'` while creating the themes, so the system knows we are overriding app extensions. Additionally, to specifically override the `light` and `dark` theme extensions, we set the `name` option to `light` and `dark`. Therefore, to override app theme extensions, we ensure that the extension `namespace` and `name` match those of the default app theme extension definitions. - -Now we are able to use the overrides in a Backstage app: - -```tsx title="packages/app/src/App.tsx" -import { createApp } from '@backstage/frontend-app-api'; -import themeOverrides from './themes'; - -const app = createApp({ - // highlight-next-line - features: [themeOverrides], -}); - -export default app.createRoot(). -``` - -If the plugin you want to change is internal to your company or you just want to replace one of the application's core extensions, you can decide to store the overrides code directly in the app package or extract them to a separate package. - -Note that it can still be a good idea to split your overrides out into separate packages in large projects. But it's up to you to decide how to group the extensions into extension overrides. - -## Override Plugin Extensions - -To override an extension that is provided by a plugin, you need to provide a new extension that has the same ID as the existing extension. That is, all kind, namespace, and name options must match the extension you want to replace. This means that you typically need to provide an explicit `namespace` when overriding extensions from a plugin. - -:::info -We recommend that plugin developers share the extension IDs in their plugin documentation, but usually you can infer the ID by following the [naming patterns](./50-naming-patterns.md) documentation. -::: - -### Example - -Imagine you have a plugin with the ID `'search'`, and the plugin provides a page extension that you want to fully override with your own custom component. To do so, you need to create your page extension with an explicit `namespace` option that matches that of the plugin that you want to override, in this case `'search'`. If the existing extension also has an explicit `name` you'd need to set the `name` of your override extension to the same value as well. - -```tsx title="packages/app/src/search.tsx" +```tsx import { createPageExtension, createExtensionOverrides, } from '@backstage/frontend-plugin-api'; -// Creating a custom search page extension -const customSearchPage = createPageExtension({ - // highlight-next-line +const customSearchPage = PageBlueprint({ + // Since this is a standalone extension we need to provide the namespace to match the search plugin namespace: 'search', - // Omitting name since it is the index plugin page - defaultPath: '/search', - loader: () => import('./SearchPage').then(m => m.), + params: { + defaultPath: '/search', + loader: () => + import('./CustomSearchPage').then(m => ), + }, }); export default createExtensionOverrides({ - extensions: [customSearchPage] + extensions: [customSearchPage], }); ``` -Don't forget to configure your overrides in the `createApp` function: +Assuming the above code resides in the `@internal/search-page` package, you can install it in your app like this: ```tsx title="packages/app/src/App.tsx" import { createApp } from '@backstage/frontend-app-api'; -import searchOverrides from './search'; +import searchPageOverride from '@internal/search-page'; const app = createApp({ // highlight-next-line - features: [searchOverrides], + features: [searchPageOverride], }); export default app.createRoot(); ``` - -Now let's talk about the last override case, orphan extensions. - -## Create Standalone Extensions - -Sometimes you just need to quickly create a new extension and not overwrite an app extension or plugin. You can also use overrides to create extensions, but remember that if you want to make this extension available for installation by other users, we recommend providing it via a plugin in a separate package. - -### Example - -Imagine you want to create a page that is currently only used by your application, like an Institutional page, for example. You can use overrides to extend the Backstage app to render it. To do so, simply create a page extension and pass it to the app as an override: - -```tsx title="packages/app/src/App.tsx" -import { createApp } from '@backstage/frontend-app-api'; -import { - createPageExtension, - createExtensionOverrides, -} from '@backstage/frontend-plugin-api'; - -const app = createApp({ - features: [ - createExtensionOverrides({ - extensions: [ - // highlight-start - createPageExtension({ - name: 'institutional', - defaultPath: '/institutional', - loader: () => - import('./institutional').then(m => ), - }), - // highlight-end - ], - }), - ], -}); - -export default app.createRoot(); -``` - -Note that we are omitting `namespace` when creating the page extension. When we omit `namespace`, we are telling the system the new extension is standalone and not an application or plugin extension! From 7673308e828ce927f6cd93e3898195f293b8561b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 15 Aug 2024 17:04:47 +0200 Subject: [PATCH 08/10] Update docs/frontend-system/architecture/25-extension-overrides.md Co-authored-by: Ben Lambert Signed-off-by: Patrik Oldsberg --- docs/frontend-system/architecture/25-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index aa83c32e8b..1cfa71aa44 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -277,7 +277,7 @@ import { createExtensionOverrides, } from '@backstage/frontend-plugin-api'; -const customSearchPage = PageBlueprint({ +const customSearchPage = PageBlueprint.make({ // Since this is a standalone extension we need to provide the namespace to match the search plugin namespace: 'search', params: { From d42c283390433ae8a132d3d73a19d010d864cf6e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 16 Aug 2024 11:47:45 +0200 Subject: [PATCH 09/10] Update docs/frontend-system/architecture/25-extension-overrides.md Signed-off-by: Patrik Oldsberg --- docs/frontend-system/architecture/25-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index 1cfa71aa44..4bbb90edfb 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -81,7 +81,7 @@ Note the `yield*` expression, which forwards all values from the provided iterab ## Overriding declared outputs -When overriding an extension out can provide a new output declaration. This **replaces** any existing output declaration, which means that you want to forward any of the original output you will need to declare it again. The following example shows how to override an extension and replace the output declaration: +When overriding an extension you can provide a new output declaration. This **replaces** any existing output declaration, which means that you want to forward any of the original output you will need to declare it again. The following example shows how to override an extension and replace the output declaration: ```tsx // Original extension From 4a8c91ec36799343a1204a71ec772764dfb11f1b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 16 Aug 2024 16:54:36 +0200 Subject: [PATCH 10/10] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Patrik Oldsberg --- .../architecture/25-extension-overrides.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/frontend-system/architecture/25-extension-overrides.md b/docs/frontend-system/architecture/25-extension-overrides.md index 4bbb90edfb..d47e2697a9 100644 --- a/docs/frontend-system/architecture/25-extension-overrides.md +++ b/docs/frontend-system/architecture/25-extension-overrides.md @@ -16,7 +16,7 @@ In general, most features should have a good level of customization built into ## Overriding an extension -Every extension created with `createExtension` comes with an `override` method, including those created from an [extension blueprint](./23-extension-blueprints.md). The `override` method **creates a new extension**, it does not mutate the existing extensions. This new extension in created in such a way that if it is installed adjacent to the existing extension, it will take precedence and override the existing extension. While the `override` method does create new extension instances, it is not intended to be used as a way to create multiple new extensions from a base template, for that use-case you will want to use an [extension blueprint](./23-extension-blueprints.md) instead. +Every extension created with `createExtension` comes with an `override` method, including those created from an [extension blueprint](./23-extension-blueprints.md). The `override` method **creates a new extension**, it does not mutate the existing extension. This new extension in created in such a way that if it is installed adjacent to the existing extension, it will take precedence and override the existing extension. While the `override` method does create new extension instances, it is not intended to be used as a way to create multiple new extensions from a base template, for that use-case you will want to use an [extension blueprint](./23-extension-blueprints.md) instead. The following is an example of calling the `.override(...)` method on an extension: @@ -81,7 +81,7 @@ Note the `yield*` expression, which forwards all values from the provided iterab ## Overriding declared outputs -When overriding an extension you can provide a new output declaration. This **replaces** any existing output declaration, which means that you want to forward any of the original output you will need to declare it again. The following example shows how to override an extension and replace the output declaration: +When overriding an extension you can provide a new output declaration. This **replaces** any existing output declaration, which means that if you want to forward any of the original output you will need to declare it again. The following example shows how to override an extension and replace the output declaration: ```tsx // Original extension @@ -138,7 +138,7 @@ const myOverrideExtension = myExtension.override({ ## Overriding configuration schema -Overriding the configuration schema works very similar to overriding the declared inputs. You can define new configuration fields that will be merged with the existing ones, but you can not re-declare existing fields. The following example shows how to override an extension and add a new configuration field: +Overriding the configuration schema works very similarly to overriding the declared inputs. You can define new configuration fields that will be merged with the existing ones, but you can not re-declare existing fields. The following example shows how to override an extension and add a new configuration field: ```tsx const exampleExtension = createExtension({ @@ -251,7 +251,7 @@ const overrideExtension = exampleExtension.override({ content: inputs.content, // Sort items input by their extension ID items: inputs.items.toSorted((a, b) => - a.node.spec.id.localCompare(b.node.spec.id), + a.node.spec.id.localeCompare(b.node.spec.id), ), }, }); @@ -261,7 +261,7 @@ const overrideExtension = exampleExtension.override({ ## Installing override extension in an app -To install extension overrides in a Backstage app you should use `plugin.withOverrides` whenever you are overriding or adding extensions to for a plugin. See the section on [overriding a plugin](./15-plugins.md#overriding-a-plugin) for more information. +To install extension overrides in a Backstage app you should use `plugin.withOverrides` whenever you are overriding or adding extensions for a plugin. See the section on [overriding a plugin](./15-plugins.md#overriding-a-plugin) for more information. There is also a `createExtensionOverrides` function that can be used to install a collection of standalone extensions in an app. This method will be replaced with a different mechanism in the future, but for now remains the only way to override the built-in extensions in the app or to package extensions for a plugin package separate from the plugin itself.