Skip to content

Commit

Permalink
fix(module-federation): remote names should follow js variable naming…
Browse files Browse the repository at this point in the history
… schema
  • Loading branch information
Coly010 committed Oct 10, 2024
1 parent cf495ec commit fe5cdec
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 8 deletions.
15 changes: 15 additions & 0 deletions packages/angular/src/generators/remote/remote.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,4 +443,19 @@ describe('MF Remote App Generator', () => {
const packageJson = readJson(tree, 'package.json');
expect(packageJson).toEqual(initialPackageJson);
});

it('should error when an invalid remote name is passed to the remote generator', async () => {
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });

await expect(
generateTestRemoteApplication(tree, {
directory: 'test/my-remote',
})
).rejects.toMatchInlineSnapshot(`
[Error: Invalid remote name: my-remote. Remote project names must:
- Start with a letter, dollar sign ($) or underscore (_)
- Followed by any valid character (letters, digits, underscores, or dollar signs)
The regular expression used is /^[a-zA-Z_$][a-zA-Z_$0-9]*$/.]
`);
});
});
10 changes: 8 additions & 2 deletions packages/angular/src/generators/remote/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
formatFiles,
getProjects,
runTasksInSerial,
stripIndents,
Tree,
} from '@nx/devkit';
import {
Expand Down Expand Up @@ -39,9 +40,14 @@ export async function remote(tree: Tree, schema: Schema) {
directory: options.directory,
});

if (remoteProjectName.includes('-')) {
const REMOTE_NAME_REGEX = '^[a-zA-Z_$][a-zA-Z_$0-9]*$';
const remoteNameRegex = new RegExp(REMOTE_NAME_REGEX);
if (!remoteNameRegex.test(remoteProjectName)) {
throw new Error(
`Remote projects cannot contain '-' in their name. Invalid remote name: ${remoteProjectName}`
stripIndents`Invalid remote name: ${remoteProjectName}. Remote project names must:
- Start with a letter, dollar sign ($) or underscore (_)
- Followed by any valid character (letters, digits, underscores, or dollar signs)
The regular expression used is ${REMOTE_NAME_REGEX}.`
);
}
const port = options.port ?? findNextAvailablePort(tree);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`remote generator bundler=rspack should create the remote with the correct config files 1`] = `
"const { composePlugins, withNx, withReact } = require('@nx/rspack');
const { withModuleFederation } = require('@nx/rspack/module-federation');
const baseConfig = require('./module-federation.config');
const config = {
...baseConfig,
};
// Nx plugins for rspack to build config object from Nx options and context.
/**
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
* The DTS Plugin can be enabled by setting dts: true
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
*/
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
"
`;

exports[`remote generator bundler=rspack should create the remote with the correct config files 2`] = `"module.exports = require('./rspack.config');"`;

exports[`remote generator bundler=rspack should create the remote with the correct config files 3`] = `
"module.exports = {
name: 'test',
exposes: {
'./Module': './src/remote-entry.ts',
},
};
"
`;

exports[`remote generator bundler=rspack should create the remote with the correct config files when --js=true 1`] = `
"const { composePlugins, withNx, withReact } = require('@nx/rspack');
const { withModuleFederation } = require('@nx/rspack/module-federation');
const baseConfig = require('./module-federation.config');
const config = {
...baseConfig,
};
// Nx plugins for rspack to build config object from Nx options and context.
/**
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
* The DTS Plugin can be enabled by setting dts: true
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
*/
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
"
`;

exports[`remote generator bundler=rspack should create the remote with the correct config files when --js=true 2`] = `"module.exports = require('./rspack.config');"`;

exports[`remote generator bundler=rspack should create the remote with the correct config files when --js=true 3`] = `
"module.exports = {
name: 'test',
exposes: {
'./Module': './src/remote-entry.js',
},
};
"
`;

exports[`remote generator bundler=rspack should create the remote with the correct config files when --typescriptConfiguration=true 1`] = `
"import { composePlugins, withNx, withReact } from '@nx/rspack';
import { withModuleFederation } from '@nx/rspack/module-federation';
import baseConfig from './module-federation.config';
const config = {
...baseConfig,
};
// Nx plugins for rspack to build config object from Nx options and context.
/**
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
* The DTS Plugin can be enabled by setting dts: true
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
*/
export default composePlugins(
withNx(),
withReact(),
withModuleFederation(config, { dts: false })
);
"
`;

exports[`remote generator bundler=rspack should create the remote with the correct config files when --typescriptConfiguration=true 2`] = `
"export default require('./rspack.config');
"
`;

exports[`remote generator bundler=rspack should create the remote with the correct config files when --typescriptConfiguration=true 3`] = `
"import { ModuleFederationConfig } from '@nx/rspack/module-federation';
const config: ModuleFederationConfig = {
name: 'test',
exposes: {
'./Module': './src/remote-entry.ts',
},
};
export default config;
"
`;

exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr 1`] = `
"const {composePlugins, withNx, withReact} = require('@nx/rspack');
const {withModuleFederationForSSR} = require('@nx/rspack/module-federation');
const baseConfig = require("./module-federation.server.config");
const defaultConfig = {
...baseConfig,
};
// Nx plugins for rspack to build config object from Nx options and context.
/**
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
* The DTS Plugin can be enabled by setting dts: true
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
*/
module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
"
`;

exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr 2`] = `
"module.exports = {
name: 'test',
exposes: {
'./Module': './src/remote-entry.ts',
},
};
"
`;

exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 1`] = `
"import { composePlugins, withNx, withReact } from '@nx/rspack';
import { withModuleFederationForSSR } from '@nx/rspack/module-federation';
import baseConfig from './module-federation.server.config';
const defaultConfig = {
...baseConfig,
};
// Nx plugins for rspack to build config object from Nx options and context.
/**
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
* The DTS Plugin can be enabled by setting dts: true
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
*/
export default composePlugins(
withNx(),
withReact({ ssr: true }),
withModuleFederationForSSR(defaultConfig, { dts: false })
);
"
`;

exports[`remote generator bundler=rspack should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 2`] = `
"import { ModuleFederationConfig } from '@nx/rspack/module-federation';
const config: ModuleFederationConfig = {
name: 'test',
exposes: {
'./Module': './src/remote-entry.ts',
},
};
export default config;
"
`;
26 changes: 24 additions & 2 deletions packages/react/src/generators/remote/remote.rspack.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ jest.mock('@nx/devkit', () => {
};
});

// TODO(colum): turn these on when rspack is moved into the main repo
xdescribe('remote generator', () => {
describe('remote generator', () => {
// TODO(@jaysoo): Turn this back to adding the plugin
let originalEnv: string;

Expand Down Expand Up @@ -324,5 +323,28 @@ xdescribe('remote generator', () => {
})
).rejects.toThrowError(`Invalid remote name provided: ${name}.`);
});

it('should throw an error when an invalid remote name is used', async () => {
const tree = createTreeWithEmptyWorkspace();
await expect(
remote(tree, {
directory: 'test/my-app',
devServerPort: 4209,
e2eTestRunner: 'cypress',
linter: Linter.EsLint,
skipFormat: false,
style: 'css',
unitTestRunner: 'jest',
ssr: true,
typescriptConfiguration: true,
bundler: 'rspack',
})
).rejects.toMatchInlineSnapshot(`
[Error: Invalid remote name: my-app. Remote project names must:
- Start with a letter, dollar sign ($) or underscore (_)
- Followed by any valid character (letters, digits, underscores, or dollar signs)
The regular expression used is ^[a-zA-Z_$][a-zA-Z_$0-9]*$.]
`);
});
});
});
20 changes: 16 additions & 4 deletions packages/react/src/generators/remote/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
names,
readProjectConfiguration,
runTasksInSerial,
stripIndents,
Tree,
updateProjectConfiguration,
} from '@nx/devkit';
Expand All @@ -25,7 +26,10 @@ import { addRemoteToDynamicHost } from './lib/add-remote-to-dynamic-host';
import { addMfEnvToTargetDefaultInputs } from '../../utils/add-mf-env-to-inputs';
import { maybeJs } from '../../utils/maybe-js';
import { isValidVariable } from '@nx/js';
import { moduleFederationEnhancedVersion } from '../../utils/versions';
import {
moduleFederationEnhancedVersion,
nxVersion,
} from '../../utils/versions';
import { ensureProjectName } from '@nx/devkit/src/generators/project-name-and-root-utils';
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';

Expand Down Expand Up @@ -119,9 +123,14 @@ export async function remoteGenerator(host: Tree, schema: Schema) {
}

await ensureProjectName(host, options, 'application');
if (options.projectName.includes('-')) {
const REMOTE_NAME_REGEX = '^[a-zA-Z_$][a-zA-Z_$0-9]*$';
const remoteNameRegex = new RegExp(REMOTE_NAME_REGEX);
if (!remoteNameRegex.test(options.projectName)) {
throw new Error(
`Remote projects cannot contain '-' in their name. Invalid remote name: ${options.projectName}`
stripIndents`Invalid remote name: ${options.projectName}. Remote project names must:
- Start with a letter, dollar sign ($) or underscore (_)
- Followed by any valid character (letters, digits, underscores, or dollar signs)
The regular expression used is ${REMOTE_NAME_REGEX}.`
);
}
const initAppTask = await applicationGenerator(host, {
Expand Down Expand Up @@ -216,7 +225,10 @@ export async function remoteGenerator(host: Tree, schema: Schema) {
const installTask = addDependenciesToPackageJson(
host,
{},
{ '@module-federation/enhanced': moduleFederationEnhancedVersion }
{
'@module-federation/enhanced': moduleFederationEnhancedVersion,
'@nx/web': nxVersion,
}
);
tasks.push(installTask);

Expand Down

0 comments on commit fe5cdec

Please sign in to comment.