feat(angular): add opinionated mfe webpack helpers (#9233)
* feat(angular): add mfe helpers for sharing workspace libraries * feat(angular): add share packages helper * chore(angular): add webpack dep
This commit is contained in:
parent
5d447c559a
commit
bef8fb3629
@ -15,7 +15,8 @@
|
||||
"webpack-merge",
|
||||
"ts-node",
|
||||
"tsconfig-paths",
|
||||
"semver"
|
||||
"semver",
|
||||
"webpack"
|
||||
],
|
||||
"keepLifecycleScripts": true
|
||||
}
|
||||
|
||||
@ -50,6 +50,7 @@
|
||||
"webpack-merge": "5.7.3",
|
||||
"ts-node": "~9.1.1",
|
||||
"tsconfig-paths": "^3.9.0",
|
||||
"semver": "7.3.4"
|
||||
"semver": "7.3.4",
|
||||
"webpack": "^5.58.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,11 @@ import { normalizeOptions } from './lib';
|
||||
import type { Schema } from './schema';
|
||||
|
||||
export function webpackServer(schema: Schema, context: BuilderContext) {
|
||||
process.env.NX_TSCONFIG_PATH = joinPathFragments(
|
||||
context.workspaceRoot,
|
||||
'tsconfig.base.json'
|
||||
);
|
||||
|
||||
const options = normalizeOptions(schema);
|
||||
const workspaceConfig = new Workspaces(
|
||||
context.workspaceRoot
|
||||
|
||||
125
packages/angular/src/utils/mfe-webpack.spec.ts
Normal file
125
packages/angular/src/utils/mfe-webpack.spec.ts
Normal file
@ -0,0 +1,125 @@
|
||||
jest.mock('fs');
|
||||
jest.mock('@nrwl/workspace');
|
||||
import * as fs from 'fs';
|
||||
import * as workspace from '@nrwl/workspace';
|
||||
|
||||
import { sharePackages, shareWorkspaceLibraries } from './mfe-webpack';
|
||||
|
||||
describe('MFE Webpack Utils', () => {
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
describe('ShareWorkspaceLibraries', () => {
|
||||
it('should error when the tsconfig file does not exist', () => {
|
||||
// ARRANGE
|
||||
(fs.existsSync as jest.Mock).mockReturnValue(false);
|
||||
|
||||
// ACT
|
||||
try {
|
||||
shareWorkspaceLibraries(['@myorg/shared']);
|
||||
} catch (error) {
|
||||
// ASSERT
|
||||
expect(error.message).toEqual(
|
||||
'NX MFE: TsConfig Path for workspace libraries does not exist! (undefined)'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should create an object with correct setup', () => {
|
||||
// ARRANGE
|
||||
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
||||
(workspace.readTsConfig as jest.Mock).mockReturnValue({
|
||||
options: {
|
||||
paths: {
|
||||
'@myorg/shared': ['/libs/shared/src/index.ts'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ACT
|
||||
const sharedLibraries = shareWorkspaceLibraries(['@myorg/shared']);
|
||||
// ASSERT
|
||||
expect(sharedLibraries.getAliases()).toHaveProperty('@myorg/shared');
|
||||
expect(sharedLibraries.getAliases()['@myorg/shared']).toContain(
|
||||
'libs/shared/src/index.ts'
|
||||
);
|
||||
expect(sharedLibraries.getLibraries()).toEqual({
|
||||
'@myorg/shared': {
|
||||
eager: undefined,
|
||||
requiredVersion: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an object with empty setup when tsconfig does not contain the shared lib', () => {
|
||||
// ARRANGE
|
||||
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
||||
(workspace.readTsConfig as jest.Mock).mockReturnValue({
|
||||
options: {
|
||||
paths: {},
|
||||
},
|
||||
});
|
||||
|
||||
// ACT
|
||||
const sharedLibraries = shareWorkspaceLibraries(['@myorg/shared']);
|
||||
// ASSERT
|
||||
expect(sharedLibraries.getAliases()).toEqual({});
|
||||
expect(sharedLibraries.getLibraries()).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SharePackages', () => {
|
||||
it('should throw when it cannot find root package.json', () => {
|
||||
// ARRANGE
|
||||
(fs.existsSync as jest.Mock).mockReturnValue(false);
|
||||
|
||||
// ACT
|
||||
try {
|
||||
sharePackages(['@angular/core']);
|
||||
} catch (error) {
|
||||
// ASSERT
|
||||
expect(error.message).toEqual(
|
||||
'NX MFE: Could not find root package.json to determine dependency versions.'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should correctly map the shared packages to objects', () => {
|
||||
// ARRANGE
|
||||
(fs.existsSync as jest.Mock).mockReturnValue(true);
|
||||
(fs.readFileSync as jest.Mock).mockReturnValue(
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
'@angular/core': '~13.2.0',
|
||||
'@angular/common': '~13.2.0',
|
||||
rxjs: '~7.4.0',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// ACT
|
||||
const packages = sharePackages([
|
||||
'@angular/core',
|
||||
'@angular/common',
|
||||
'rxjs',
|
||||
]);
|
||||
// ASSERT
|
||||
expect(packages).toEqual({
|
||||
'@angular/core': {
|
||||
singleton: true,
|
||||
strictVersion: true,
|
||||
requiredVersion: '~13.2.0',
|
||||
},
|
||||
'@angular/common': {
|
||||
singleton: true,
|
||||
strictVersion: true,
|
||||
requiredVersion: '~13.2.0',
|
||||
},
|
||||
rxjs: {
|
||||
singleton: true,
|
||||
strictVersion: true,
|
||||
requiredVersion: '~7.4.0',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
96
packages/angular/src/utils/mfe-webpack.ts
Normal file
96
packages/angular/src/utils/mfe-webpack.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { readTsConfig } from '@nrwl/workspace';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { NormalModuleReplacementPlugin } from 'webpack';
|
||||
import { appRootPath as rootPath } from '@nrwl/tao/src/utils/app-root';
|
||||
import { normalizePath, joinPathFragments } from '@nrwl/devkit';
|
||||
import { dirname } from 'path';
|
||||
import { ParsedCommandLine } from 'typescript';
|
||||
|
||||
export function shareWorkspaceLibraries(
|
||||
libraries: string[],
|
||||
tsConfigPath = process.env.NX_TSCONFIG_PATH
|
||||
) {
|
||||
if (!existsSync(tsConfigPath)) {
|
||||
throw new Error(
|
||||
`NX MFE: TsConfig Path for workspace libraries does not exist! (${tsConfigPath})`
|
||||
);
|
||||
}
|
||||
|
||||
const tsConfig: ParsedCommandLine = readTsConfig(tsConfigPath);
|
||||
const tsconfigPathAliases = tsConfig.options?.paths;
|
||||
|
||||
if (!tsconfigPathAliases) {
|
||||
return {
|
||||
getAliases: () => [],
|
||||
getLibraries: () => [],
|
||||
getReplacementPlugin: () =>
|
||||
new NormalModuleReplacementPlugin(/./, () => {}),
|
||||
};
|
||||
}
|
||||
|
||||
const pathMappings: { name: string; path: string }[] = [];
|
||||
for (const [key, paths] of Object.entries(tsconfigPathAliases)) {
|
||||
if (libraries && libraries.includes(key)) {
|
||||
const pathToLib = normalizePath(joinPathFragments(rootPath, paths[0]));
|
||||
pathMappings.push({
|
||||
name: key,
|
||||
path: pathToLib,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getAliases: () =>
|
||||
pathMappings.reduce(
|
||||
(aliases, library) => ({ ...aliases, [library.name]: library.path }),
|
||||
{}
|
||||
),
|
||||
getLibraries: (eager?: boolean) =>
|
||||
pathMappings.reduce(
|
||||
(libraries, library) => ({
|
||||
...libraries,
|
||||
[library.name]: { requiredVersion: false, eager },
|
||||
}),
|
||||
{}
|
||||
),
|
||||
getReplacementPlugin: () =>
|
||||
new NormalModuleReplacementPlugin(/./, (req) => {
|
||||
if (!req.request.startsWith('.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const from = req.context;
|
||||
const to = normalizePath(joinPathFragments(req.context, req.request));
|
||||
|
||||
for (const library of pathMappings) {
|
||||
const libFolder = normalizePath(dirname(library.path));
|
||||
if (!from.startsWith(libFolder) && to.startsWith(libFolder)) {
|
||||
req.request = library.name;
|
||||
}
|
||||
}
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function sharePackages(packages: string[]) {
|
||||
const pkgJsonPath = joinPathFragments(rootPath, 'package.json');
|
||||
if (!existsSync(pkgJsonPath)) {
|
||||
throw new Error(
|
||||
'NX MFE: Could not find root package.json to determine dependency versions.'
|
||||
);
|
||||
}
|
||||
|
||||
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
||||
|
||||
return packages.reduce(
|
||||
(shared, pkgName) => ({
|
||||
...shared,
|
||||
[pkgName]: {
|
||||
singleton: true,
|
||||
strictVersion: true,
|
||||
requiredVersion: pkgJson.dependencies[pkgName],
|
||||
},
|
||||
}),
|
||||
{}
|
||||
);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user