Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: experimental embed-as-dependencies option for the backend #1418

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@
"webpack": "^5.89.0",
"webpack-dev-server": "^4.15.1",
"yml-loader": "^2.1.0",
"yn": "^4.0.0"
"yn": "^4.0.0",
"is-native-module": "^1.1.3"
},
"devDependencies": {
"@backstage/backend-common": "0.21.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,14 @@
*/

import { BackstagePackageJson, PackageRoleInfo } from '@backstage/cli-node';
import { ConfigReader } from '@backstage/config';
import { loadConfig } from '@backstage/config-loader';

import { getPackages } from '@manypkg/get-packages';
import { OptionValues } from 'commander';
import fs from 'fs-extra';
import { InteropType, rollup } from 'rollup';
import * as semver from 'semver';

import { execSync } from 'child_process';
import path, { basename } from 'path';
import path from 'path';

import { Output } from '../../lib/builder';
import { makeRollupConfigs } from '../../lib/builder/config';
Expand All @@ -35,11 +32,15 @@ import { readEntryPoints } from '../../lib/entryPoints';
import { productionPack } from '../../lib/packager/productionPack';
import { paths } from '../../lib/paths';
import { Task } from '../../lib/tasks';
import {
addToDependenciesForModule,
addToMainDependencies,
} from './backend-utils';

export async function backend(
roleInfo: PackageRoleInfo,
opts: OptionValues,
): Promise<void> {
): Promise<string> {
if (!fs.existsSync(paths.resolveTarget('src', 'dynamic'))) {
console.warn(
`Package doesn't seem to provide dynamic loading entrypoints. You might want to add dynamic loading entrypoints in a src/dynamic folder.`,
Expand Down Expand Up @@ -166,46 +167,15 @@ export async function backend(
rollupConfig.plugins?.push(
embedModules({
filter: filter,
addDependency(embeddedModule, dependencyName, newDependencyVersion) {
const existingDependencyVersion = dependenciesToAdd[dependencyName];
if (existingDependencyVersion === undefined) {
dependenciesToAdd[dependencyName] = newDependencyVersion;
return;
}

if (existingDependencyVersion === newDependencyVersion) {
return;
}

const existingDependencyMinVersion = semver.minVersion(
existingDependencyVersion,
);
if (
existingDependencyMinVersion &&
semver.satisfies(existingDependencyMinVersion, newDependencyVersion)
) {
console.log(
`Several compatible versions ('${existingDependencyVersion}', '${newDependencyVersion}') of the same transitive dependency ('${dependencyName}') for embedded module ('${embeddedModule}'): keeping '${existingDependencyVersion}'`,
);
return;
}

const newDependencyMinVersion = semver.minVersion(newDependencyVersion);
if (
newDependencyMinVersion &&
semver.satisfies(newDependencyMinVersion, existingDependencyVersion)
) {
dependenciesToAdd[dependencyName] = newDependencyVersion;
console.log(
`Several compatible versions ('${existingDependencyVersion}', '${newDependencyVersion}') of the same transitive dependency ('${dependencyName}') for embedded module ('${embeddedModule}'): keeping '${newDependencyVersion}'`,
);
return;
}

throw new Error(
`Several incompatible versions ('${existingDependencyVersion}', '${newDependencyVersion}') of the same transitive dependency ('${dependencyName}') for embedded module ('${embeddedModule}')`,
);
},
addDependency: (embeddedModule, dependencyName, newDependencyVersion) =>
addToDependenciesForModule(
{
name: dependencyName,
version: newDependencyVersion,
},
dependenciesToAdd,
embeddedModule,
),
}),
);

Expand Down Expand Up @@ -267,33 +237,19 @@ export async function backend(
f => !f.startsWith('dist-dynamic/'),
);

for (const dep in dependenciesToAdd) {
if (!Object.prototype.hasOwnProperty.call(dependenciesToAdd, dep)) {
continue;
}
pkgToCustomize.dependencies ||= {};
const existingVersion = pkgToCustomize.dependencies[dep];
if (existingVersion === undefined) {
pkgToCustomize.dependencies[dep] = dependenciesToAdd[dep];
continue;
}
if (existingVersion !== dependenciesToAdd[dep]) {
const existingMinVersion = semver.minVersion(existingVersion);

if (
existingMinVersion &&
semver.satisfies(existingMinVersion, dependenciesToAdd[dep])
) {
console.log(
`The version of a dependency ('${dep}') of an embedded module differs from the main module's dependencies: '${dependenciesToAdd[dep]}', '${existingVersion}': keeping it as it is compatible`,
);
} else {
throw new Error(
`The version of a dependency ('${dep}') of an embedded module conflicts with main module dependencies: '${dependenciesToAdd[dep]}', '${existingVersion}': cannot proceed!`,
);
}
}
// We remove scripts, because they do not make sense for this derived package.
// They even bring errors, especially the pre-pack and post-pack ones:
// we want to be able to use npm pack on this derived package to distribute it as a dynamic plugin,
// and obviously this should not trigger the backstage pre-pack or post-pack actions
// which are related to the packaging of the original static package.
pkgToCustomize.scripts = {};

const pkgDependencies = pkgToCustomize.dependencies || {};
addToMainDependencies(dependenciesToAdd, pkgDependencies);
if (Object.keys(pkgDependencies).length > 0) {
pkgToCustomize.dependencies = pkgDependencies;
}

if (pkgToCustomize.dependencies) {
for (const monoRepoPackage of monoRepoPackages.packages) {
if (pkgToCustomize.dependencies[monoRepoPackage.packageJson.name]) {
Expand Down Expand Up @@ -376,10 +332,13 @@ export async function backend(
}

if (opts.install) {
const version = execSync('yarn --version').toString().trim();
const yarn = 'yarn';
const version = execSync(`${yarn} --version`).toString().trim();
const yarnInstall = version.startsWith('1.')
? `yarn install --production${yarnLockExists ? ' --frozen-lockfile' : ''}`
: `yarn install${yarnLockExists ? ' --immutable' : ''}`;
? `${yarn} install --production${
yarnLockExists ? ' --frozen-lockfile' : ''
}`
: `${yarn} install${yarnLockExists ? ' --immutable' : ''}`;

await Task.forCommand(yarnInstall, { cwd: target, optional: false });
await fs.remove(paths.resolveTarget('dist-dynamic', '.yarn'));
Expand All @@ -393,39 +352,5 @@ export async function backend(
minify: Boolean(opts.minify),
});

if (opts.dev) {
const appConfigs = await loadConfig({
configRoot: paths.targetRoot,
configTargets: [],
});
const fullConfig = ConfigReader.fromConfigs(appConfigs.appConfigs);

const dynamicPlugins = fullConfig.getOptional('dynamicPlugins');
if (
typeof dynamicPlugins === 'object' &&
dynamicPlugins !== null &&
'rootDirectory' in dynamicPlugins &&
typeof dynamicPlugins.rootDirectory === 'string'
) {
await fs.ensureSymlink(
paths.resolveTarget('src'),
path.resolve(target, 'src'),
'dir',
);
const dynamicPluginsRootPath = path.isAbsolute(
dynamicPlugins.rootDirectory,
)
? dynamicPlugins.rootDirectory
: paths.resolveTargetRoot(dynamicPlugins.rootDirectory);
await fs.ensureSymlink(
target,
path.resolve(dynamicPluginsRootPath, basename(paths.targetDir)),
'dir',
);
} else {
throw new Error(
`'dynamicPlugins.rootDirectory' should be configured in the app config in order to use the --dev option.`,
);
}
}
return target;
}
Loading
Loading