Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
350 lines
9.3 KiB
JavaScript
350 lines
9.3 KiB
JavaScript
/* eslint-disable no-console */
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import * as url from 'url';
|
|
|
|
const componentAreas = {
|
|
accordion: 'surfaces',
|
|
accordionactions: 'surfaces',
|
|
accordiondetails: 'surfaces',
|
|
accordionsummary: 'surfaces',
|
|
alert: 'feedback',
|
|
alerttitle: 'feedback',
|
|
appbar: 'surfaces',
|
|
aspectratio: 'dataDisplay',
|
|
autocomplete: 'inputs',
|
|
avatar: 'dataDisplay',
|
|
avatargroup: 'dataDisplay',
|
|
backdrop: 'feedback',
|
|
badge: 'dataDisplay',
|
|
bottomnavigation: 'navigation',
|
|
bottomnavigationaction: 'navigation',
|
|
box: 'layout',
|
|
breadcrumbs: 'navigation',
|
|
button: 'inputs',
|
|
buttonbase: 'inputs',
|
|
buttongroup: 'inputs',
|
|
card: 'surfaces',
|
|
cardactionarea: 'surfaces',
|
|
cardactions: 'surfaces',
|
|
cardcontent: 'surfaces',
|
|
cardcover: 'surfaces',
|
|
cardheader: 'surfaces',
|
|
cardmedia: 'surfaces',
|
|
cardoverflow: 'surfaces',
|
|
checkbox: 'inputs',
|
|
chip: 'dataDisplay',
|
|
chipdelete: 'dataDisplay',
|
|
circularprogress: 'feedback',
|
|
clickawaylistener: 'utils',
|
|
collapse: 'utils',
|
|
container: 'layout',
|
|
cssbaseline: 'utils',
|
|
dialog: 'feedback',
|
|
dialogactions: 'feedback',
|
|
dialogcontent: 'feedback',
|
|
dialogcontenttext: 'feedback',
|
|
dialogtitle: 'feedback',
|
|
divider: 'dataDisplay',
|
|
drawer: 'navigation',
|
|
fab: 'inputs',
|
|
fade: 'utils',
|
|
filledinput: 'inputs',
|
|
formcontrol: 'inputs',
|
|
formcontrollabel: 'inputs',
|
|
formgroup: 'inputs',
|
|
formhelpertext: 'inputs',
|
|
formlabel: 'inputs',
|
|
globalstyles: 'utils',
|
|
gridlegacy: 'layout',
|
|
grid: 'layout',
|
|
grow: 'utils',
|
|
hidden: 'layout',
|
|
icon: 'dataDisplay',
|
|
iconbutton: 'inputs',
|
|
imagelist: 'layout',
|
|
imagelistitem: 'layout',
|
|
imagelistitembar: 'layout',
|
|
input: 'inputs',
|
|
inputadornment: 'inputs',
|
|
inputbase: 'inputs',
|
|
inputlabel: 'inputs',
|
|
linearprogress: 'feedback',
|
|
link: 'navigation',
|
|
list: 'dataDisplay',
|
|
listbox: 'utils',
|
|
listdivider: 'dataDisplay',
|
|
listitem: 'dataDisplay',
|
|
listitemavatar: 'dataDisplay',
|
|
listitembutton: 'dataDisplay',
|
|
listitemcontent: 'dataDisplay',
|
|
listitemdecorator: 'dataDisplay',
|
|
listitemicon: 'dataDisplay',
|
|
listitemsecondaryaction: 'dataDisplay',
|
|
listitemtext: 'dataDisplay',
|
|
listsubheader: 'dataDisplay',
|
|
masonry: 'layout',
|
|
mediaquery: 'utils',
|
|
menu: 'navigation',
|
|
menuitem: 'navigation',
|
|
menulist: 'navigation',
|
|
mobilestepper: 'navigation',
|
|
modal: 'utils',
|
|
multiselect: 'inputs',
|
|
nativeselect: 'inputs',
|
|
nossr: 'utils',
|
|
option: 'inputs',
|
|
optiongroup: 'inputs',
|
|
outlinedinput: 'inputs',
|
|
pagination: 'navigation',
|
|
paginationitem: 'navigation',
|
|
paper: 'surfaces',
|
|
popover: 'utils',
|
|
popper: 'utils',
|
|
portal: 'utils',
|
|
progress: 'feedback',
|
|
radio: 'inputs',
|
|
radiogroup: 'inputs',
|
|
rating: 'inputs',
|
|
scopedcssbaseline: 'utils',
|
|
scrolltrigger: 'surfaces',
|
|
select: 'inputs',
|
|
sheet: 'surfaces',
|
|
skeleton: 'feedback',
|
|
slide: 'utils',
|
|
slider: 'inputs',
|
|
snackbar: 'feedback',
|
|
snackbarcontent: 'feedback',
|
|
speeddial: 'navigation',
|
|
speeddialaction: 'navigation',
|
|
speeddialicon: 'navigation',
|
|
stack: 'layout',
|
|
step: 'navigation',
|
|
stepbutton: 'navigation',
|
|
stepconnector: 'navigation',
|
|
stepcontent: 'navigation',
|
|
stepicon: 'navigation',
|
|
steplabel: 'navigation',
|
|
stepper: 'navigation',
|
|
svgicon: 'dataDisplay',
|
|
swipeabledrawer: 'navigation',
|
|
switch: 'inputs',
|
|
tab: 'navigation',
|
|
table: 'dataDisplay',
|
|
tablebody: 'dataDisplay',
|
|
tablecell: 'dataDisplay',
|
|
tablecontainer: 'dataDisplay',
|
|
tablefooter: 'dataDisplay',
|
|
tablehead: 'dataDisplay',
|
|
tablepagination: 'dataDisplay',
|
|
tablerow: 'dataDisplay',
|
|
tablesortlabel: 'dataDisplay',
|
|
tablist: 'navigation',
|
|
tabpanel: 'navigation',
|
|
tabs: 'navigation',
|
|
tabscrollbutton: 'navigation',
|
|
tabslist: 'navigation',
|
|
textarea: 'inputs',
|
|
textareaautosize: 'utils',
|
|
textfield: 'inputs',
|
|
timeline: 'dataDisplay',
|
|
togglebutton: 'inputs',
|
|
togglebuttongroup: 'inputs',
|
|
toolbar: 'surfaces',
|
|
tooltip: 'dataDisplay',
|
|
touchripple: 'inputs',
|
|
transferlist: 'inputs',
|
|
transitions: 'utils',
|
|
focustrap: 'utils',
|
|
treeview: 'dataDisplay',
|
|
typography: 'dataDisplay',
|
|
zoom: 'utils',
|
|
};
|
|
|
|
const areaMaintainers = {
|
|
inputs: ['michaldudak', 'mnajdova'],
|
|
dataDisplay: ['siriwatknp', 'michaldudak'],
|
|
feedback: ['siriwatknp', 'hbjORbj'],
|
|
surfaces: ['siriwatknp', 'hbjORbj'],
|
|
navigation: ['mnajdova', 'michaldudak'],
|
|
layout: ['siriwatknp', 'hbjORbj'],
|
|
utils: ['mnajdova', 'michaldudak'],
|
|
};
|
|
|
|
const packageOwners = {
|
|
base: ['michaldudak'],
|
|
joy: ['siriwatknp'],
|
|
material: ['mnajdova'],
|
|
};
|
|
|
|
const packageMaintainers = {
|
|
base: ['michaldudak', 'mnajdova'],
|
|
'icons-material': ['michaldudak', 'siriwatknp'],
|
|
joy: ['siriwatknp', 'danilo-leal'],
|
|
material: ['mnajdova', 'danilo-leal'],
|
|
system: ['mnajdova', 'siriwatknp'],
|
|
};
|
|
|
|
const additionalRules = {
|
|
'/scripts/': ['michaldudak', 'm4theushw'],
|
|
};
|
|
|
|
const thisDirectory = url.fileURLToPath(new URL('.', import.meta.url));
|
|
|
|
const buffer = [];
|
|
|
|
function write(text) {
|
|
buffer.push(text);
|
|
}
|
|
|
|
function save() {
|
|
const fileContents = [...buffer, ''].join('\n');
|
|
fs.writeFileSync(path.join(thisDirectory, '../.github/CODEOWNERS'), fileContents);
|
|
}
|
|
|
|
function findComponentArea(componentName) {
|
|
// TODO: could make it smarter to reduce the number of entries in componentAreas
|
|
// for example, "AccordionActions" could look up "Accordion"
|
|
return componentAreas[componentName];
|
|
}
|
|
|
|
function normalizeComponentName(componentName) {
|
|
// remove the "use" and "Unstable_" prefixes and "Unstyled" suffix
|
|
return componentName.replace(/^(use|Unstable_)?(.*?)(Unstyled)?$/gm, '$2').toLowerCase();
|
|
}
|
|
|
|
function normalizeDocsComponentName(componentName) {
|
|
switch (componentName) {
|
|
case 'breadcrumbs':
|
|
case 'progress':
|
|
case 'transitions':
|
|
return componentName;
|
|
|
|
case 'badges':
|
|
return 'badge';
|
|
|
|
case 'floating-action-button':
|
|
return 'fab';
|
|
|
|
case 'focus-trap':
|
|
return 'focustrap';
|
|
|
|
case 'radio-buttons':
|
|
return 'radio';
|
|
|
|
case 'tables':
|
|
return 'table';
|
|
|
|
default:
|
|
// remove the "use" and "Unstable" prefixes and remove the trailing "s" or "es" to make a singular form
|
|
return componentName
|
|
.replace(/^(use|Unstable)?(.*?)(es|s)?$/gm, '$2')
|
|
.replace(/-/g, '')
|
|
.toLowerCase();
|
|
}
|
|
}
|
|
|
|
function getCodeowners(mapping) {
|
|
return Object.entries(mapping)
|
|
.map(([directory, maintainers]) => `${directory} @${maintainers.join(' @')}`)
|
|
.join('\n');
|
|
}
|
|
|
|
function getAreaMaintainers(area, packageName) {
|
|
return Array.from(
|
|
new Set([
|
|
...areaMaintainers[area],
|
|
// Material UI package owner is not added to individual components' owners
|
|
// to reduce the number of PRs they'll get to review.
|
|
...(packageName === 'material' ? [] : packageOwners[packageName]),
|
|
]),
|
|
)
|
|
.map((name) => `@${name}`)
|
|
.join(' ');
|
|
}
|
|
|
|
function processComponents(packageName) {
|
|
const componentsDirectory = path.join(thisDirectory, `../packages/mui-${packageName}/src`);
|
|
const componentDirectories = fs.readdirSync(componentsDirectory);
|
|
const result = [];
|
|
|
|
for (const componentDirectory of componentDirectories) {
|
|
if (!fs.statSync(path.join(componentsDirectory, componentDirectory)).isDirectory()) {
|
|
continue;
|
|
}
|
|
|
|
const componentName = normalizeComponentName(componentDirectory);
|
|
const componentArea = findComponentArea(componentName);
|
|
|
|
if (componentArea) {
|
|
const maintainers = getAreaMaintainers(componentArea, packageName);
|
|
const codeowners = `/packages/mui-${packageName}/src/${componentDirectory}/ ${maintainers}`;
|
|
|
|
result.push(codeowners);
|
|
} else {
|
|
console.info(`No explicit owner defined for "${componentDirectory}" in ${packageName}.`);
|
|
}
|
|
}
|
|
|
|
return result.join('\n');
|
|
}
|
|
|
|
function processDocs(packageName) {
|
|
const docsDirectory = path.join(thisDirectory, `../docs/data/${packageName}/components`);
|
|
const componentDirectories = fs.readdirSync(docsDirectory);
|
|
const result = [];
|
|
|
|
for (const componentDirectory of componentDirectories) {
|
|
if (!fs.statSync(path.join(docsDirectory, componentDirectory)).isDirectory()) {
|
|
continue;
|
|
}
|
|
|
|
const componentName = normalizeDocsComponentName(componentDirectory);
|
|
const componentArea = findComponentArea(componentName);
|
|
|
|
if (componentArea) {
|
|
const maintainers = getAreaMaintainers(componentArea, packageName);
|
|
const codeowners = `/docs/data/${packageName}/components/${componentDirectory}/ ${maintainers}`;
|
|
result.push(codeowners);
|
|
} else {
|
|
console.info(
|
|
`No explicit owner defined for docs of "${componentDirectory}" in ${packageName}.`,
|
|
);
|
|
}
|
|
}
|
|
|
|
return result.join('\n');
|
|
}
|
|
|
|
function processPackages() {
|
|
return Object.entries(packageMaintainers)
|
|
.map(([packageName, maintainers]) => `/packages/mui-${packageName}/ @${maintainers.join(' @')}`)
|
|
.join('\n');
|
|
}
|
|
|
|
function run() {
|
|
write('# This file is auto-generated, do not modify it manually.');
|
|
write('# run `pnpm generate-codeowners` to update it.\n\n');
|
|
|
|
write(getCodeowners(additionalRules));
|
|
|
|
write('\n# Packages\n');
|
|
write(processPackages());
|
|
|
|
write('\n# Components - Material UI\n');
|
|
write(processComponents('material'));
|
|
write(processDocs('material'));
|
|
|
|
write('\n# Components - Base UI\n');
|
|
write(processComponents('base'));
|
|
write(processDocs('base'));
|
|
|
|
write('\n# Components - Joy UI\n');
|
|
write(processComponents('joy'));
|
|
write(processDocs('joy'));
|
|
|
|
save();
|
|
}
|
|
|
|
run();
|