mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 13:11:49 +00:00
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:
parent
78c5353b75
commit
1969f3e6b6
2
client/backstage-backend/.prettierignore
Normal file
2
client/backstage-backend/.prettierignore
Normal file
@ -0,0 +1,2 @@
|
||||
dist
|
||||
node_modules
|
||||
@ -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",
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
12
client/backstage-frontend/.eslintrc.js
Normal file
12
client/backstage-frontend/.eslintrc.js
Normal 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
3
client/backstage-frontend/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
dist
|
||||
dev/
|
||||
!dev/index.tsx
|
||||
3
client/backstage-frontend/.prettierignore
Normal file
3
client/backstage-frontend/.prettierignore
Normal file
@ -0,0 +1,3 @@
|
||||
dist/
|
||||
dev/*
|
||||
!dev/*.tsx
|
||||
17
client/backstage-frontend/README.md
Normal file
17
client/backstage-frontend/README.md
Normal 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.
|
||||
8
client/backstage-frontend/babel.config.js
Normal file
8
client/backstage-frontend/babel.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('@babel/core').TransformOptions} */
|
||||
const config = {
|
||||
extends: '../../babel.config.js',
|
||||
}
|
||||
|
||||
module.exports = config
|
||||
12
client/backstage-frontend/dev/index.tsx
Normal file
12
client/backstage-frontend/dev/index.tsx
Normal 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()
|
||||
27
client/backstage-frontend/package.dist.json
Normal file
27
client/backstage-frontend/package.dist.json
Normal 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"
|
||||
}
|
||||
}
|
||||
57
client/backstage-frontend/package.json
Normal file
57
client/backstage-frontend/package.json
Normal 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"
|
||||
]
|
||||
}
|
||||
48
client/backstage-frontend/scripts/esbuild.ts
Normal file
48
client/backstage-frontend/scripts/esbuild.ts
Normal 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))
|
||||
}
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
@ -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>
|
||||
)
|
||||
@ -0,0 +1 @@
|
||||
export { ExampleComponent } from './ExampleComponent'
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
@ -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 || []} />
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
export { ExampleFetchComponent } from './ExampleFetchComponent'
|
||||
1
client/backstage-frontend/src/index.ts
Normal file
1
client/backstage-frontend/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { sourcegraphPlugin, SourcegraphPage } from './plugin'
|
||||
7
client/backstage-frontend/src/plugin.test.ts
Normal file
7
client/backstage-frontend/src/plugin.test.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { sourcegraphPlugin } from './plugin'
|
||||
|
||||
describe('sourcegraph', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(sourcegraphPlugin).toBeDefined()
|
||||
})
|
||||
})
|
||||
18
client/backstage-frontend/src/plugin.ts
Normal file
18
client/backstage-frontend/src/plugin.ts
Normal 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,
|
||||
})
|
||||
)
|
||||
5
client/backstage-frontend/src/routes.ts
Normal file
5
client/backstage-frontend/src/routes.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { createRouteRef } from '@backstage/core-plugin-api'
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
id: 'sourcegraph',
|
||||
})
|
||||
2
client/backstage-frontend/src/setupTests.ts
Normal file
2
client/backstage-frontend/src/setupTests.ts
Normal file
@ -0,0 +1,2 @@
|
||||
import '@testing-library/jest-dom'
|
||||
import 'cross-fetch/polyfill'
|
||||
31
client/backstage-frontend/tsconfig.json
Normal file
31
client/backstage-frontend/tsconfig.json
Normal 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",
|
||||
},
|
||||
},
|
||||
}
|
||||
2078
pnpm-lock.yaml
2078
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user