backstage frontend scaffolding building with es (#47722)

# Basic Scaffolding
This is **JUST** scaffolding.

- The code was generated by backstage
- All the changes are related to esbuild / package.json / tsconfig.json

## Note for reviewers

The only 3 things to review are:
- scripts/esbuild.ts
- package.json / package.dist.json
- tsconfig.json

## Test plan
nothing - this is scaffolding / build configuration

<!-- All pull requests REQUIRE a test plan: https://docs.sourcegraph.com/dev/background-information/testing_principles -->

## App preview:

- [Web](https://sg-web-wb-backstage-frontend-scaffold.onrender.com/search)

Check out the [client app preview documentation](https://docs.sourcegraph.com/dev/how-to/client_pr_previews) to learn more.
This commit is contained in:
William Bezuidenhout 2023-02-16 15:18:39 +02:00 committed by GitHub
parent 78c5353b75
commit 1969f3e6b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 2408 additions and 112 deletions

View File

@ -0,0 +1,2 @@
dist
node_modules

View File

@ -1,5 +1,5 @@
{
"name": "@sourcegraph/backstage-common",
"name": "@sourcegraph/backstage-backend",
"description": "Common functionalities for the Sourcegraph plugin",
"version": "0.1.0",
"main": "dist/index.js",
@ -18,13 +18,13 @@
"scripts": {
"dist": "ts-node --transpile-only ./scripts/esbuild.ts && pnpm types && cp package.dist.json dist/package.json",
"types": "tsc -d -p tsconfig.json",
"format": "prettier -w true ./src ",
"format": "prettier -w true .",
"test": "jest"
},
"devDependencies": {
"@backstage/cli": "^0.22.1",
"@jest/globals": "^29.4.0",
"@sourcegraph/build-config": "workspace:^0.0.1",
"@sourcegraph/build-config": "workspace:*",
"@sourcegraph/tsconfig": "^4.0.1",
"babel-jest": "^28.1.3",
"babel-loader": "^9.1.0",

View File

@ -4,17 +4,18 @@ import path from 'path'
import * as esbuild from 'esbuild'
import { rm } from 'shelljs'
import { buildTimerPlugin } from '@sourcegraph/build-config'
import { WORKSPACES_PATH, buildTimerPlugin } from '@sourcegraph/build-config'
const distributionPath = path.resolve(__dirname, '..', 'dist')
const PACKAGE_ROOT_PATH = path.resolve(WORKSPACES_PATH, 'backstage-frontend')
const DIST_PATH = path.resolve(PACKAGE_ROOT_PATH, 'dist')
;(async function build(): Promise<void> {
if (existsSync(distributionPath)) {
rm('-rf', distributionPath)
async function build(): Promise<void> {
if (existsSync(DIST_PATH)) {
rm('-rf', DIST_PATH)
}
await esbuild.build({
entryPoints: [path.resolve(__dirname, '..', 'src', 'index.ts')],
entryPoints: [path.resolve(PACKAGE_ROOT_PATH, 'src', 'index.ts')],
bundle: true,
external: [
'@backstage/cli',
@ -41,6 +42,13 @@ const distributionPath = path.resolve(__dirname, '..', 'dist')
ignoreAnnotations: true,
treeShaking: true,
sourcemap: true,
outdir: distributionPath,
outdir: DIST_PATH,
})
})()
}
if (require.main == module) {
build().catch(error => {
console.error('Error:', error)
process.exit(1)
})
}

View File

@ -0,0 +1,12 @@
const baseConfig = require('../../.eslintrc')
module.exports = {
extends: '../../.eslintrc.js',
parserOptions: {
...baseConfig.parserOptions,
project: [__dirname + '/tsconfig.json'],
},
rules: {
'no-console': 'off',
},
overrides: baseConfig.overrides,
}

3
client/backstage-frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
dist
dev/
!dev/index.tsx

View File

@ -0,0 +1,3 @@
dist/
dev/*
!dev/*.tsx

View File

@ -0,0 +1,17 @@
# Sourcegraph Backstage Frontend (WIP)
Welcome to the Sourcegraph Backstage Frontend plugin!
_This plugin was created through the Backstage CLI_
## NOTE
This plugin is still very much in development as is a work in progress. For any questions please ask in #job-fair-backstage or dm @William Bezuidenhout
## Getting started
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/sourcegraph](http://localhost:3000/sourcegraph).
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.

View File

@ -0,0 +1,8 @@
// @ts-check
/** @type {import('@babel/core').TransformOptions} */
const config = {
extends: '../../babel.config.js',
}
module.exports = config

View File

@ -0,0 +1,12 @@
import React from 'react'
import { createDevApp } from '@backstage/dev-utils'
import { sourcegraphPlugin, SourcegraphPage } from '../src/plugin'
createDevApp()
.registerPlugin(sourcegraphPlugin)
.addPage({
element: <SourcegraphPage />,
title: 'Root Page',
path: '/william',
})
.render()

View File

@ -0,0 +1,27 @@
{
"name": "@sourcegraph/backstage-frontend",
"description": "The Sourcegraph frontend plugin for Backstage",
"version": "0.1.0",
"main": "index.js",
"types": "types/src/index.d.ts",
"module": "index.js",
"license": "Apache-2.0",
"private": true,
"publishConfig": {
"access": "public"
},
"backstage": {
"role": "frontend-library"
},
"dependencies": {
"@backstage/core-components": "^0.12.3",
"@backstage/core-plugin-api": "^1.3.0",
"@backstage/theme": "^0.2.16",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-use": "^17.2.4"
}
}

View File

@ -0,0 +1,57 @@
{
"name": "@sourcegraph/backstage-frontend",
"version": "0.1.0",
"main": "dist/index.ts",
"types": "dist/types/index.d.ts",
"license": "Apache-2.0",
"private": true,
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "frontend-plugin"
},
"scripts": {
"dist": "ts-node --transpile-only ./scripts/esbuild.ts && pnpm types && cp package.dist.json dist/package.json",
"types": "tsc -d -p tsconfig.json",
"format": "prettier -w true .",
"test": "jest"
},
"dependencies": {
"@backstage/core-components": "^0.12.3",
"@backstage/core-plugin-api": "^1.3.0",
"@backstage/theme": "^0.2.16",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-use": "^17.2.4"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.22.1",
"@backstage/core-app-api": "^1.4.0",
"@backstage/dev-utils": "^1.0.11",
"@backstage/test-utils": "^1.2.4",
"@sourcegraph/build-config": "workspace:^0.0.1",
"@sourcegraph/tsconfig": "^4.0.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",
"@types/node": "*",
"babel-jest": "^28.1.3",
"babel-loader": "^9.1.0",
"cross-fetch": "^3.1.5",
"esbuild": "^0.17.8",
"esbuild-node-externals": "^1.6.0",
"msw": "^0.49.0"
},
"files": [
"dist"
]
}

View File

@ -0,0 +1,48 @@
import { existsSync } from 'fs'
import path from 'path'
import { nodeExternalsPlugin } from 'esbuild-node-externals'
import * as esbuild from 'esbuild'
import { rm } from 'shelljs'
import { stylePlugin, workerPlugin, buildTimerPlugin, WORKSPACES_PATH } from '@sourcegraph/build-config'
const PACKAGE_ROOT_PATH = path.resolve(WORKSPACES_PATH, 'backstage-frontend')
const DIST_PATH = path.resolve(PACKAGE_ROOT_PATH, 'dist')
async function build(): Promise<void> {
if (existsSync(DIST_PATH)) {
rm('-rf', DIST_PATH)
}
await esbuild.build({
entryPoints: [path.resolve(PACKAGE_ROOT_PATH, 'src', 'index.ts')],
bundle: true,
format: 'esm',
logLevel: 'error',
jsx: 'automatic',
external: ['@backstage/*', '@material-ui/*', 'react-use', 'react', 'react-dom'],
plugins: [stylePlugin, workerPlugin, buildTimerPlugin, nodeExternalsPlugin()],
// mainFields: ['browser', 'module', 'main'],
// platform: 'browser',
define: {
'process.env.IS_TEST': 'false',
global: 'window',
},
loader: {
'.yaml': 'text',
'.ttf': 'file',
'.png': 'file',
},
treeShaking: true,
target: 'esnext',
sourcemap: true,
outdir: DIST_PATH,
})
}
if (require.main == module) {
build()
.catch(error => console.error('Error:', error))
.finally(() => process.exit(0))
}

View File

@ -0,0 +1,22 @@
import React from 'react'
import { ExampleComponent } from './ExampleComponent'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
import { screen } from '@testing-library/react'
import { setupRequestMockHandlers, renderInTestApp } from '@backstage/test-utils'
describe('ExampleComponent', () => {
const server = setupServer()
// Enable sane handlers for network requests
setupRequestMockHandlers(server)
// setup mock response
beforeEach(() => {
server.use(rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))))
})
it('should render', async () => {
await renderInTestApp(<ExampleComponent />)
expect(screen.getByText('Welcome to Sourcgraph!')).toBeInTheDocument()
})
})

View File

@ -0,0 +1,28 @@
import React from 'react'
import { Typography, Grid } from '@material-ui/core'
import { InfoCard, Header, Page, Content, ContentHeader, HeaderLabel, SupportButton } from '@backstage/core-components'
import { ExampleFetchComponent } from '../ExampleFetchComponent'
export const ExampleComponent = () => (
<Page themeId="tool">
<Header title="Welcome to Sourcegraph!" subtitle="Optional subtitle">
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content>
<ContentHeader title="Plugin title">
<SupportButton>A description of your plugin goes here.</SupportButton>
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Information card">
<Typography variant="body1">All content should be wrapped in a card like this.</Typography>
</InfoCard>
</Grid>
<Grid item>
<ExampleFetchComponent />
</Grid>
</Grid>
</Content>
</Page>
)

View File

@ -0,0 +1 @@
export { ExampleComponent } from './ExampleComponent'

View File

@ -0,0 +1,23 @@
import React from 'react'
import { render, screen } from '@testing-library/react'
import { ExampleFetchComponent } from './ExampleFetchComponent'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
import { setupRequestMockHandlers } from '@backstage/test-utils'
describe('ExampleFetchComponent', () => {
const server = setupServer()
// Enable sane handlers for network requests
setupRequestMockHandlers(server)
// setup mock response
beforeEach(() => {
server.use(
rest.get('https://randomuser.me/*', (_, res, ctx) => res(ctx.status(200), ctx.delay(2000), ctx.json({})))
)
})
it('should render', async () => {
await render(<ExampleFetchComponent />)
expect(await screen.findByTestId('progress')).toBeInTheDocument()
})
})

View File

@ -0,0 +1,84 @@
import React from 'react'
import { makeStyles } from '@material-ui/core/styles'
import { Table, TableColumn, Progress } from '@backstage/core-components'
import Alert from '@material-ui/lab/Alert'
import useAsync from 'react-use/lib/useAsync'
const useStyles = makeStyles({
avatar: {
height: 32,
width: 32,
borderRadius: '50%',
},
})
type User = {
gender: string // "male"
name: {
title: string // "Mr",
first: string // "Duane",
last: string // "Reed"
}
location: object // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…}
email: string // "duane.reed@example.com"
login: object // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…}
dob: object // {date: "1983-06-22T12:30:23.016Z", age: 37}
registered: object // {date: "2006-06-13T18:48:28.037Z", age: 14}
phone: string // "07-2154-5651"
cell: string // "0405-592-879"
id: {
name: string // "TFN",
value: string // "796260432"
}
picture: { medium: string } // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…}
nat: string // "AU"
}
type DenseTableProps = {
users: User[]
}
export const DenseTable = ({ users }: DenseTableProps) => {
const classes = useStyles()
const columns: TableColumn[] = [
{ title: 'Avatar', field: 'avatar' },
{ title: 'Name', field: 'name' },
{ title: 'Email', field: 'email' },
{ title: 'Nationality', field: 'nationality' },
]
const data = users.map(user => {
return {
avatar: <img src={user.picture.medium} className={classes.avatar} alt={user.name.first} />,
name: `${user.name.first} ${user.name.last}`,
email: user.email,
nationality: user.nat,
}
})
return (
<Table
title="Example User List (fetching data from randomuser.me)"
options={{ search: false, paging: false }}
columns={columns}
data={data}
/>
)
}
export const ExampleFetchComponent = () => {
const { value, loading, error } = useAsync(async (): Promise<User[]> => {
const response = await fetch('https://randomuser.me/api/?results=20')
const data = await response.json()
return data.results
}, [])
if (loading) {
return <Progress />
} else if (error) {
return <Alert severity="error">{error.message}</Alert>
}
return <DenseTable users={value || []} />
}

View File

@ -0,0 +1 @@
export { ExampleFetchComponent } from './ExampleFetchComponent'

View File

@ -0,0 +1 @@
export { sourcegraphPlugin, SourcegraphPage } from './plugin'

View File

@ -0,0 +1,7 @@
import { sourcegraphPlugin } from './plugin'
describe('sourcegraph', () => {
it('should export plugin', () => {
expect(sourcegraphPlugin).toBeDefined()
})
})

View File

@ -0,0 +1,18 @@
import { createPlugin, createRoutableExtension } from '@backstage/core-plugin-api'
import { rootRouteRef } from './routes'
export const sourcegraphPlugin = createPlugin({
id: 'sourcegraph',
routes: {
root: rootRouteRef,
},
})
export const SourcegraphPage = sourcegraphPlugin.provide(
createRoutableExtension({
name: 'SourcegraphPage',
component: () => import('./components/ExampleComponent').then(m => m.ExampleComponent),
mountPoint: rootRouteRef,
})
)

View File

@ -0,0 +1,5 @@
import { createRouteRef } from '@backstage/core-plugin-api'
export const rootRouteRef = createRouteRef({
id: 'sourcegraph',
})

View File

@ -0,0 +1,2 @@
import '@testing-library/jest-dom'
import 'cross-fetch/polyfill'

View File

@ -0,0 +1,31 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"target": "es2020",
"sourceMap": true,
"sourceRoot": "src",
"baseUrl": ".",
"paths": {
"@sourcegraph/*": ["../*"],
"*": ["types/*", "../../shared/src/types/*", "../../common/src/types/*", "*"],
},
"resolveJsonModule": true,
"jsx": "react",
"declaration": true,
"outDir": "dist/types",
"rootDir": ".",
},
"references": [
{
"path": "../build-config",
},
],
"include": ["**/*", ".*", "./src/**/*.json"],
"exclude": ["node_modules", "../../node_modules", "dist", "**/*.test.ts", "scripts"],
"ts-node": {
"moduleTypes": {
"scripts/**/*": "cjs",
},
},
}

File diff suppressed because it is too large Load Diff