Merge pull request #5878 from backstage/rugvip/codemods
Introduce @backstage/codemods package, with an initial transform for migrating core-* imports
This commit is contained in:
@@ -38,6 +38,8 @@ codeblocks
|
||||
Codecov
|
||||
codehilite
|
||||
Codehilite
|
||||
codemod
|
||||
codemods
|
||||
codeowners
|
||||
composability
|
||||
composable
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
|
||||
rules: {
|
||||
'no-console': 0,
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: true,
|
||||
optionalDependencies: false,
|
||||
peerDependencies: false,
|
||||
bundledDependencies: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
# @backstage/codemods
|
||||
|
||||
A collection of codemods for use with Backstage projects. They are intended to improve and simplify large scale refactoring, upgrades of code to use new implementations, alignment to ADRs or project standards, or other refactoring.
|
||||
|
||||
## Usage
|
||||
|
||||
This package is a wrapper around [`jscodeshift`](https://github.com/facebook/jscodeshift) with some included transforms. The transforms can either be executed via the included CLI or directly via `jscodeshift`.
|
||||
|
||||
To run the `core-imports` codemod towards all source files in the current directory, run the following:
|
||||
|
||||
```sh
|
||||
npx @backstage/codemods apply core-imports
|
||||
```
|
||||
|
||||
Note that this will modify the source files directly, but it's possible to do a dry-run by adding the `--dry` flag.
|
||||
|
||||
By passing a list of paths the codemod will only be applied to those paths:
|
||||
|
||||
```sh
|
||||
npx @backstage/codemods apply core-imports plugins/my-plugin-a plugins/my-plugin-b
|
||||
```
|
||||
|
||||
To print a list of all available transforms you use the `list` command:
|
||||
|
||||
```sh
|
||||
npx @backstage/codemods list
|
||||
```
|
||||
|
||||
You can also apply the transforms manually using `jscodeshift`. The transforms are located within the `transforms/` directory in this package, so running directly with `jscodeshift` looks like this:
|
||||
|
||||
```sh
|
||||
npx jscodeshift --parser=tsx --extensions=tsx,js,ts,tsx --transform=node_modules/@backstage/codemods/transforms/core-imports.js .
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
|
||||
- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md)
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env node
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
require('ts-node').register({
|
||||
transpileOnly: true,
|
||||
/* eslint-disable-next-line no-restricted-syntax */
|
||||
project: path.resolve(__dirname, '../../../tsconfig.json'),
|
||||
compilerOptions: {
|
||||
module: 'CommonJS',
|
||||
},
|
||||
});
|
||||
|
||||
require('../src');
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@backstage/codemods",
|
||||
"description": "A collection of codemods for Backstage projects",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "packages/codemods"
|
||||
},
|
||||
"keywords": [
|
||||
"backstage"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"start": "nodemon --",
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test"
|
||||
},
|
||||
"bin": {
|
||||
"backstage-codemods": "bin/backstage-codemods"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli-common": "0.1.1",
|
||||
"@backstage/core-app-api": "*",
|
||||
"@backstage/core-components": "*",
|
||||
"@backstage/core-plugin-api": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"jscodeshift": "^0.12.0",
|
||||
"jscodeshift-add-imports": "^1.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jscodeshift": "^0.11.0",
|
||||
"@types/node": "^14.14.32",
|
||||
"commander": "^6.1.0",
|
||||
"ts-node": "^9.1.1"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"watch": "./src",
|
||||
"exec": "bin/backstage-codemods",
|
||||
"ext": "ts"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"transforms"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { relative as relativePath } from 'path';
|
||||
import { spawn } from 'child_process';
|
||||
import { Command } from 'commander';
|
||||
import { findPaths } from '@backstage/cli-common';
|
||||
import { ExitCodeError } from './errors';
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const paths = findPaths(__dirname);
|
||||
|
||||
export function createCodemodAction(name: string) {
|
||||
return async (_: unknown, cmd: Command) => {
|
||||
const transformPath = relativePath(
|
||||
process.cwd(),
|
||||
paths.resolveOwn('transforms', `${name}.js`),
|
||||
);
|
||||
|
||||
const args = [
|
||||
'--parser=tsx',
|
||||
'--extensions=tsx,js,ts,tsx',
|
||||
'--transform',
|
||||
transformPath,
|
||||
'--ignore-pattern=**/node_modules/**',
|
||||
];
|
||||
|
||||
if (cmd.dry) {
|
||||
args.push('--dry');
|
||||
}
|
||||
|
||||
if (cmd.args.length) {
|
||||
args.push(...cmd.args);
|
||||
} else {
|
||||
args.push('.');
|
||||
}
|
||||
|
||||
console.log(`Running jscodeshift with these arguments: ${args.join(' ')}`);
|
||||
|
||||
const jscodeshiftScript = require.resolve('.bin/jscodeshift');
|
||||
const child = spawn(process.argv0, [jscodeshiftScript, ...args], {
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
env: {
|
||||
...process.env,
|
||||
FORCE_COLOR: 'true',
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof child.exitCode === 'number') {
|
||||
if (child.exitCode) {
|
||||
throw new ExitCodeError(child.exitCode, name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
child.once('error', error => reject(error));
|
||||
child.once('exit', code => {
|
||||
if (code) {
|
||||
reject(new ExitCodeError(code, name));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2021 Spotify AB
|
||||
*
|
||||
* 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 interface Codemod {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export const codemods: Codemod[] = [
|
||||
{
|
||||
name: 'core-imports',
|
||||
description:
|
||||
'Updates @backstage/core imports to use @backstage/core-* imports instead.',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 chalk from 'chalk';
|
||||
|
||||
export class CustomError extends Error {
|
||||
get name(): string {
|
||||
return this.constructor.name;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExitCodeError extends CustomError {
|
||||
readonly code: number;
|
||||
|
||||
constructor(code: number, command?: string) {
|
||||
if (command) {
|
||||
super(`Command '${command}' exited with code ${code}`);
|
||||
} else {
|
||||
super(`Child exited with code ${code}`);
|
||||
}
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
export function exitWithError(error: Error): never {
|
||||
if (error instanceof ExitCodeError) {
|
||||
process.stderr.write(`\n${chalk.red(error.message)}\n\n`);
|
||||
process.exit(error.code);
|
||||
} else {
|
||||
process.stderr.write(`\n${chalk.red(`${error}`)}\n\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 program from 'commander';
|
||||
import chalk from 'chalk';
|
||||
import { codemods } from './codemods';
|
||||
import { exitWithError } from './errors';
|
||||
import { createCodemodAction } from './action';
|
||||
import { version } from '../package.json';
|
||||
|
||||
async function main(argv: string[]) {
|
||||
program.name('backstage-codemods').version(version);
|
||||
|
||||
const applyCommand = program
|
||||
.command('apply <codemod> [<target-dirs...>]')
|
||||
.description(
|
||||
'Apply a codemod to target directories, defaulting to the current directory',
|
||||
);
|
||||
|
||||
for (const codemod of codemods) {
|
||||
applyCommand
|
||||
.command(`${codemod.name} [<target-dirs...>]`)
|
||||
.description(codemod.description)
|
||||
.option('-d, --dry', 'Dry run, no changes written to files')
|
||||
.action(createCodemodAction(codemod.name));
|
||||
}
|
||||
|
||||
program
|
||||
.command('list')
|
||||
.description('List available codemods')
|
||||
.action(() => {
|
||||
const maxNameLength = Math.max(...codemods.map(m => m.name.length));
|
||||
for (const codemod of codemods) {
|
||||
const paddedName = codemod.name.padEnd(maxNameLength, ' ');
|
||||
console.log(`${paddedName} - ${codemod.description}`);
|
||||
}
|
||||
});
|
||||
|
||||
program.on('command:*', () => {
|
||||
console.log();
|
||||
console.log(chalk.red(`Invalid command: ${program.args.join(' ')}`));
|
||||
console.log();
|
||||
program.outputHelp();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
program.parse(argv);
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', (rejection: unknown) => {
|
||||
if (rejection instanceof Error) {
|
||||
exitWithError(rejection);
|
||||
} else {
|
||||
exitWithError(new Error(`Unknown rejection: '${rejection}'`));
|
||||
}
|
||||
});
|
||||
|
||||
main(process.argv).catch(exitWithError);
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2021 Spotify AB
|
||||
*
|
||||
* 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 jscodeshift, { API } from 'jscodeshift';
|
||||
import coreImportTransform from '../../transforms/core-imports';
|
||||
|
||||
function runTransform(source: string) {
|
||||
const j = jscodeshift.withParser('tsx');
|
||||
const api: API = {
|
||||
j,
|
||||
jscodeshift: j,
|
||||
stats: () => undefined,
|
||||
report: () => undefined,
|
||||
};
|
||||
return coreImportTransform({ source, file: 'test.ts' }, api);
|
||||
}
|
||||
|
||||
describe('core-imports', () => {
|
||||
it('should leave file unchanged', () => {
|
||||
const input = `
|
||||
import something from 'somewhere';
|
||||
|
||||
function nothing() {
|
||||
return something()
|
||||
}
|
||||
`;
|
||||
|
||||
expect(runTransform(input)).toBe(input);
|
||||
});
|
||||
|
||||
it('should refactor imports', () => {
|
||||
const input = `
|
||||
/* COPYRIGHT: ME */
|
||||
import { Button as MyButton, createApiRef, createApp } from '@backstage/core';
|
||||
|
||||
const app = createApp();
|
||||
const apiRef = createApiRef();
|
||||
const button = <MyButton />
|
||||
`;
|
||||
|
||||
const output = `
|
||||
/* COPYRIGHT: ME */
|
||||
import { Button as MyButton } from '@backstage/core-components';
|
||||
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
import { createApp } from '@backstage/core-app-api';
|
||||
|
||||
const app = createApp();
|
||||
const apiRef = createApiRef();
|
||||
const button = <MyButton />
|
||||
`;
|
||||
|
||||
expect(runTransform(input)).toBe(output);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2021 Spotify AB
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const addImports = require('jscodeshift-add-imports');
|
||||
const { resolve: resolvePath } = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
function findExports(packageName) {
|
||||
const packagePath = require.resolve(`${packageName}/package.json`);
|
||||
const typesPath = resolvePath(packagePath, '../dist/index.d.ts');
|
||||
const content = fs.readFileSync(typesPath, 'utf8');
|
||||
|
||||
// For each export statement in the type declarations we grab the exported symbol names
|
||||
return content
|
||||
.split(/export \{ (.*) \}/)
|
||||
.filter((_, i) => i % 2)
|
||||
.flatMap(symbolsStr =>
|
||||
symbolsStr
|
||||
.split(', ')
|
||||
.map(exported => exported.match(/.* as (.*)/)?.[1] || exported),
|
||||
);
|
||||
}
|
||||
|
||||
const symbolTable = {
|
||||
'@backstage/core-app-api': findExports('@backstage/core-app-api'),
|
||||
'@backstage/core-components': findExports('@backstage/core-components'),
|
||||
'@backstage/core-plugin-api': findExports('@backstage/core-plugin-api'),
|
||||
};
|
||||
|
||||
const reverseSymbolTable = Object.entries(symbolTable).reduce(
|
||||
(table, [pkg, symbols]) => {
|
||||
for (const symbol of symbols) {
|
||||
table[symbol] = pkg;
|
||||
}
|
||||
return table;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
module.exports = (file, /** @type {import('jscodeshift').API} */ api) => {
|
||||
const j = api.jscodeshift;
|
||||
const root = j(file.source);
|
||||
|
||||
// Grab the file comment from the first node in case the import gets removed
|
||||
const firstNodeComment = root.find(j.Program).get('body', 0).node.comments;
|
||||
|
||||
// Find all import statements of @backstage/core
|
||||
const imports = root.find(j.ImportDeclaration, {
|
||||
source: {
|
||||
value: '@backstage/core',
|
||||
},
|
||||
});
|
||||
|
||||
if (imports.size === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Check what style we're using for the imports, ' or "
|
||||
const useSingleQuote =
|
||||
root.find(j.ImportDeclaration).nodes()[0]?.source.extra.raw[0] === "'";
|
||||
|
||||
// Then loop through all the import statement and collects each imported symbol
|
||||
const importedSymbols = imports.nodes().flatMap(node =>
|
||||
(node.specifiers || []).flatMap(specifier => ({
|
||||
name: specifier.imported.name, // The symbol we're importing
|
||||
local: specifier.local.name, // The local name, usually this is the same
|
||||
})),
|
||||
);
|
||||
|
||||
// Now that we gathered all the imports we want to add, we get rid of the old one
|
||||
imports.remove();
|
||||
|
||||
// The convert the imports into actual import statements
|
||||
const newImportStatements = importedSymbols.map(({ name, local }) => {
|
||||
const targetPackage = reverseSymbolTable[name];
|
||||
if (!targetPackage) {
|
||||
throw new Error(`No target package found for import of ${name}`);
|
||||
}
|
||||
|
||||
return j.importDeclaration(
|
||||
[j.importSpecifier(j.identifier(name), j.identifier(local))],
|
||||
j.literal(targetPackage),
|
||||
);
|
||||
});
|
||||
|
||||
// And add the new imports. `addImports` will take care of resolving duplicates
|
||||
addImports(root, newImportStatements);
|
||||
|
||||
// Restore the initial file comment in case it got removed
|
||||
root.find(j.Program).get('body', 0).node.comments = firstNodeComment;
|
||||
|
||||
return root.toSource({
|
||||
quote: useSingleQuote ? 'single' : 'double',
|
||||
});
|
||||
};
|
||||
@@ -15,14 +15,6 @@
|
||||
*/
|
||||
|
||||
import { Entity, LOCATION_ANNOTATION } from '@backstage/catalog-model';
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
Content,
|
||||
Header,
|
||||
HeaderLabel,
|
||||
Page,
|
||||
} from '@backstage/core';
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import OnlineIcon from '@material-ui/icons/Cloud';
|
||||
@@ -30,6 +22,9 @@ import OfflineIcon from '@material-ui/icons/Storage';
|
||||
import React from 'react';
|
||||
import { EntityTodoContent, todoApiRef, todoPlugin } from '../src';
|
||||
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { Content, Header, HeaderLabel, Page } from '@backstage/core-components';
|
||||
|
||||
const entity: Entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.8.0",
|
||||
"@backstage/core": "^0.7.11",
|
||||
"@backstage/core-components": "^0.1.0",
|
||||
"@backstage/core-plugin-api": "^0.1.0",
|
||||
"@backstage/errors": "^0.1.1",
|
||||
"@backstage/plugin-catalog-react": "^0.2.0",
|
||||
"@backstage/theme": "^0.2.8",
|
||||
@@ -40,6 +41,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.6.14",
|
||||
"@backstage/core-app-api": "^0.1.0",
|
||||
"@backstage/dev-utils": "^0.1.17",
|
||||
"@backstage/test-utils": "^0.1.13",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
import { serializeEntityRef } from '@backstage/catalog-model';
|
||||
import { ResponseError } from '@backstage/errors';
|
||||
import { DiscoveryApi, IdentityApi } from '@backstage/core';
|
||||
import { TodoApi, TodoListOptions, TodoListResult } from './types';
|
||||
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
|
||||
|
||||
interface Options {
|
||||
discoveryApi: DiscoveryApi;
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createApiRef } from '@backstage/core';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
export type TodoItem = {
|
||||
/** The contents of the TODO comment */
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import { renderWithEffects } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { TodoApi, todoApiRef } from '../../api';
|
||||
import { TodoList } from './TodoList';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
|
||||
describe('TodoList', () => {
|
||||
it('should render', async () => {
|
||||
|
||||
@@ -14,19 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableColumn,
|
||||
useApi,
|
||||
OverflowTooltip,
|
||||
Link,
|
||||
ResponseErrorPanel,
|
||||
} from '@backstage/core';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import React, { useState } from 'react';
|
||||
import { todoApiRef } from '../../api';
|
||||
import { TodoItem, TodoListOptions } from '../../api/types';
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableColumn,
|
||||
OverflowTooltip,
|
||||
Link,
|
||||
ResponseErrorPanel,
|
||||
} from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
const columns: TableColumn<TodoItem>[] = [
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { todoApiRef, TodoClient } from './api';
|
||||
import {
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createComponentExtension,
|
||||
discoveryApiRef,
|
||||
identityApiRef,
|
||||
} from '@backstage/core';
|
||||
import { todoApiRef, TodoClient } from './api';
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
// import { rootRouteRef } from './routes';
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createRouteRef } from '@backstage/core';
|
||||
import { createRouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
title: 'todo',
|
||||
|
||||
Reference in New Issue
Block a user