Merge branch 'master' of https://github.com/spotify/backstage into lintMod
This commit is contained in:
@@ -15,12 +15,43 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import chalk from 'chalk';
|
||||
import uniq from 'lodash/uniq';
|
||||
import { Command } from 'commander';
|
||||
import { serveBundle } from '../../lib/bundler';
|
||||
import { loadCliConfig } from '../../lib/config';
|
||||
import { paths } from '../../lib/paths';
|
||||
import { Lockfile } from '../../lib/versioning';
|
||||
import { includedFilter } from '../versions/lint';
|
||||
|
||||
export default async (cmd: Command) => {
|
||||
const lockfile = await Lockfile.load(paths.resolveTargetRoot('yarn.lock'));
|
||||
const result = lockfile.analyze({
|
||||
filter: includedFilter,
|
||||
});
|
||||
const problemPackages = [...result.newVersions, ...result.newRanges].map(
|
||||
({ name }) => name,
|
||||
);
|
||||
|
||||
if (problemPackages.length > 1) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`⚠️ Some of the following packages may be outdated or have duplicate installations:
|
||||
|
||||
${uniq(problemPackages).join(', ')}
|
||||
`,
|
||||
),
|
||||
);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`⚠️ This can be resolved using the following command:
|
||||
|
||||
yarn backstage-cli versions:check --fix
|
||||
`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const { name } = await fs.readJson(paths.resolveTarget('package.json'));
|
||||
const waitForExit = await serveBundle({
|
||||
entry: 'src/index',
|
||||
|
||||
@@ -20,6 +20,8 @@ import chalk from 'chalk';
|
||||
import inquirer, { Answers, Question } from 'inquirer';
|
||||
import { exec as execCb } from 'child_process';
|
||||
import { resolve as resolvePath, join as joinPath } from 'path';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import os from 'os';
|
||||
import { Command } from 'commander';
|
||||
import {
|
||||
@@ -104,16 +106,12 @@ export async function addPluginDependencyToApp(
|
||||
});
|
||||
}
|
||||
|
||||
export async function addPluginToApp(
|
||||
export async function addPluginImportToApp(
|
||||
rootDir: string,
|
||||
pluginName: string,
|
||||
pluginVar: string,
|
||||
pluginPackage: string,
|
||||
) {
|
||||
const pluginNameCapitalized = pluginName
|
||||
.split('-')
|
||||
.map(name => capitalize(name))
|
||||
.join('');
|
||||
const pluginExport = `export { plugin as ${pluginNameCapitalized} } from '${pluginPackage}';`;
|
||||
const pluginExport = `export { ${pluginVar} } from '${pluginPackage}';`;
|
||||
const pluginsFilePath = 'packages/app/src/plugins.ts';
|
||||
const pluginsFile = resolvePath(rootDir, pluginsFilePath);
|
||||
|
||||
@@ -126,6 +124,46 @@ export async function addPluginToApp(
|
||||
});
|
||||
}
|
||||
|
||||
export async function addPluginExtensionToApp(
|
||||
pluginId: string,
|
||||
extensionName: string,
|
||||
pluginPackage: string,
|
||||
) {
|
||||
const pluginsFilePath = paths.resolveTargetRoot('packages/app/src/App.tsx');
|
||||
if (!(await fs.pathExists(pluginsFilePath))) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.forItem('processing', pluginsFilePath, async () => {
|
||||
const content = await fs.readFile(pluginsFilePath, 'utf8');
|
||||
const revLines = content.split('\n').reverse();
|
||||
|
||||
const lastImportIndex = revLines.findIndex(line =>
|
||||
line.match(/ from ("|').*("|')/),
|
||||
);
|
||||
const lastRouteIndex = revLines.findIndex(line =>
|
||||
line.match(/<\/FlatRoutes/),
|
||||
);
|
||||
|
||||
if (lastImportIndex !== -1 && lastRouteIndex !== -1) {
|
||||
revLines.splice(
|
||||
lastImportIndex,
|
||||
0,
|
||||
`import { ${extensionName} } from '${pluginPackage}';`,
|
||||
);
|
||||
const [indentation] = revLines[lastRouteIndex + 1].match(/^\s*/) ?? [];
|
||||
revLines.splice(
|
||||
lastRouteIndex + 1,
|
||||
0,
|
||||
`${indentation}<Route path="/${pluginId}" element={<${extensionName} />}/>`,
|
||||
);
|
||||
|
||||
const newContent = revLines.reverse().join('\n');
|
||||
await fs.writeFile(pluginsFilePath, newContent, 'utf8');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function cleanUp(tempDir: string) {
|
||||
await Task.forItem('remove', 'temporary directory', async () => {
|
||||
await fs.remove(tempDir);
|
||||
@@ -223,6 +261,8 @@ export default async (cmd: Command) => {
|
||||
const name = cmd.scope
|
||||
? `@${cmd.scope.replace(/^@/, '')}/plugin-${pluginId}`
|
||||
: `plugin-${pluginId}`;
|
||||
const pluginVar = `${camelCase(answers.id)}Plugin`;
|
||||
const extensionName = `${upperFirst(camelCase(answers.id))}Page`;
|
||||
const npmRegistry = cmd.npmRegistry && cmd.scope ? cmd.npmRegistry : '';
|
||||
const privatePackage = cmd.private === false ? false : true;
|
||||
const isMonoRepo = await fs.pathExists(paths.resolveTargetRoot('lerna.json'));
|
||||
@@ -259,7 +299,9 @@ export default async (cmd: Command) => {
|
||||
tempDir,
|
||||
{
|
||||
...answers,
|
||||
pluginVar,
|
||||
pluginVersion,
|
||||
extensionName,
|
||||
name,
|
||||
privatePackage,
|
||||
npmRegistry,
|
||||
@@ -278,7 +320,8 @@ export default async (cmd: Command) => {
|
||||
await addPluginDependencyToApp(paths.targetRoot, name, pluginVersion);
|
||||
|
||||
Task.section('Import plugin in app');
|
||||
await addPluginToApp(paths.targetRoot, pluginId, name);
|
||||
await addPluginImportToApp(paths.targetRoot, pluginVar, name);
|
||||
await addPluginExtensionToApp(pluginId, extensionName, name);
|
||||
}
|
||||
|
||||
if (ownerIds && ownerIds.length) {
|
||||
|
||||
@@ -39,6 +39,11 @@ const fileHandlers = [
|
||||
patterns: ['package.json'],
|
||||
handler: handlers.packageJson,
|
||||
},
|
||||
{
|
||||
// Not all plugins have routes
|
||||
patterns: ['src/routes.ts'],
|
||||
handler: handlers.skip,
|
||||
},
|
||||
{
|
||||
// make sure files in 1st level of src/ and dev/ exist
|
||||
patterns: ['.eslintrc.js', /^(src|dev)\/[^/]+$/],
|
||||
|
||||
@@ -219,7 +219,7 @@ export async function createBackendConfig(
|
||||
}
|
||||
: {}),
|
||||
externals: [
|
||||
nodeExternals({
|
||||
nodeExternalsWithResolve({
|
||||
modulesDir: paths.rootNodeModules,
|
||||
additionalModuleDirs: moduleDirs,
|
||||
allowlist: ['webpack/hot/poll?100', ...localPackageNames],
|
||||
@@ -296,3 +296,32 @@ export async function createBackendConfig(
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// This makes the module resolution happen from the context of each non-external module, rather
|
||||
// than the main entrypoint. This fixes a bug where dependencies would be resolved from the backend
|
||||
// package rather than each individual backend package and plugin.
|
||||
//
|
||||
// TODO(Rugvip): Feature suggestion/contribute this to webpack-externals
|
||||
function nodeExternalsWithResolve(
|
||||
options: Parameters<typeof nodeExternals>[0],
|
||||
) {
|
||||
let currentContext: string;
|
||||
const externals = nodeExternals({
|
||||
...options,
|
||||
importType(request) {
|
||||
const resolved = require.resolve(request, {
|
||||
paths: [currentContext],
|
||||
});
|
||||
return `commonjs ${resolved}`;
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
context: string,
|
||||
request: string,
|
||||
callback: webpack.ExternalsFunctionCallback,
|
||||
) => {
|
||||
currentContext = context;
|
||||
return externals(context, request, callback);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user