@backstage/cli 0.33.1 → 0.34.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/config/nodeTransformHooks.mjs +4 -4
  3. package/dist/lib/version.cjs.js +20 -14
  4. package/dist/packages/backend-defaults/package.json.cjs.js +1 -1
  5. package/dist/packages/backend-plugin-api/package.json.cjs.js +1 -1
  6. package/dist/packages/backend-test-utils/package.json.cjs.js +1 -1
  7. package/dist/packages/catalog-client/package.json.cjs.js +1 -1
  8. package/dist/packages/cli/package.json.cjs.js +1 -1
  9. package/dist/packages/core-components/package.json.cjs.js +1 -1
  10. package/dist/packages/dev-utils/package.json.cjs.js +1 -1
  11. package/dist/packages/frontend-defaults/package.json.cjs.js +6 -0
  12. package/dist/packages/frontend-plugin-api/package.json.cjs.js +6 -0
  13. package/dist/packages/frontend-test-utils/package.json.cjs.js +6 -0
  14. package/dist/packages/test-utils/package.json.cjs.js +1 -1
  15. package/dist/packages/theme/package.json.cjs.js +1 -1
  16. package/dist/plugins/auth-backend/package.json.cjs.js +1 -1
  17. package/dist/plugins/auth-backend-module-guest-provider/package.json.cjs.js +1 -1
  18. package/dist/plugins/catalog-node/package.json.cjs.js +1 -1
  19. package/dist/plugins/scaffolder-node/package.json.cjs.js +1 -1
  20. package/dist/plugins/scaffolder-node-test-utils/package.json.cjs.js +1 -1
  21. package/package.json +27 -27
  22. package/templates/new-frontend-plugin/.eslintrc.js.hbs +1 -0
  23. package/templates/new-frontend-plugin/README.md.hbs +20 -0
  24. package/templates/new-frontend-plugin/dev/index.tsx +10 -0
  25. package/templates/new-frontend-plugin/package.json.hbs +49 -0
  26. package/templates/new-frontend-plugin/portable-template.yaml +5 -0
  27. package/templates/new-frontend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs +28 -0
  28. package/templates/new-frontend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs +37 -0
  29. package/templates/new-frontend-plugin/src/components/ExampleComponent/index.ts +1 -0
  30. package/templates/new-frontend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs +19 -0
  31. package/templates/new-frontend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs +308 -0
  32. package/templates/new-frontend-plugin/src/components/ExampleFetchComponent/index.ts +1 -0
  33. package/templates/new-frontend-plugin/src/index.ts.hbs +1 -0
  34. package/templates/new-frontend-plugin/src/plugin.test.ts.hbs +7 -0
  35. package/templates/new-frontend-plugin/src/plugin.tsx.hbs +26 -0
  36. package/templates/new-frontend-plugin/src/routes.ts +3 -0
  37. package/templates/new-frontend-plugin/src/setupTests.ts +1 -0
  38. package/templates/new-frontend-plugin-module/.eslintrc.js.hbs +1 -0
  39. package/templates/new-frontend-plugin-module/README.md.hbs +5 -0
  40. package/templates/new-frontend-plugin-module/package.json.hbs +35 -0
  41. package/templates/new-frontend-plugin-module/portable-template.yaml +5 -0
  42. package/templates/new-frontend-plugin-module/src/index.ts.hbs +1 -0
  43. package/templates/new-frontend-plugin-module/src/module.tsx.hbs +8 -0
  44. package/templates/new-frontend-plugin-module/src/setupTests.ts +1 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @backstage/cli
2
2
 
