frontend-*: initial refactor to create and use -internal

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-09-02 14:50:09 +03:00
parent 1a8837e074
commit 043d7cd963
16 changed files with 234 additions and 91 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/frontend-plugin-api': patch
'@backstage/frontend-test-utils': patch
---
Internal refactor
+5
View File
@@ -0,0 +1,5 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
rules: {
'@backstage/no-top-level-material-ui-4-imports': 'error',
},
});
+3
View File
@@ -0,0 +1,3 @@
# @internal/frontend
This is an internal package used by the other frontend packages. It does not get published to NPM, but instead inlined into consuming packages due to the `backstage.inline` flag in `package.json`.
@@ -0,0 +1,9 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: internal-frontend
title: '@internal/frontend'
spec:
lifecycle: experimental
type: backstage-web-library
owner: maintainers
+39
View File
@@ -0,0 +1,39 @@
{
"name": "@internal/frontend",
"version": "0.0.0",
"backstage": {
"role": "web-library",
"inline": true
},
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "packages/frontend-internal"
},
"license": "Apache-2.0",
"sideEffects": false,
"main": "src/index.ts",
"types": "src/index.ts",
"files": [
"dist"
],
"scripts": {
"lint": "backstage-cli package lint",
"test": "backstage-cli package test"
},
"dependencies": {
"@backstage/frontend-plugin-api": "workspace:^",
"@backstage/types": "workspace:^",
"@backstage/version-bridge": "workspace:^",
"zod": "^3.22.4"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"@backstage/frontend-app-api": "workspace:^",
"@backstage/frontend-test-utils": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^15.0.0"
}
}
+17
View File
@@ -0,0 +1,17 @@
/*
* 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 * from './wiring';
@@ -0,0 +1,104 @@
/*
* 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 {
AnyExtensionDataRef,
ApiHolder,
AppNode,
ExtensionDataValue,
ExtensionDefinition,
ExtensionDefinitionParameters,
ExtensionInput,
PortableSchema,
ResolvedExtensionInputs,
} from '@backstage/frontend-plugin-api';
export type InternalExtensionDefinition<
T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters,
> = ExtensionDefinition<T> & {
readonly kind?: string;
readonly namespace?: string;
readonly name?: string;
readonly attachTo: { id: string; input: string };
readonly disabled: boolean;
readonly configSchema?: PortableSchema<T['config'], T['configInput']>;
} & (
| {
readonly version: 'v1';
readonly inputs: {
[inputName in string]: {
$$type: '@backstage/ExtensionInput';
extensionData: {
[name in string]: AnyExtensionDataRef;
};
config: { optional: boolean; singleton: boolean };
};
};
readonly output: {
[name in string]: AnyExtensionDataRef;
};
factory(context: {
node: AppNode;
apis: ApiHolder;
config: object;
inputs: {
[inputName in string]: unknown;
};
}): {
[inputName in string]: unknown;
};
}
| {
readonly version: 'v2';
readonly inputs: {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
};
readonly output: Array<AnyExtensionDataRef>;
factory(context: {
node: AppNode;
apis: ApiHolder;
config: object;
inputs: ResolvedExtensionInputs<{
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
}>;
}): Iterable<ExtensionDataValue<any, any>>;
}
);
/** @internal */
export function toInternalExtensionDefinition<
T extends ExtensionDefinitionParameters,
>(overrides: ExtensionDefinition<T>): InternalExtensionDefinition<T> {
const internal = overrides as InternalExtensionDefinition<T>;
if (internal.$$type !== '@backstage/ExtensionDefinition') {
throw new Error(
`Invalid extension definition instance, bad type '${internal.$$type}'`,
);
}
const version = internal.version;
if (version !== 'v1' && version !== 'v2') {
throw new Error(
`Invalid extension definition instance, bad version '${version}'`,
);
}
return internal;
}
@@ -0,0 +1,20 @@
/*
* 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 {
toInternalExtensionDefinition,
type InternalExtensionDefinition,
} from './InternalExtensionDefinition';
@@ -15,7 +15,6 @@
*/
import { ApiHolder, AppNode } from '../apis';
import { PortableSchema } from '../schema';
import { Expand } from '../types';
import {
ResolveInputValueOverrides,
@@ -32,6 +31,7 @@ import {
import { ExtensionInput } from './createExtensionInput';
import { z } from 'zod';
import { createSchemaFromZod } from '../schema/createSchemaFromZod';
import { InternalExtensionDefinition } from '@internal/frontend';
/**
* Convert a single extension input into a matching resolved input.
@@ -235,84 +235,6 @@ export type ExtensionDefinition<
}>;
};
/** @internal */
export type InternalExtensionDefinition<
T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters,
> = ExtensionDefinition<T> & {
readonly kind?: string;
readonly namespace?: string;
readonly name?: string;
readonly attachTo: { id: string; input: string };
readonly disabled: boolean;
readonly configSchema?: PortableSchema<T['config'], T['configInput']>;
} & (
| {
readonly version: 'v1';
readonly inputs: {
[inputName in string]: {
$$type: '@backstage/ExtensionInput';
extensionData: {
[name in string]: AnyExtensionDataRef;
};
config: { optional: boolean; singleton: boolean };
};
};
readonly output: {
[name in string]: AnyExtensionDataRef;
};
factory(context: {
node: AppNode;
apis: ApiHolder;
config: object;
inputs: {
[inputName in string]: unknown;
};
}): {
[inputName in string]: unknown;
};
}
| {
readonly version: 'v2';
readonly inputs: {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
};
readonly output: Array<AnyExtensionDataRef>;
factory(context: {
node: AppNode;
apis: ApiHolder;
config: object;
inputs: ResolvedExtensionInputs<{
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
}>;
}): Iterable<ExtensionDataValue<any, any>>;
}
);
/** @internal */
export function toInternalExtensionDefinition<
T extends ExtensionDefinitionParameters,
>(overrides: ExtensionDefinition<T>): InternalExtensionDefinition<T> {
const internal = overrides as InternalExtensionDefinition<T>;
if (internal.$$type !== '@backstage/ExtensionDefinition') {
throw new Error(
`Invalid extension definition instance, bad type '${internal.$$type}'`,
);
}
const version = internal.version;
if (version !== 'v1' && version !== 'v2') {
throw new Error(
`Invalid extension definition instance, bad version '${version}'`,
);
}
return internal;
}
/** @public */
export function createExtension<
UOutput extends AnyExtensionDataRef,
@@ -27,11 +27,9 @@ import {
} from './createExtensionDataRef';
import { createExtensionInput } from './createExtensionInput';
import { RouteRef } from '../routing';
import {
ExtensionDefinition,
toInternalExtensionDefinition,
} from './createExtension';
import { ExtensionDefinition } from './createExtension';
import { createExtensionDataContainer } from './createExtensionDataContainer';
import { toInternalExtensionDefinition } from '@internal/frontend';
function unused(..._any: any[]) {}
@@ -15,10 +15,10 @@
*/
import {
ExtensionDefinition,
InternalExtensionDefinition,
toInternalExtensionDefinition,
} from './createExtension';
} from '@internal/frontend';
import { ExtensionDefinition } from './createExtension';
import {
Extension,
resolveExtensionDefinition,
@@ -15,10 +15,10 @@
*/
import {
ExtensionDefinition,
InternalExtensionDefinition,
toInternalExtensionDefinition,
} from './createExtension';
} from '@internal/frontend';
import { ExtensionDefinition } from './createExtension';
import {
Extension,
ResolveExtensionId,
@@ -19,7 +19,6 @@ import {
ExtensionDefinition,
ExtensionDefinitionParameters,
ResolvedExtensionInputs,
toInternalExtensionDefinition,
} from './createExtension';
import { PortableSchema } from '../schema';
import { ExtensionInput } from './createExtensionInput';
@@ -27,6 +26,7 @@ import {
AnyExtensionDataRef,
ExtensionDataValue,
} from './createExtensionDataRef';
import { toInternalExtensionDefinition } from '@internal/frontend';
/** @public */
export interface Extension<TConfig, TConfigInput = TConfig> {
+3 -1
View File
@@ -36,7 +36,9 @@
"@backstage/frontend-plugin-api": "workspace:^",
"@backstage/plugin-app": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@backstage/types": "workspace:^"
"@backstage/types": "workspace:^",
"@backstage/version-bridge": "workspace:^",
"zod": "^3.22.4"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
@@ -29,8 +29,6 @@ import { JsonArray, JsonObject, JsonValue } from '@backstage/types';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { toInternalExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/createExtension';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { resolveAppTree } from '../../../frontend-app-api/src/tree/resolveAppTree';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { resolveAppNodeSpecs } from '../../../frontend-app-api/src/tree/resolveAppNodeSpecs';
@@ -39,6 +37,7 @@ import { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/insta
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig';
import { TestApiRegistry } from '@backstage/test-utils';
import { toInternalExtensionDefinition } from '@internal/frontend';
/** @public */
export class ExtensionQuery<UOutput extends AnyExtensionDataRef> {
+19
View File
@@ -4522,8 +4522,10 @@ __metadata:
"@backstage/plugin-app": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/types": "workspace:^"
"@backstage/version-bridge": "workspace:^"
"@testing-library/jest-dom": ^6.0.0
"@types/react": "*"
zod: ^3.22.4
peerDependencies:
"@testing-library/react": ^15.0.0
react: ^16.13.1 || ^17.0.0 || ^18.0.0
@@ -10002,6 +10004,23 @@ __metadata:
languageName: node
linkType: hard
"@internal/frontend@workspace:packages/frontend-internal":
version: 0.0.0-use.local
resolution: "@internal/frontend@workspace:packages/frontend-internal"
dependencies:
"@backstage/cli": "workspace:^"
"@backstage/frontend-app-api": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/frontend-test-utils": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/types": "workspace:^"
"@backstage/version-bridge": "workspace:^"
"@testing-library/jest-dom": ^6.0.0
"@testing-library/react": ^15.0.0
zod: ^3.22.4
languageName: unknown
linkType: soft
"@internal/plugin-todo-list-backend@workspace:plugins/example-todo-list-backend":
version: 0.0.0-use.local
resolution: "@internal/plugin-todo-list-backend@workspace:plugins/example-todo-list-backend"