Skip to content

Commit

Permalink
feat: generate combination packages
Browse files Browse the repository at this point in the history
instead of putting all common stuff in the main package, leading to it depending on xterm
  • Loading branch information
Loïc Mangeonjean committed Nov 6, 2024
1 parent cf73fa3 commit 2d4612f
Show file tree
Hide file tree
Showing 2 changed files with 305 additions and 37 deletions.
104 changes: 83 additions & 21 deletions rollup/rollup-metadata-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import { builtinModules } from 'module'

interface Group {
name: string
publicName?: string
modules: Set<string>
entrypoints: Set<string>
dependencies: Set<string>
groupDependencies: Set<string>
// A greater priority means the that this group will be chosen for module present in multiple groups
priority: number
isCombination: boolean
}

interface GroupResult {
name: string
directDependencies: Set<string>
exclusiveModules: Set<string>
entrypoints: Set<string>
isCombination: boolean
}

interface Options {
Expand All @@ -30,16 +33,25 @@ interface Options {
group: GroupResult
moduleGroupName: Map<string, string | undefined>
otherDependencies: Set<string>
otherModules: Set<string>
options: OutputOptions
bundle: OutputBundle
}
): void | Promise<void>
// Should shared modules be put in new combination groups
// By default, they are put in the group with the highest priority
generateCombinationGroups?: boolean
getCombinedGroup?: (names: string[]) => { name: string; publicName?: string }
minCompinedGroupSize?: number
}

export default ({
handle,
getGroup = () => ({ name: 'main' }),
stage = 'generateBundle'
stage = 'generateBundle',
generateCombinationGroups = false,
getCombinedGroup = (names) => ({ name: names.reduce((a, b) => `${a}_${b}`) }),
minCompinedGroupSize = 10
}: Options): Plugin => ({
name: 'generate-metadata',
[stage]: async function (this: PluginContext, options: OutputOptions, bundle: OutputBundle) {
Expand Down Expand Up @@ -104,11 +116,13 @@ export default ({
if (!groups.has(groupName)) {
groups.set(groupName, {
entrypoints: new Set<string>(),
publicName,
name: groupName,
modules: new Set<string>(),
dependencies: new Set(),
groupDependencies: new Set(),
priority: priority ?? 0
priority: priority ?? 0,
isCombination: false
})
}
const group = groups.get(groupName)!
Expand All @@ -121,14 +135,6 @@ export default ({
}
}

for (const group of groups.values()) {
group.groupDependencies = new Set(
Array.from(group.dependencies)
.map((d) => groupByPublicName.get(d)?.name)
.filter((g): g is string => g != null)
)
}

const moduleGroups = new Map<string, Group[]>()
for (const [_, group] of groups.entries()) {
for (const module of group.modules) {
Expand All @@ -140,18 +146,68 @@ export default ({
}

const moduleGroup = new Map<string, Group | null>()
for (const [id, groups] of moduleGroups.entries()) {
const combinedModuleGroup = new Map<string, Group>()
for (const [id, currentModuleGroups] of moduleGroups.entries()) {
// Find a group that everyone depends on
const greatestPriority = Math.max(...groups.map((g) => g.priority))
const priorityGroups = groups.filter((g) => g.priority >= greatestPriority)
moduleGroup.set(
id,
priorityGroups.find((group) =>
priorityGroups
.filter((ogroup) => ogroup !== group)
.every((ogroup) => ogroup.groupDependencies.has(group.name))
) ?? null
const greatestPriority = Math.max(...currentModuleGroups.map((g) => g.priority))
const priorityGroups = currentModuleGroups.filter((g) => g.priority >= greatestPriority)

const groupThatEveryOneDependsOn = priorityGroups.find((group) =>
priorityGroups
.filter((ogroup) => ogroup !== group)
.every((ogroup) => ogroup.groupDependencies.has(group.name))
)
moduleGroup.set(id, groupThatEveryOneDependsOn ?? null)

if (generateCombinationGroups && groupThatEveryOneDependsOn == null) {
const newGroup = getCombinedGroup(priorityGroups.map((g) => g.name))
let group = groups.get(newGroup.name)
if (group == null) {
// The combination group doesn't exists yet
group = {
entrypoints: new Set<string>(),
name: newGroup.name,
publicName: newGroup.publicName,
modules: new Set<string>(),
dependencies: new Set(),
groupDependencies: new Set(),
priority: 0,
isCombination: true
}
groups.set(newGroup.name, group)
if (newGroup.publicName != null) {
groupByPublicName.set(newGroup.publicName, group)
}
}
group.modules.add(id)
combinedModuleGroup.set(id, group)
}
}

for (const group of groups.values()) {
group.groupDependencies = new Set(
Array.from(group.dependencies)
.map((d) => groupByPublicName.get(d)?.name)
.filter((g): g is string => g != null)
)
}

for (const [id, group] of combinedModuleGroup) {
if (group.modules.size < minCompinedGroupSize) {
// if the combined group is too small and if it doesn't have direct dependencies, remove it
groups.delete(group.name)
} else {
moduleGroup.set(id, group)

if (group.publicName != null) {
const previousGroups = moduleGroups.get(id)
if (previousGroups != null) {
for (const previousGroup of previousGroups) {
previousGroup.dependencies.add(group.publicName)
}
}
}
}
}

const moduleGroupName = new Map(
Expand All @@ -172,7 +228,8 @@ export default ({
directDependencies,
entrypoints: group.entrypoints,
exclusiveModules,
name
name,
isCombination: group.isCombination
}
})

Expand All @@ -181,10 +238,14 @@ export default ({
.map((set) => Array.from(set))
.flat()
)
const otherModules = new Set(Array.from(moduleGroups.keys()))
for (const group of groupResults) {
for (const directDependency of group.directDependencies) {
otherDependencies.delete(directDependency)
}
for (const exclusiveModule of group.exclusiveModules) {
otherModules.delete(exclusiveModule)
}
}

await Promise.all(
Expand All @@ -193,6 +254,7 @@ export default ({
group,
moduleGroupName,
otherDependencies,
otherModules,
options,
bundle
})
Expand Down
Loading

0 comments on commit 2d4612f

Please sign in to comment.