3
+ ## 0.34.0-next.1
4
+
5
+ ### Minor Changes
6
+
7
+ - 38b4243: Added plugin and module templates for the new frontend system. These templates are not included by default, but can be included by adding `@backstage/cli/templates/new-frontend-plugin` and `@backstage/cli/templates/new-frontend-plugin-module` as [custom templates](https://backstage.io/docs/tooling/cli/templates#installing-custom-templates).
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @backstage/catalog-model@1.7.5
13
+ - @backstage/cli-common@0.1.15
14
+ - @backstage/cli-node@0.2.13
15
+ - @backstage/config@1.3.3
16
+ - @backstage/config-loader@1.10.2
17
+ - @backstage/errors@1.2.7
18
+ - @backstage/eslint-plugin@0.1.11
19
+ - @backstage/integration@1.17.1
20
+ - @backstage/release-manifests@0.0.13
21
+ - @backstage/types@1.2.1
22
+
23
+ ## 0.33.2-next.0
24
+
25
+ ### Patch Changes
26
+
27
+ - a6af768: Allow js files to be processed by the nodeTransform loader
28
+ - Updated dependencies
29
+ - @backstage/cli-node@0.2.13
30
+ - @backstage/config-loader@1.10.2
31
+ - @backstage/catalog-model@1.7.5
32
+ - @backstage/cli-common@0.1.15
33
+ - @backstage/config@1.3.3
34
+ - @backstage/errors@1.2.7
35
+ - @backstage/eslint-plugin@0.1.11
36
+ - @backstage/integration@1.17.1
37
+ - @backstage/release-manifests@0.0.13
38
+ - @backstage/types@1.2.1
39
+
3
40
  ## 0.33.1
4
41
 
5
42
  ### Patch Changes
@@ -127,10 +127,10 @@ async function withDetectedModuleType(resolved) {
127
127
  };
128
128
  }
129
129
 
130
- // TODO(Rugvip): Afaik this should never happen and we can remove this check, but want it here for a little while to verify.
131
- if (ext === '.js') {
132
- throw new Error('Unexpected .js file without explicit format');
133
- }
130
+ // Under normal circumstances .js files should reliably have a format and so
131
+ // we should only reach this point for .ts files. However, if additional
132
+ // custom loaders are being used the format may not be detected for .js files
133
+ // either. As such we don't restrict the file format at this point.
134
134
 
135
135
  // TODO(Rugvip): Does this need caching? kept it simple for now but worth exploring
136
136
  const packageJsonPath = await findPackageJSON(fileURLToPath(resolved.url));
@@ -13,13 +13,16 @@ var _package$7 = require('../packages/core-components/package.json.cjs.js');
13
13
  var _package$8 = require('../packages/core-plugin-api/package.json.cjs.js');
14
14
  var _package$9 = require('../packages/dev-utils/package.json.cjs.js');
15
15
  var _package$a = require('../packages/errors/package.json.cjs.js');
16
- var _package$b = require('../packages/test-utils/package.json.cjs.js');
17
- var _package$d = require('../plugins/scaffolder-node/package.json.cjs.js');
18
- var _package$e = require('../plugins/scaffolder-node-test-utils/package.json.cjs.js');
19
- var _package$f = require('../plugins/auth-backend/package.json.cjs.js');
20
- var _package$g = require('../plugins/auth-backend-module-guest-provider/package.json.cjs.js');
21
- var _package$h = require('../plugins/catalog-node/package.json.cjs.js');
22
- var _package$c = require('../packages/theme/package.json.cjs.js');
16
+ var _package$b = require('../packages/frontend-defaults/package.json.cjs.js');
17
+ var _package$c = require('../packages/frontend-plugin-api/package.json.cjs.js');
18
+ var _package$d = require('../packages/frontend-test-utils/package.json.cjs.js');
19
+ var _package$e = require('../packages/test-utils/package.json.cjs.js');
20
+ var _package$g = require('../plugins/scaffolder-node/package.json.cjs.js');
21
+ var _package$h = require('../plugins/scaffolder-node-test-utils/package.json.cjs.js');
22
+ var _package$i = require('../plugins/auth-backend/package.json.cjs.js');
23
+ var _package$j = require('../plugins/auth-backend-module-guest-provider/package.json.cjs.js');
24
+ var _package$k = require('../plugins/catalog-node/package.json.cjs.js');
25
+ var _package$f = require('../packages/theme/package.json.cjs.js');
23
26
  var _package = require('../packages/backend-defaults/package.json.cjs.js');
24
27
 
25
28
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
@@ -39,13 +42,16 @@ const packageVersions = {
39
42
  "@backstage/core-plugin-api": _package$8.version,
40
43
  "@backstage/dev-utils": _package$9.version,
41
44
  "@backstage/errors": _package$a.version,
42
- "@backstage/test-utils": _package$b.version,
43
- "@backstage/theme": _package$c.version,
44
- "@backstage/plugin-scaffolder-node": _package$d.version,
45
- "@backstage/plugin-scaffolder-node-test-utils": _package$e.version,
46
- "@backstage/plugin-auth-backend": _package$f.version,
47
- "@backstage/plugin-auth-backend-module-guest-provider": _package$g.version,
48
- "@backstage/plugin-catalog-node": _package$h.version
45
+ "@backstage/frontend-defaults": _package$b.version,
46
+ "@backstage/frontend-plugin-api": _package$c.version,
47
+ "@backstage/frontend-test-utils": _package$d.version,
48
+ "@backstage/test-utils": _package$e.version,
49
+ "@backstage/theme": _package$f.version,
50
+ "@backstage/plugin-scaffolder-node": _package$g.version,
51
+ "@backstage/plugin-scaffolder-node-test-utils": _package$h.version,
52
+ "@backstage/plugin-auth-backend": _package$i.version,
53
+ "@backstage/plugin-auth-backend-module-guest-provider": _package$j.version,
54
+ "@backstage/plugin-catalog-node": _package$k.version
49
55
  };
50
56
  function findVersion() {
51
57
  const pkgContent = fs__default.default.readFileSync(paths.paths.resolveOwn("package.json"), "utf8");
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.11.1";
3
+ var version = "0.11.2-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "1.4.1";
3
+ var version = "1.4.2-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "1.7.0";
3
+ var version = "1.7.1-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "1.10.2";
3
+ var version = "1.11.0-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.33.1";
3
+ var version = "0.34.0-next.1";
4
4
  var dependencies = {
5
5
  "@backstage/catalog-model": "workspace:^",
6
6
  "@backstage/cli-common": "workspace:^",
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.17.4";
3
+ var version = "0.17.5-next.1";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "1.1.12";
3
+ var version = "1.1.13-next.1";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ var version = "0.3.0-next.2";
4
+
5
+ exports.version = version;
6
+ //# sourceMappingURL=package.json.cjs.js.map
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ var version = "0.11.0-next.1";
4
+
5
+ exports.version = version;
6
+ //# sourceMappingURL=package.json.cjs.js.map
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ var version = "0.3.5-next.2";
4
+
5
+ exports.version = version;
6
+ //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "1.7.10";
3
+ var version = "1.7.11-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.6.7";
3
+ var version = "0.6.8-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.25.2";
3
+ var version = "0.25.3-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.2.10";
3
+ var version = "0.2.11-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "1.17.2";
3
+ var version = "1.18.0-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.10.0";
3
+ var version = "0.11.0-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.3.1";
3
+ var version = "0.3.2-next.0";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/cli",
3
- "version": "0.33.1",
3
+ "version": "0.34.0-next.1",
4
4
  "description": "CLI for developing Backstage plugins and apps",
5
5
  "backstage": {
6
6
  "role": "cli"
@@ -47,16 +47,16 @@
47
47
  ]
48
48
  },
49
49
  "dependencies": {
50
- "@backstage/catalog-model": "^1.7.5",
51
- "@backstage/cli-common": "^0.1.15",
52
- "@backstage/cli-node": "^0.2.13",
53
- "@backstage/config": "^1.3.3",
54
- "@backstage/config-loader": "^1.10.2",
55
- "@backstage/errors": "^1.2.7",
56
- "@backstage/eslint-plugin": "^0.1.11",
57
- "@backstage/integration": "^1.17.1",
58
- "@backstage/release-manifests": "^0.0.13",
59
- "@backstage/types": "^1.2.1",
50
+ "@backstage/catalog-model": "1.7.5",
51
+ "@backstage/cli-common": "0.1.15",
52
+ "@backstage/cli-node": "0.2.13",
53
+ "@backstage/config": "1.3.3",
54
+ "@backstage/config-loader": "1.10.2",
55
+ "@backstage/errors": "1.2.7",
56
+ "@backstage/eslint-plugin": "0.1.11",
57
+ "@backstage/integration": "1.17.1",
58
+ "@backstage/release-manifests": "0.0.13",
59
+ "@backstage/types": "1.2.1",
60
60
  "@manypkg/get-packages": "^1.1.3",
61
61
  "@module-federation/enhanced": "^0.9.0",
62
62
  "@octokit/graphql": "^5.0.0",
@@ -163,22 +163,22 @@
163
163
  "zod-validation-error": "^3.4.0"
164
164
  },
165
165
  "devDependencies": {
166
- "@backstage/backend-plugin-api": "^1.4.1",
167
- "@backstage/backend-test-utils": "^1.7.0",
168
- "@backstage/catalog-client": "^1.10.2",
169
- "@backstage/config": "^1.3.3",
170
- "@backstage/core-app-api": "^1.18.0",
171
- "@backstage/core-components": "^0.17.4",
172
- "@backstage/core-plugin-api": "^1.10.9",
173
- "@backstage/dev-utils": "^1.1.12",
174
- "@backstage/errors": "^1.2.7",
175
- "@backstage/plugin-auth-backend": "^0.25.2",
176
- "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.10",
177
- "@backstage/plugin-catalog-node": "^1.17.2",
178
- "@backstage/plugin-scaffolder-node": "^0.10.0",
179
- "@backstage/plugin-scaffolder-node-test-utils": "^0.3.1",
180
- "@backstage/test-utils": "^1.7.10",
181
- "@backstage/theme": "^0.6.7",
166
+ "@backstage/backend-plugin-api": "1.4.2-next.0",
167
+ "@backstage/backend-test-utils": "1.7.1-next.0",
168
+ "@backstage/catalog-client": "1.11.0-next.0",
169
+ "@backstage/config": "1.3.3",
170
+ "@backstage/core-app-api": "1.18.0",
171
+ "@backstage/core-components": "0.17.5-next.1",
172
+ "@backstage/core-plugin-api": "1.10.9",
173
+ "@backstage/dev-utils": "1.1.13-next.1",
174
+ "@backstage/errors": "1.2.7",
175
+ "@backstage/plugin-auth-backend": "0.25.3-next.0",
176
+ "@backstage/plugin-auth-backend-module-guest-provider": "0.2.11-next.0",
177
+ "@backstage/plugin-catalog-node": "1.18.0-next.0",
178
+ "@backstage/plugin-scaffolder-node": "0.11.0-next.0",
179
+ "@backstage/plugin-scaffolder-node-test-utils": "0.3.2-next.0",
180
+ "@backstage/test-utils": "1.7.11-next.0",
181
+ "@backstage/theme": "0.6.8-next.0",
182
182
  "@rspack/core": "^1.3.9",
183
183
  "@rspack/dev-server": "^1.1.1",
184
184
  "@rspack/plugin-react-refresh": "^1.4.2",
@@ -0,0 +1 @@
1
+ module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
@@ -0,0 +1,20 @@
1
+ # {{pluginId}}
2
+
3
+ Welcome to the {{pluginId}} plugin!
4
+
5
+ _This plugin was created through the Backstage CLI_
6
+
7
+ ## Getting started
8
+
9
+ Your plugin has been added to the app in this repository, meaning you'll be able
10
+ to access it by running `yarn start` in the root directory, and then navigating
11
+ to [/{{pluginId}}](http://localhost:3000/{{pluginId}}).
12
+
13
+ This plugin is built with Backstage's [new frontend
14
+ system](https://backstage.io/docs/frontend-system/architecture/index), and you
15
+ can find more information about building plugins in the [plugin builder
16
+ documentation](https://backstage.io/docs/frontend-system/building-plugins/index).
17
+
18
+ You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
19
+ This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
20
+ It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
@@ -0,0 +1,10 @@
1
+ import { createApp } from '@backstage/frontend-defaults';
2
+ import ReactDOM from 'react-dom';
3
+
4
+ import plugin from '../src';
5
+
6
+ const app = createApp({
7
+ features: [plugin],
8
+ });
9
+
10
+ ReactDOM.render(app.createRoot(), document.getElementById('root'));
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "main": "src/index.ts",
4
+ "types": "src/index.ts",
5
+ "publishConfig": {
6
+ "access": "public",
7
+ "main": "dist/index.esm.js",
8
+ "types": "dist/index.d.ts"
9
+ },
10
+ "backstage": {
11
+ "role": "frontend-plugin",
12
+ "pluginId": "{{pluginId}}"
13
+ },
14
+ "sideEffects": false,
15
+ "scripts": {
16
+ "start": "backstage-cli package start",
17
+ "build": "backstage-cli package build",
18
+ "lint": "backstage-cli package lint",
19
+ "test": "backstage-cli package test",
20
+ "clean": "backstage-cli package clean",
21
+ "prepack": "backstage-cli package prepack",
22
+ "postpack": "backstage-cli package postpack"
23
+ },
24
+ "dependencies": {
25
+ "@backstage/core-components": "{{versionQuery '@backstage/core-components'}}",
26
+ "@backstage/frontend-plugin-api": "{{versionQuery '@backstage/frontend-plugin-api'}}",
27
+ "@backstage/theme": "{{versionQuery '@backstage/theme'}}",
28
+ "@material-ui/core": "{{versionQuery '@material-ui/core' '4.12.2'}}",
29
+ "@material-ui/icons": "{{versionQuery '@material-ui/icons' '4.9.1'}}",
30
+ "@material-ui/lab": "{{versionQuery '@material-ui/lab' '4.0.0-alpha.61'}}",
31
+ "react-use": "{{versionQuery 'react-use' '17.2.4'}}"
32
+ },
33
+ "peerDependencies": {
34
+ "react": "{{versionQuery 'react' '^16.13.1 || ^17.0.0 || ^18.0.0'}}"
35
+ },
36
+ "devDependencies": {
37
+ "@backstage/cli": "{{versionQuery '@backstage/cli'}}",
38
+ "@backstage/frontend-defaults": "{{versionQuery '@backstage/frontend-defaults'}}",
39
+ "@backstage/frontend-test-utils": "{{versionQuery '@backstage/frontend-test-utils'}}",
40
+ "@testing-library/jest-dom": "{{versionQuery '@testing-library/jest-dom' '6.0.0'}}",
41
+ "@testing-library/react": "{{versionQuery '@testing-library/react' '14.0.0'}}",
42
+ "@testing-library/user-event": "{{versionQuery '@testing-library/user-event' '14.0.0'}}",
43
+ "msw": "{{versionQuery 'msw' '1.0.0'}}",
44
+ "react": "{{versionQuery 'react' '^16.13.1 || ^17.0.0 || ^18.0.0'}}"
45
+ },
46
+ "files": [
47
+ "dist"
48
+ ]
49
+ }
@@ -0,0 +1,5 @@
1
+ name: frontend-plugin
2
+ role: frontend-plugin
3
+ description: A new frontend plugin
4
+ values:
5
+ pluginVar: '{{ camelCase pluginId }}Plugin'
@@ -0,0 +1,28 @@
1
+ import { ExampleComponent } from './ExampleComponent';
2
+ import { rest } from 'msw';
3
+ import { setupServer } from 'msw/node';
4
+ import { screen } from '@testing-library/react';
5
+ import {
6
+ registerMswTestHooks,
7
+ renderInTestApp,
8
+ } from '@backstage/frontend-test-utils';
9
+
10
+ describe('ExampleComponent', () => {
11
+ const server = setupServer();
12
+ // Enable sane handlers for network requests
13
+ registerMswTestHooks(server);
14
+
15
+ // setup mock response
16
+ beforeEach(() => {
17
+ server.use(
18
+ rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))),
19
+ );
20
+ });
21
+
22
+ it('should render', async () => {
23
+ await renderInTestApp(<ExampleComponent />);
24
+ expect(
25
+ screen.getByText('Welcome to {{pluginId}}!'),
26
+ ).toBeInTheDocument();
27
+ });
28
+ });
@@ -0,0 +1,37 @@
1
+ import { Typography, Grid } from '@material-ui/core';
2
+ import {
3
+ InfoCard,
4
+ Header,
5
+ Page,
6
+ Content,
7
+ ContentHeader,
8
+ HeaderLabel,
9
+ SupportButton,
10
+ } from '@backstage/core-components';
11
+ import { ExampleFetchComponent } from '../ExampleFetchComponent';
12
+
13
+ export const ExampleComponent = () => (
14
+ <Page themeId="tool">
15
+ <Header title="Welcome to {{pluginId}}!" subtitle="Optional subtitle">
16
+ <HeaderLabel label="Owner" value="Team X" />
17
+ <HeaderLabel label="Lifecycle" value="Alpha" />
18
+ </Header>
19
+ <Content>
20
+ <ContentHeader title="Plugin title">
21
+ <SupportButton>A description of your plugin goes here.</SupportButton>
22
+ </ContentHeader>
23
+ <Grid container spacing={3} direction="column">
24
+ <Grid item>
25
+ <InfoCard title="Information card">
26
+ <Typography variant="body1">
27
+ All content should be wrapped in a card like this.
28
+ </Typography>
29
+ </InfoCard>
30
+ </Grid>
31
+ <Grid item>
32
+ <ExampleFetchComponent />
33
+ </Grid>
34
+ </Grid>
35
+ </Content>
36
+ </Page>
37
+ );
@@ -0,0 +1 @@
1
+ export { ExampleComponent } from './ExampleComponent';
@@ -0,0 +1,19 @@
1
+ import { renderInTestApp } from '@backstage/frontend-test-utils';
2
+ import { ExampleFetchComponent } from './ExampleFetchComponent';
3
+
4
+ describe('ExampleFetchComponent', () => {
5
+ it('renders the user table', async () => {
6
+ const { getAllByText, getByAltText, getByText, findByRole } =
7
+ await renderInTestApp(<ExampleFetchComponent />);
8
+
9
+ // Wait for the table to render
10
+ const table = await findByRole('table');
11
+ const nationality = getAllByText('GB');
12
+ // Assert that the table contains the expected user data
13
+ expect(table).toBeInTheDocument();
14
+ expect(getByAltText('Carolyn')).toBeInTheDocument();
15
+ expect(getByText('Carolyn Moore')).toBeInTheDocument();
16
+ expect(getByText('carolyn.moore@example.com')).toBeInTheDocument();
17
+ expect(nationality[0]).toBeInTheDocument();
18
+ });
19
+ });
@@ -0,0 +1,308 @@
1
+ import { makeStyles } from '@material-ui/core/styles';
2
+ import {
3
+ Table,
4
+ TableColumn,
5
+ Progress,
6
+ ResponseErrorPanel,
7
+ } from '@backstage/core-components';
8
+ import useAsync from 'react-use/lib/useAsync';
9
+
10
+ export const exampleUsers = {
11
+ results: [
12
+ {
13
+ gender: 'female',
14
+ name: {
15
+ title: 'Miss',
16
+ first: 'Carolyn',
17
+ last: 'Moore',
18
+ },
19
+ email: 'carolyn.moore@example.com',
20
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Carolyn',
21
+ nat: 'GB',
22
+ },
23
+ {
24
+ gender: 'female',
25
+ name: {
26
+ title: 'Ms',
27
+ first: 'Esma',
28
+ last: 'Berberoğlu',
29
+ },
30
+ email: 'esma.berberoglu@example.com',
31
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Esma',
32
+ nat: 'TR',
33
+ },
34
+ {
35
+ gender: 'female',
36
+ name: {
37
+ title: 'Ms',
38
+ first: 'Isabella',
39
+ last: 'Rhodes',
40
+ },
41
+ email: 'isabella.rhodes@example.com',
42
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella',
43
+ nat: 'GB',
44
+ },
45
+ {
46
+ gender: 'male',
47
+ name: {
48
+ title: 'Mr',
49
+ first: 'Derrick',
50
+ last: 'Carter',
51
+ },
52
+ email: 'derrick.carter@example.com',
53
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Derrick',
54
+ nat: 'IE',
55
+ },
56
+ {
57
+ gender: 'female',
58
+ name: {
59
+ title: 'Miss',
60
+ first: 'Mattie',
61
+ last: 'Lambert',
62
+ },
63
+ email: 'mattie.lambert@example.com',
64
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mattie',
65
+ nat: 'AU',
66
+ },
67
+ {
68
+ gender: 'male',
69
+ name: {
70
+ title: 'Mr',
71
+ first: 'Mijat',
72
+ last: 'Rakić',
73
+ },
74
+ email: 'mijat.rakic@example.com',
75
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mijat',
76
+ nat: 'RS',
77
+ },
78
+ {
79
+ gender: 'male',
80
+ name: {
81
+ title: 'Mr',
82
+ first: 'Javier',
83
+ last: 'Reid',
84
+ },
85
+ email: 'javier.reid@example.com',
86
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Javier',
87
+ nat: 'US',
88
+ },
89
+ {
90
+ gender: 'female',
91
+ name: {
92
+ title: 'Ms',
93
+ first: 'Isabella',
94
+ last: 'Li',
95
+ },
96
+ email: 'isabella.li@example.com',
97
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella',
98
+ nat: 'CA',
99
+ },
100
+ {
101
+ gender: 'female',
102
+ name: {
103
+ title: 'Mrs',
104
+ first: 'Stephanie',
105
+ last: 'Garrett',
106
+ },
107
+ email: 'stephanie.garrett@example.com',
108
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Stephanie',
109
+ nat: 'AU',
110
+ },
111
+ {
112
+ gender: 'female',
113
+ name: {
114
+ title: 'Ms',
115
+ first: 'Antonia',
116
+ last: 'Núñez',
117
+ },
118
+ email: 'antonia.nunez@example.com',
119
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Antonia',
120
+ nat: 'ES',
121
+ },
122
+ {
123
+ gender: 'male',
124
+ name: {
125
+ title: 'Mr',
126
+ first: 'Donald',
127
+ last: 'Young',
128
+ },
129
+ email: 'donald.young@example.com',
130
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Donald',
131
+ nat: 'US',
132
+ },
133
+ {
134
+ gender: 'male',
135
+ name: {
136
+ title: 'Mr',
137
+ first: 'Iegor',
138
+ last: 'Holodovskiy',
139
+ },
140
+ email: 'iegor.holodovskiy@example.com',
141
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Iegor',
142
+ nat: 'UA',
143
+ },
144
+ {
145
+ gender: 'female',
146
+ name: {
147
+ title: 'Madame',
148
+ first: 'Jessica',
149
+ last: 'David',
150
+ },
151
+ email: 'jessica.david@example.com',
152
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jessica',
153
+ nat: 'CH',
154
+ },
155
+ {
156
+ gender: 'female',
157
+ name: {
158
+ title: 'Ms',
159
+ first: 'Eve',
160
+ last: 'Martinez',
161
+ },
162
+ email: 'eve.martinez@example.com',
163
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Eve',
164
+ nat: 'FR',
165
+ },
166
+ {
167
+ gender: 'male',
168
+ name: {
169
+ title: 'Mr',
170
+ first: 'Caleb',
171
+ last: 'Silva',
172
+ },
173
+ email: 'caleb.silva@example.com',
174
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Caleb',
175
+ nat: 'US',
176
+ },
177
+ {
178
+ gender: 'female',
179
+ name: {
180
+ title: 'Miss',
181
+ first: 'Marcia',
182
+ last: 'Jenkins',
183
+ },
184
+ email: 'marcia.jenkins@example.com',
185
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Marcia',
186
+ nat: 'US',
187
+ },
188
+ {
189
+ gender: 'female',
190
+ name: {
191
+ title: 'Mrs',
192
+ first: 'Mackenzie',
193
+ last: 'Jones',
194
+ },
195
+ email: 'mackenzie.jones@example.com',
196
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mackenzie',
197
+ nat: 'NZ',
198
+ },
199
+ {
200
+ gender: 'male',
201
+ name: {
202
+ title: 'Mr',
203
+ first: 'Jeremiah',
204
+ last: 'Gutierrez',
205
+ },
206
+ email: 'jeremiah.gutierrez@example.com',
207
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jeremiah',
208
+ nat: 'AU',
209
+ },
210
+ {
211
+ gender: 'female',
212
+ name: {
213
+ title: 'Ms',
214
+ first: 'Luciara',
215
+ last: 'Souza',
216
+ },
217
+ email: 'luciara.souza@example.com',
218
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Luciara',
219
+ nat: 'BR',
220
+ },
221
+ {
222
+ gender: 'male',
223
+ name: {
224
+ title: 'Mr',
225
+ first: 'Valgi',
226
+ last: 'da Cunha',
227
+ },
228
+ email: 'valgi.dacunha@example.com',
229
+ picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Valgi',
230
+ nat: 'BR',
231
+ },
232
+ ],
233
+ };
234
+
235
+ const useStyles = makeStyles({
236
+ avatar: {
237
+ height: 32,
238
+ width: 32,
239
+ borderRadius: '50%',
240
+ },
241
+ });
242
+
243
+ type User = {
244
+ gender: string; // "male"
245
+ name: {
246
+ title: string; // "Mr",
247
+ first: string; // "Duane",
248
+ last: string; // "Reed"
249
+ };
250
+ email: string; // "duane.reed@example.com"
251
+ picture: string; // "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane"
252
+ nat: string; // "AU"
253
+ };
254
+
255
+ type DenseTableProps = {
256
+ users: User[];
257
+ };
258
+
259
+ export const DenseTable = ({ users }: DenseTableProps) => {
260
+ const classes = useStyles();
261
+
262
+ const columns: TableColumn[] = [
263
+ { title: 'Avatar', field: 'avatar' },
264
+ { title: 'Name', field: 'name' },
265
+ { title: 'Email', field: 'email' },
266
+ { title: 'Nationality', field: 'nationality' },
267
+ ];
268
+
269
+ const data = users.map(user => {
270
+ return {
271
+ avatar: (
272
+ <img
273
+ src={user.picture}
274
+ className={classes.avatar}
275
+ alt={user.name.first}
276
+ />
277
+ ),
278
+ name: `${user.name.first} ${user.name.last}`,
279
+ email: user.email,
280
+ nationality: user.nat,
281
+ };
282
+ });
283
+
284
+ return (
285
+ <Table
286
+ title="Example User List"
287
+ options=\{{ search: false, paging: false }}
288
+ columns={columns}
289
+ data={data}
290
+ />
291
+ );
292
+ };
293
+
294
+ export const ExampleFetchComponent = () => {
295
+
296
+ const { value, loading, error } = useAsync(async (): Promise<User[]> => {
297
+ // Would use fetch in a real world example
298
+ return exampleUsers.results;
299
+ }, []);
300
+
301
+ if (loading) {
302
+ return <Progress />;
303
+ } else if (error) {
304
+ return <ResponseErrorPanel error={error} />;
305
+ }
306
+
307
+ return <DenseTable users={value || []} />;
308
+ };
@@ -0,0 +1 @@
1
+ export { ExampleFetchComponent } from './ExampleFetchComponent';
@@ -0,0 +1 @@
1
+ export { {{ pluginVar }} as default } from './plugin';
@@ -0,0 +1,7 @@
1
+ import { {{ pluginVar }} } from './plugin';
2
+
3
+ describe('{{pluginId}}', () => {
4
+ it('should export plugin', () => {
5
+ expect({{ pluginVar }}).toBeDefined();
6
+ });
7
+ });
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import {
3
+ createFrontendPlugin,
4
+ PageBlueprint,
5
+ } from '@backstage/frontend-plugin-api';
6
+
7
+ import { rootRouteRef } from './routes';
8
+
9
+ export const page = PageBlueprint.make({
10
+ params: {
11
+ path: '/{{pluginId}}',
12
+ routeRef: rootRouteRef,
13
+ loader: () =>
14
+ import('./components/ExampleComponent').then(m =>
15
+ <m.ExampleComponent />,
16
+ ),
17
+ },
18
+ });
19
+
20
+ export const {{ pluginVar }} = createFrontendPlugin({
21
+ id: '{{pluginId}}',
22
+ extensions: [page],
23
+ routes: {
24
+ root: rootRouteRef,
25
+ }
26
+ });
@@ -0,0 +1,3 @@
1
+ import { createRouteRef } from '@backstage/frontend-plugin-api';
2
+
3
+ export const rootRouteRef = createRouteRef();
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1 @@
1
+ module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
@@ -0,0 +1,5 @@
1
+ # {{packageName}}
2
+
3
+ The {{moduleId}} frontend module for the {{pluginId}} plugin.
4
+
5
+ _This plugin was created through the Backstage CLI_
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "{{packageName}}",
3
+ "description": "The {{moduleId}} frontend module for the {{pluginId}} plugin.",
4
+ "main": "src/index.ts",
5
+ "types": "src/index.ts",
6
+ "publishConfig": {
7
+ "access": "public",
8
+ "main": "dist/index.cjs.js",
9
+ "module": "dist/index.esm.js",
10
+ "types": "dist/index.d.ts"
11
+ },
12
+ "backstage": {
13
+ "role": "frontend-plugin-module",
14
+ "pluginId": "{{pluginId}}"
15
+ },
16
+ "sideEffects": false,
17
+ "scripts": {
18
+ "build": "backstage-cli package build",
19
+ "lint": "backstage-cli package lint",
20
+ "test": "backstage-cli package test",
21
+ "clean": "backstage-cli package clean",
22
+ "prepack": "backstage-cli package prepack",
23
+ "postpack": "backstage-cli package postpack"
24
+ },
25
+ "dependencies": {
26
+ "@backstage/frontend-plugin-api": "{{versionQuery '@backstage/frontend-plugin-api'}}"
27
+ },
28
+ "devDependencies": {
29
+ "@backstage/cli": "{{versionQuery '@backstage/cli'}}",
30
+ "@testing-library/jest-dom": "{{versionQuery '@testing-library/jest-dom' '6.0.0'}}"
31
+ },
32
+ "files": [
33
+ "dist"
34
+ ]
35
+ }
@@ -0,0 +1,5 @@
1
+ name: frontend-plugin-module
2
+ role: frontend-plugin-module
3
+ description: A new frontend module that extends an existing frontend plugin
4
+ values:
5
+ moduleVar: '{{ camelCase pluginId }}Module{{ upperFirst ( camelCase moduleId ) }}'
@@ -0,0 +1 @@
1
+ export { {{ moduleVar }} as default } from './plugin';
@@ -0,0 +1,8 @@
1
+ import { createFrontendModule } from '@backstage/frontend-plugin-api';
2
+
3
+ export const {{ moduleVar }} = createFrontendModule({
4
+ pluginId: '{{ pluginId }}',
5
+ extensions: [
6
+ /* TODO */
7
+ ],
8
+ });
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';