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

feat(enhanced): layers support #3241

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5b107fa
feat(enhanced): support layers
ScriptedAlchemy Nov 17, 2024
062706f
feat(enhanced): support layers
ScriptedAlchemy Nov 17, 2024
cfd9d01
feat(enhanced): layers for consume shared module
ScriptedAlchemy Nov 21, 2024
3e59c55
Merge branch 'main' into layers-support
ScriptedAlchemy Nov 21, 2024
536b110
Merge branch 'main' into layers-support
ScriptedAlchemy Nov 21, 2024
aed08e9
feat(enhanced): add issuerLayer support to consume shared
ScriptedAlchemy Nov 22, 2024
9fb164b
chore(enhanced): update test
ScriptedAlchemy Nov 25, 2024
a9d899c
chore(enhanced): update test
ScriptedAlchemy Nov 25, 2024
33a36d1
feat(enhanced): ConsumeSharedPlugin issuerLayer support
ScriptedAlchemy Nov 25, 2024
f0c5c50
chore: update tests for layer combos
ScriptedAlchemy Nov 25, 2024
578adbb
chore: update tests for layer combos
ScriptedAlchemy Nov 25, 2024
31f08d0
chore: update tests for layer combos
ScriptedAlchemy Nov 25, 2024
066ffa6
feat(enhanced): support direct layer
ScriptedAlchemy Nov 25, 2024
b70eb8d
Merge branch 'main' into layers-support
ScriptedAlchemy Nov 25, 2024
358ba00
fix(enhanced): update share options of share plugin
ScriptedAlchemy Nov 25, 2024
94d72ed
fix(enhanced): update share options of share plugin
ScriptedAlchemy Nov 25, 2024
91dbb12
chore(enhanced): refactor layers tests
ScriptedAlchemy Nov 25, 2024
514cf03
chore(enhanced): remove layer options from provider
ScriptedAlchemy Nov 25, 2024
be6c95c
Merge branch 'main' into layers-support
ScriptedAlchemy Nov 25, 2024
5308991
Merge branch 'main' into layers-support
ScriptedAlchemy Nov 26, 2024
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
1 change: 1 addition & 0 deletions .cursorrules
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# PersonaYou are a senior full-stack developer. One of those rare 10x developers that has incredible knowledge.# Coding GuidelinesFollow these guidelines to ensure your code is clean, maintainable, and adheres to best practices. Remember, less code is better. Lines of code = Debt.# Key Mindsets**1** **Simplicity**: Write simple and straightforward code.**2** **Readability**: Ensure your code is easy to read and understand.**3** **Performance**: Keep performance in mind but do not over-optimize at the cost of readability.**4** **Maintainability**: Write code that is easy to maintain and update.**5** **Testability**: Ensure your code is easy to test.**6** **Reusability**: Write reusable components and functions.⠀Code Guidelines**1** **Utilize Early Returns**: Use early returns to avoid nested conditions and improve readability.**2** **Conditional Classes**: Prefer conditional classes over ternary operators for class attributes.**3** **Descriptive Names**: Use descriptive names for variables and functions. Prefix event handler functions with "handle" (e.g., handleClick, handleKeyDown).**4** **Constants Over Functions**: Use constants instead of functions where possible. Define types if applicable.**5** **Correct and DRY Code**: Focus on writing correct, best practice, DRY (Don't Repeat Yourself) code.**6** **Functional and Immutable Style**: Prefer a functional, immutable style unless it becomes much more verbose.**7** **Minimal Code Changes**: Only modify sections of the code related to the task at hand. Avoid modifying unrelated pieces of code. Accomplish goals with minimal code changes.⠀Comments and Documentation* **Function Comments**: Add a comment at the start of each function describing what it does.* **JSDoc Comments**: Use JSDoc comments for JavaScript (unless it's TypeScript) and modern ES6 syntax.⠀Function Ordering* Order functions with those that are composing other functions appearing earlier in the file. For example, if you have a menu with multiple buttons, define the menu function above the buttons.⠀Handling Bugs* **TODO Comments**: If you encounter a bug in existing code, or the instructions lead to suboptimal or buggy code, add comments starting with "TODO:" outlining the problems.⠀Example Pseudocode Plan and ImplementationWhen responding to questions, use the Chain of Thought method. Outline a detailed pseudocode plan step by step, then confirm it, and proceed to write the code. Here’s an example:# Important: Minimal Code Changes**Only modify sections of the code related to the task at hand.****Avoid modifying unrelated pieces of code.****Avoid changing existing comments.****Avoid any kind of cleanup unless specifically instructed to.****Accomplish the goal with the minimum amount of code changes.****Code change = potential for bugs and technical debt.**Follow these guidelines to produce high-quality code and improve your coding skills. If you have any questions or need clarification, don’t hesitate to ask!
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@
"vue-tsc": "^2.0.26",
"wait-on": "^7.2.0",
"webpack": "5.93.0",
"webpack-cli": "^5.1.4",
"webpack-virtual-modules": "0.6.2",
"whatwg-fetch": "^3.6.20",
"yargs": "^17.7.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,12 @@ export interface ConsumesConfig {
* Do not accept shared module if version is not valid (defaults to yes, if local fallback module is available and shared module is not a singleton, otherwise no, has no effect if there is no required version specified).
*/
strictVersion?: boolean;
/**
* Share a specific layer of the module, if the module supports layers.
*/
layer?: string;
/**
* Issuer layer for the shared module.
*/
issuerLayer?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,8 @@ export interface SharedConfig {
* Version of the provided module. Will replace lower matching versions, but not higher.
*/
version?: false | string;
/**
* Share a specific layer of the module, if the module supports layers.
*/
layer?: string;
}
26 changes: 23 additions & 3 deletions packages/enhanced/src/lib/sharing/ConsumeSharedModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ export type ConsumeOptions = {
* include the fallback module in a sync way
*/
eager: boolean;
/**
* Share a specific layer of the module, if the module supports layers.
*/
layer?: string | null;
/**
* Issuer layer in which the module should be resolved
*/
issuerLayer?: string | null;
};

/**
Expand All @@ -91,6 +99,8 @@ export type ConsumeOptions = {
* @property {boolean} strictVersion don't use shared version even if version isn't valid
* @property {boolean} singleton use single global version
* @property {boolean} eager include the fallback module in a sync way
* @property {string | null=} layer Share a specific layer of the module, if the module supports layers
* @property {string | null=} issuerLayer Issuer layer in which the module should be resolved
*/

const TYPES = new Set(['consume-shared']);
Expand All @@ -103,7 +113,11 @@ class ConsumeSharedModule extends Module {
* @param {ConsumeOptions} options consume options
*/
constructor(context: string, options: ConsumeOptions) {
super(WEBPACK_MODULE_TYPE_CONSUME_SHARED_MODULE, context);
super(
WEBPACK_MODULE_TYPE_CONSUME_SHARED_MODULE,
context,
options.layer ?? undefined,
);
this.options = options;
}

Expand All @@ -119,10 +133,12 @@ class ConsumeSharedModule extends Module {
strictVersion,
singleton,
eager,
layer,
issuerLayer,
} = this.options;
return `${WEBPACK_MODULE_TYPE_CONSUME_SHARED_MODULE}|${shareScope}|${shareKey}|${
requiredVersion && rangeToString(requiredVersion)
}|${strictVersion}|${importResolved}|${singleton}|${eager}`;
}|${strictVersion}|${importResolved}|${singleton}|${eager}|${layer}|${issuerLayer}`;
}

/**
Expand All @@ -138,14 +154,18 @@ class ConsumeSharedModule extends Module {
strictVersion,
singleton,
eager,
layer,
issuerLayer,
} = this.options;
return `consume shared module (${shareScope}) ${shareKey}@${
requiredVersion ? rangeToString(requiredVersion) : '*'
}${strictVersion ? ' (strict)' : ''}${singleton ? ' (singleton)' : ''}${
importResolved
? ` (fallback: ${requestShortener.shorten(importResolved)})`
: ''
}${eager ? ' (eager)' : ''}`;
}${eager ? ' (eager)' : ''}${layer ? ` (${layer})` : ''}${
issuerLayer ? ` (issuer: ${issuerLayer})` : ''
}`;
}

