-
-
- Contributor Spotlight nomination}>
-
-
-
-
-
- );
-};
-
-export default Nominate;
diff --git a/microsite/src/pages/nominate/nominate.module.scss b/microsite/src/pages/nominate/nominate.module.scss
deleted file mode 100644
index cdec103a8a..0000000000
--- a/microsite/src/pages/nominate/nominate.module.scss
+++ /dev/null
@@ -1,32 +0,0 @@
-.nominatePage {
- font-size: 1.25rem;
-
- &,
- & > section,
- & > section > div {
- flex: 1;
- display: flex;
- flex-direction: column;
- }
-
- & > section > div > div {
- flex: 1;
- grid-template-rows: auto 1fr;
- }
-
- h1 {
- font-size: 54px;
- line-height: 56px;
- word-break: break-word;
- }
-
- p {
- text-align: justify;
- }
-
- iframe {
- width: 100%;
- height: 100%;
- min-height: 400px;
- }
-}
diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts
index 67f6a975c1..462821a20f 100644
--- a/packages/backend-common/src/reading/GithubUrlReader.ts
+++ b/packages/backend-common/src/reading/GithubUrlReader.ts
@@ -333,10 +333,7 @@ export class GithubUrlReader implements UrlReader {
// GitHub returns a 403 response with a couple of headers indicating rate
// limit status. See more in the GitHub docs:
// https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting
- if (
- response.status === 403 &&
- response.headers.get('X-RateLimit-Remaining') === '0'
- ) {
+ if (this.integration.parseRateLimitInfo(response).isRateLimited) {
message += ' (rate limit exceeded)';
}
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 5ca7d675d3..edf03167ec 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -62,7 +62,7 @@
"@swc/core": "^1.3.46",
"@swc/helpers": "^0.5.0",
"@swc/jest": "^0.2.22",
- "@types/jest": "^29.0.0",
+ "@types/jest": "^29.5.11",
"@types/webpack-env": "^1.15.2",
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.7.2",
@@ -100,7 +100,7 @@
"handlebars": "^4.7.3",
"html-webpack-plugin": "^5.3.1",
"inquirer": "^8.2.0",
- "jest": "^29.0.2",
+ "jest": "^29.7.0",
"jest-css-modules": "^2.1.0",
"jest-environment-jsdom": "^29.0.2",
"jest-runtime": "^29.0.2",
@@ -112,6 +112,7 @@
"node-libs-browser": "^2.2.1",
"npm-packlist": "^5.0.0",
"ora": "^5.3.0",
+ "p-queue": "^6.6.2",
"postcss": "^8.1.0",
"process": "^0.11.10",
"react-dev-utils": "^12.0.0-next.60",
diff --git a/packages/cli/src/lib/bundler/packageDetection.ts b/packages/cli/src/lib/bundler/packageDetection.ts
index 3aa0955c06..0c357fe79e 100644
--- a/packages/cli/src/lib/bundler/packageDetection.ts
+++ b/packages/cli/src/lib/bundler/packageDetection.ts
@@ -18,6 +18,7 @@ import { BackstagePackageJson } from '@backstage/cli-node';
import { Config, ConfigReader } from '@backstage/config';
import chokidar from 'chokidar';
import fs from 'fs-extra';
+import PQueue from 'p-queue';
import { join as joinPath, resolve as resolvePath } from 'path';
import { paths as cliPaths } from '../paths';
@@ -104,6 +105,9 @@ async function detectPackages(
});
}
+// Make sure we're not issuing multiple writes at the same time, which can cause partial overwrites
+const writeQueue = new PQueue({ concurrency: 1 });
+
async function writeDetectedPackagesModule(
pkgs: { name: string; export?: string; import: string }[],
) {
@@ -116,13 +120,15 @@ async function writeDetectedPackagesModule(
)
.join(',');
- await fs.writeFile(
- joinPath(
- cliPaths.targetRoot,
- 'node_modules',
- `${DETECTED_MODULES_MODULE_NAME}.js`,
+ await writeQueue.add(() =>
+ fs.writeFile(
+ joinPath(
+ cliPaths.targetRoot,
+ 'node_modules',
+ `${DETECTED_MODULES_MODULE_NAME}.js`,
+ ),
+ `window['__@backstage/discovered__'] = { modules: [${requirePackageScript}] };`,
),
- `window['__@backstage/discovered__'] = { modules: [${requirePackageScript}] };`,
);
}
diff --git a/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts b/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts
index 349b71319f..c46f82f4fc 100644
--- a/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts
+++ b/packages/eslint-plugin/src/no-forbidden-package-imports.test.ts
@@ -25,6 +25,11 @@ const ERR = (name: string, path: string) => ({
message: `${name} does not export ${path}`,
});
+// cwd must be restored
+const origDir = process.cwd();
+afterAll(() => {
+ process.chdir(origDir);
+});
process.chdir(FIXTURE);
const ruleTester = new RuleTester({
diff --git a/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts b/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts
index 6888d258cc..31e1674b76 100644
--- a/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts
+++ b/packages/eslint-plugin/src/no-relative-monorepo-imports.test.ts
@@ -28,6 +28,11 @@ const ERR_FORBIDDEN = (newImp: string) => ({
message: `Relative imports of monorepo packages are forbidden, use '${newImp}' instead`,
});
+// cwd must be restored
+const origDir = process.cwd();
+afterAll(() => {
+ process.chdir(origDir);
+});
process.chdir(FIXTURE);
const ruleTester = new RuleTester({
diff --git a/packages/eslint-plugin/src/no-undeclared-imports.test.ts b/packages/eslint-plugin/src/no-undeclared-imports.test.ts
index e92d422057..29e2c3a87c 100644
--- a/packages/eslint-plugin/src/no-undeclared-imports.test.ts
+++ b/packages/eslint-plugin/src/no-undeclared-imports.test.ts
@@ -53,6 +53,11 @@ const ERR_SWITCH_BACK = () => ({
message: 'Switch back to import declaration',
});
+// cwd must be restored
+const origDir = process.cwd();
+afterAll(() => {
+ process.chdir(origDir);
+});
process.chdir(FIXTURE);
const ruleTester = new RuleTester({
diff --git a/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx
new file mode 100644
index 0000000000..7d62103727
--- /dev/null
+++ b/packages/frontend-app-api/src/apis/implementations/ComponentsApi/DefaultComponentsApi.test.tsx
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2023 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 React from 'react';
+import {
+ coreExtensionData,
+ createComponentExtension,
+ createComponentRef,
+ createExtension,
+ createExtensionOverrides,
+} from '@backstage/frontend-plugin-api';
+import { resolveAppNodeSpecs } from '../../../tree/resolveAppNodeSpecs';
+import { resolveAppTree } from '../../../tree/resolveAppTree';
+import { App } from '../../../extensions/App';
+import { DefaultComponentsApi } from './DefaultComponentsApi';
+import { render, screen } from '@testing-library/react';
+import { instantiateAppNodeTree } from '../../../tree/instantiateAppNodeTree';
+
+const testRefA = createComponentRef({ id: 'test.a' });
+const testRefB1 = createComponentRef({ id: 'test.b' });
+const testRefB2 = createComponentRef({ id: 'test.b' });
+
+const baseOverrides = createExtensionOverrides({
+ extensions: [
+ App,
+ createExtension({
+ namespace: 'app',
+ name: 'root',
+ attachTo: { id: 'app', input: 'root' },
+ output: {
+ element: coreExtensionData.reactElement,
+ },
+ factory() {
+ return {
+ element: