cli: remove LinkedPackageResolvePlugin

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-10-22 11:16:17 +02:00
parent d99b439399
commit d6bdcb8876
3 changed files with 0 additions and 264 deletions
@@ -1,176 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 * as os from 'os';
import * as path from 'path';
import { LinkedPackageResolvePlugin } from './LinkedPackageResolvePlugin';
describe('LinkedPackageResolvePlugin', () => {
const root = os.platform() === 'win32' ? 'C:\\root' : '/root';
it('should re-write paths for external packages', () => {
const plugin = new LinkedPackageResolvePlugin(
path.resolve(root, 'repo/node_modules'),
[
{
dir: path.resolve(root, 'external-a'),
packageJson: {
name: 'a',
version: '1.0.0',
},
},
{
dir: path.resolve(root, 'external-b'),
packageJson: {
name: '@s/b',
version: '1.0.0',
},
},
],
);
const tapAsync = jest.fn();
const doResolve = jest.fn();
const resolver = {
hooks: { resolve: { tapAsync } },
doResolve,
};
plugin.apply(resolver);
expect(tapAsync).toHaveBeenCalledTimes(1);
expect(tapAsync).toHaveBeenCalledWith(
'LinkedPackageResolvePlugin',
expect.any(Function),
);
expect(doResolve).toHaveBeenCalledTimes(0);
// Internal module resolution is not affected
const tap = tapAsync.mock.calls[0][1];
const callbackX = jest.fn();
tap(
{
request: path.resolve(root, 'repo/package/x/src/module.ts'),
path: path.resolve(root, 'repo/package/x/src'),
context: {
issuer: path.resolve(root, 'repo/package/x/src/index.ts'),
},
},
'some-context',
callbackX,
);
expect(callbackX).toHaveBeenCalledTimes(1);
expect(callbackX).toHaveBeenCalledWith();
expect(doResolve).toHaveBeenCalledTimes(0);
// Path is sometimes false
const callbackFalse = jest.fn();
tap(
{
request: 'sample',
path: false,
},
'some-context',
callbackFalse,
);
expect(callbackFalse).toHaveBeenCalledTimes(1);
expect(callbackFalse).toHaveBeenCalledWith();
expect(doResolve).toHaveBeenCalledTimes(0);
// Internal modules with a path prefix of an external module
const callbackY = jest.fn();
tap(
{
request: path.resolve(root, 'external-aa/src/module.ts'),
path: path.resolve(root, 'external-aa/src'),
context: {
issuer: path.resolve(root, 'external-aa/src/index.ts'),
},
},
'some-context',
callbackY,
);
expect(callbackY).toHaveBeenCalledTimes(1);
expect(callbackY).toHaveBeenCalledWith();
expect(doResolve).toHaveBeenCalledTimes(0);
// External modules have their path and issuer context rewritten, but not the request
const callbackA = jest.fn();
tap(
{
request: path.resolve(root, 'external-a/src/module.ts'),
path: path.resolve(root, 'external-a/src'),
context: {
issuer: path.resolve(root, 'external-a/src/index.ts'),
},
},
'some-context',
callbackA,
);
expect(callbackA).toHaveBeenCalledTimes(0);
expect(doResolve).toHaveBeenCalledTimes(1);
expect(doResolve).toHaveBeenCalledWith(
resolver.hooks.resolve,
{
request: path.resolve(root, 'external-a/src/module.ts'),
path: path.resolve(root, 'repo/node_modules/a/src'),
context: {
issuer: path.resolve(root, 'repo/node_modules/a/src/index.ts'),
},
},
`resolve ${path.resolve(
root,
'external-a/src/module.ts',
)} in ${path.resolve(root, 'repo/node_modules/a')}`,
'some-context',
callbackA,
);
// Also handles scoped packages correctly, and issuer is not required
const callbackB = jest.fn();
tap(
{
request: path.resolve(root, 'external-b/src/module.ts'),
path: path.resolve(root, 'external-b/src'),
context: {
issuer: false,
},
},
'some-context',
callbackB,
);
expect(callbackB).toHaveBeenCalledTimes(0);
expect(doResolve).toHaveBeenCalledTimes(2);
expect(doResolve).toHaveBeenLastCalledWith(
resolver.hooks.resolve,
{
request: path.resolve(root, 'external-b/src/module.ts'),
path: path.resolve(root, 'repo/node_modules/@s/b/src'),
context: {
issuer: false,
},
},
`resolve ${path.resolve(
root,
'external-b/src/module.ts',
)} in ${path.resolve(root, 'repo/node_modules/@s/b')}`,
'some-context',
callbackB,
);
expect(tapAsync).toHaveBeenCalledTimes(1);
});
});
@@ -1,82 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 { resolve as resolvePath } from 'path';
import { WebpackPluginInstance } from 'webpack';
import { isChildPath } from '@backstage/cli-common';
import { Package } from '@manypkg/get-packages';
// Enables proper resolution of packages when linking in external packages.
// Without this the packages would depend on dependencies in the node_modules
// of the external packages themselves, leading to module duplication
export class LinkedPackageResolvePlugin implements WebpackPluginInstance {
constructor(
private readonly targetModules: string,
private readonly packages: Package[],
) {}
apply(resolver: any) {
resolver.hooks.resolve.tapAsync(
'LinkedPackageResolvePlugin',
(
data: {
request: string;
path?: false | string;
context?: { issuer?: string };
},
context: unknown,
callback: () => void,
) => {
const pkg = this.packages.find(
pkge => data.path && isChildPath(pkge.dir, data.path),
);
if (!pkg) {
callback();
return;
}
// pkg here is an external package. We rewrite the context of any imports to resolve
// from the location of the package within the node_modules of the target root rather
// than the real location of the external package.
const modulesLocation = resolvePath(
this.targetModules,
pkg.packageJson.name,
);
const newContext = data.context?.issuer
? {
...data.context,
issuer: data.context.issuer.replace(pkg.dir, modulesLocation),
}
: data.context;
// Re-run resolution but this time from the point of view of our target monorepo rather
// than the location of the external package. By resolving modules using this method we avoid
// pulling in e.g. `react` from the external repo, which would otherwise lead to conflicts.
resolver.doResolve(
resolver.hooks.resolve,
{
...data,
context: newContext,
path: data.path && data.path.replace(pkg.dir, modulesLocation),
},
`resolve ${data.request} in ${modulesLocation}`,
context,
callback,
);
},
);
}
}
-6
View File
@@ -25,13 +25,10 @@ import ESLintPlugin from 'eslint-webpack-plugin';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import { ModuleFederationPlugin } from '@module-federation/enhanced/webpack';
import { LinkedPackageResolvePlugin } from './LinkedPackageResolvePlugin';
import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin';
import ReactRefreshPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import { paths as cliPaths } from '../../lib/paths';
import fs from 'fs-extra';
import { getPackages } from '@manypkg/get-packages';
import { isChildPath } from '@backstage/cli-common';
import { optimization as optimizationConfig } from './optimization';
import pickBy from 'lodash/pickBy';
import { runPlain } from '../run';
@@ -130,8 +127,6 @@ export async function createConfig(
const { plugins, loaders } = transforms(options);
// Any package that is part of the monorepo but outside the monorepo root dir need
// separate resolution logic.
const { packages } = await getPackages(cliPaths.targetDir);
const externalPkgs = packages.filter(p => !isChildPath(paths.root, p.dir));
const validBaseUrl = resolveBaseUrl(frontendConfig, moduleFederation);
let publicPath = validBaseUrl.pathname.replace(/\/$/, '');
@@ -406,7 +401,6 @@ export async function createConfig(
// FIXME: see also https://github.com/web-infra-dev/rspack/issues/3408
...(!rspack && {
plugins: [
new LinkedPackageResolvePlugin(paths.rootNodeModules, externalPkgs),
new ModuleScopePlugin(
[paths.targetSrc, paths.targetDev],
[paths.targetPackageJson, ...reactRefreshFiles],