/**
Expand Down
39 changes: 23 additions & 16 deletions packages/enhanced/src/lib/sharing/ConsumeSharedPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
normalizeWebpackPath,
} from '@module-federation/sdk/normalize-webpack-path';
import { isRequiredVersion } from '@module-federation/sdk';
import type { Compiler, Compilation } from 'webpack';
import type { Compiler, Compilation, Module } from 'webpack';
import { parseOptions } from '../container/options';
import { ConsumeOptions } from './ConsumeSharedModule';
import { ConsumeSharedPluginOptions } from '../../declarations/plugins/sharing/ConsumeSharedPlugin';
Expand All @@ -29,6 +29,7 @@ import ProvideForSharedDependency from './ProvideForSharedDependency';
import FederationRuntimePlugin from '../container/runtime/FederationRuntimePlugin';
import ShareRuntimeModule from './ShareRuntimeModule';
import type { SemVerRange } from 'webpack/lib/util/semver';
import type { ResolveData } from 'webpack/lib/NormalModuleFactory';

const ModuleNotFoundError = require(
normalizeWebpackPath('webpack/lib/ModuleNotFoundError'),
Expand All @@ -48,17 +49,8 @@ const createSchemaValidation = require(

const validate = createSchemaValidation(
//eslint-disable-next-line
require(
normalizeWebpackPath(
'webpack/schemas/plugins/sharing/ConsumeSharedPlugin.check.js',
),
),
() =>
require(
normalizeWebpackPath(
'webpack/schemas/plugins/sharing/ConsumeSharedPlugin.json',
),
),
require('../../schemas/sharing/ConsumeSharedPlugin.check.js'),
() => require('../../schemas/sharing/ConsumeSharedPlugin'),
{
name: 'Consume Shared Plugin',
baseDataPath: 'options',
Expand Down Expand Up @@ -94,6 +86,8 @@ class ConsumeSharedPlugin {
strictVersion: false,
singleton: false,
eager: false,
layer: undefined,
issuerLayer: undefined,
}
: // key is a request/key
// item is a version
Expand All @@ -107,6 +101,8 @@ class ConsumeSharedPlugin {
packageName: undefined,
singleton: false,
eager: false,
layer: undefined,
issuerLayer: undefined,
};
return result;
},
Expand All @@ -124,6 +120,7 @@ class ConsumeSharedPlugin {
packageName: item.packageName,
singleton: !!item.singleton,
eager: !!item.eager,
issuerLayer: item.issuerLayer ? item.issuerLayer : undefined,
}),
);
}
Expand Down Expand Up @@ -296,18 +293,26 @@ class ConsumeSharedPlugin {

normalModuleFactory.hooks.factorize.tapPromise(
PLUGIN_NAME,
({ context, request, dependencies }) =>
async (resolveData: ResolveData): Promise<Module | undefined> => {
const { context, request, dependencies, contextInfo } = resolveData;
// wait for resolving to be complete
//@ts-ignore
promise.then(() => {
return promise.then(() => {
if (
dependencies[0] instanceof ConsumeSharedFallbackDependency ||
dependencies[0] instanceof ProvideForSharedDependency
) {
return;
}
const match = unresolvedConsumes.get(request);

const match = unresolvedConsumes.get(
contextInfo.issuerLayer
? contextInfo.issuerLayer + request
: request,
);

if (match !== undefined) {
debugger;
return createConsumeSharedModule(context, request, match);
}
for (const [prefix, options] of prefixedConsumes) {
Expand All @@ -322,7 +327,8 @@ class ConsumeSharedPlugin {
});
}
}
}),
});
},
);
normalModuleFactory.hooks.createModule.tapPromise(
PLUGIN_NAME,
Expand All @@ -336,6 +342,7 @@ class ConsumeSharedPlugin {
if (resource) {
const options = resolvedConsumes.get(resource);
if (options !== undefined) {
//@ts-ignore
return createConsumeSharedModule(context, resource, options);
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/enhanced/src/lib/sharing/ProvideSharedModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class ProvideSharedModule extends Module {
* @returns {string | null} an identifier for library inclusion
*/
override libIdent(options: LibIdentOptions): string | null {
// debugger;
return `${this.layer ? `(${this.layer})/` : ''}webpack/sharing/provide/${
this._shareScope
}/${this._name}`;
Expand Down
2 changes: 2 additions & 0 deletions packages/enhanced/src/lib/sharing/SharePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class SharePlugin {
singleton: options.singleton,
packageName: options.packageName,
eager: options.eager,
layer: options.layer,
},
}),
);
Expand All @@ -66,6 +67,7 @@ class SharePlugin {
requiredVersion: options.requiredVersion,
strictVersion: options.strictVersion,
singleton: options.singleton,
layer: options.layer,
},
}));
//@ts-ignore
Expand Down
13 changes: 11 additions & 2 deletions packages/enhanced/src/lib/sharing/resolveMatchedConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export async function resolveMatchedConfigs<T>(
// @ts-ignore
const resolver = compilation.resolverFactory.get('normal', RESOLVE_OPTIONS);
const context = compilation.compiler.context;

await Promise.all(
//@ts-ignore
configs.map(([request, config]) => {
Expand Down Expand Up @@ -73,8 +72,18 @@ export async function resolveMatchedConfigs<T>(
// module request prefix
prefixed.set(request, config);
} else {
let req = request;
//@ts-ignore
if ('import' in config && config.import && request !== config.import) {
req = config.import as string;
}
//@ts-ignore
if ('issuerLayer' in config && config.issuerLayer) {
//@ts-ignore
req = config.issuerLayer + req;
}
// module request
unresolved.set(request, config);
unresolved.set(req, config);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here i can construct contextInfo.issuerLayer from parent module - but we do not know layer it would become because this takes place in factorize of the module iteself, so i can make keys of ( issuerLayer )+ request. like (loader-layer)react as key.

then anything with issuerLayer of that, react request will match the request - which i think is correct use case here

}
}),
);
Expand Down
Loading
Loading