init project
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
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled

This commit is contained in:
how2ice
2025-12-12 14:26:25 +09:00
commit 005cf56baf
43188 changed files with 1079531 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
export { projectSettings as joyUiProjectSettings } from './joyUi/projectSettings';
export { projectSettings as materialUiProjectSettings } from './materialUi/projectSettings';
export { projectSettings as muiSystemProjectSettings } from './muiSystem/projectSettings';

View File

@@ -0,0 +1,69 @@
import fs from 'fs';
import path from 'path';
import { kebabCase } from 'es-toolkit/string';
import { getHeaders, getTitle } from '@mui/internal-markdown';
import {
ComponentInfo,
extractPackageFile,
fixPathname,
getMuiName,
getSystemComponents,
parseFile,
} from '@mui-internal/api-docs-builder/buildApiUtils';
import findPagesMarkdown from '@mui-internal/api-docs-builder/utils/findPagesMarkdown';
export function getJoyUiComponentInfo(filename: string): ComponentInfo {
const { name } = extractPackageFile(filename);
let srcInfo: null | ReturnType<ComponentInfo['readFile']> = null;
if (!name) {
throw new Error(`Could not find the component name from: ${filename}`);
}
return {
filename,
name,
muiName: getMuiName(name),
apiPathname: `/joy-ui/api/${kebabCase(name)}/`,
apiPagesDirectory: path.join(process.cwd(), `docs/pages/joy-ui/api`),
isSystemComponent: getSystemComponents().includes(name),
readFile: () => {
srcInfo = parseFile(filename);
return srcInfo;
},
getInheritance: (inheritedComponent = srcInfo?.inheritedComponent) => {
if (!inheritedComponent || inheritedComponent.match(/unstyled/i)) {
return null;
}
const urlComponentName = kebabCase(inheritedComponent.replace(/unstyled/i, ''));
// `inheritedComponent` node is coming from test files.
// `inheritedComponent` must include `Unstyled` suffix for parser to recognise that the component inherits Base UI component
// e.g., Joy UI Menu inherits Base UI Popper, and its test file uses the name `PopperUnstyled` so that we can recognise here that
// Joy UI Menu is inheriting a base component. In terms of documentation, we should no longer use the name `PopperUnstyled`, and hence
// we remove the suffix here.
return {
name: inheritedComponent,
apiPathname: `/joy-ui/api/${urlComponentName}/`,
};
},
getDemos: () => {
const allMarkdowns = findPagesMarkdown().map((markdown) => {
const markdownContent = fs.readFileSync(markdown.filename, 'utf8');
const markdownHeaders = getHeaders(markdownContent);
return {
...markdown,
markdownContent,
components: markdownHeaders.components,
};
});
return allMarkdowns
.filter((page) => page.pathname.startsWith('/joy') && page.components.includes(name))
.map((page) => ({
filePath: page.filename, // pathname of the markdown file
demoPageTitle: getTitle(page.markdownContent),
demoPathname: fixPathname(page.pathname),
}));
},
};
}

View File

@@ -0,0 +1,34 @@
import path from 'path';
import { LANGUAGES } from 'docs/config';
import { ProjectSettings } from '@mui-internal/api-docs-builder';
import findApiPages from '@mui-internal/api-docs-builder/utils/findApiPages';
import generateUtilityClass, { isGlobalState } from '@mui/utils/generateUtilityClass';
import { getJoyUiComponentInfo } from './getJoyUiComponentInfo';
export const projectSettings: ProjectSettings = {
output: {
apiManifestPath: path.join(process.cwd(), 'docs/data/joy/pagesApi.js'),
},
typeScriptProjects: [
{
name: 'joy',
rootPath: path.join(process.cwd(), 'packages/mui-joy'),
entryPointPath: 'src/index.ts',
},
],
getApiPages: () => findApiPages('docs/pages/joy-ui/api'),
getComponentInfo: getJoyUiComponentInfo,
translationLanguages: LANGUAGES,
skipComponent(filename: string) {
// Container's demo isn't ready
// GlobalStyles's demo isn't ready
return (
filename.match(
/(ThemeProvider|CssVarsProvider|Container|ColorInversion|GlobalStyles|InitColorSchemeScript)/,
) !== null
);
},
translationPagesDirectory: 'docs/translations/api-docs-joy',
generateClassName: generateUtilityClass,
isGlobalClassName: isGlobalState,
};

View File

@@ -0,0 +1,49 @@
import path from 'path';
import fs from 'fs';
import { expect } from 'chai';
import sinon from 'sinon';
import { getMaterialUiComponentInfo } from './getMaterialUiComponentInfo';
describe('getMaterialUiComponentInfo', () => {
it('return correct info for material component file', () => {
const componentInfo = getMaterialUiComponentInfo(
path.join(process.cwd(), `/packages/mui-material/src/Button/Button.js`),
);
sinon.assert.match(componentInfo, {
name: 'Button',
apiPathname: '/material-ui/api/button/',
muiName: 'MuiButton',
apiPagesDirectory: sinon.match((value) =>
value.endsWith(path.join('docs', 'pages', 'material-ui', 'api')),
),
});
expect(componentInfo.getInheritance('ButtonBase')).to.deep.equal({
name: 'ButtonBase',
apiPathname: '/material-ui/api/button-base/',
});
let existed = false;
try {
fs.readdirSync(path.join(process.cwd(), 'docs/data'));
existed = true;
// eslint-disable-next-line no-empty
} catch (error) {}
if (existed) {
const demos = componentInfo.getDemos();
expect(demos).to.not.have.lengthOf(0);
expect(demos[0]).to.deep.include({
demoPageTitle: 'Button Group',
demoPathname: '/material-ui/react-button-group/',
});
expect(demos[0].filePath).to.include('button-group/button-group.md');
expect(demos[1]).to.deep.include({
demoPageTitle: 'Button',
demoPathname: '/material-ui/react-button/',
});
expect(demos[1].filePath).to.include('buttons/buttons.md');
}
});
});

View File

@@ -0,0 +1,71 @@
import fs from 'fs';
import path from 'path';
import { kebabCase } from 'es-toolkit/string';
import { getHeaders, getTitle } from '@mui/internal-markdown';
import {
ComponentInfo,
extractPackageFile,
fixPathname,
getMuiName,
getSystemComponents,
parseFile,
} from '@mui-internal/api-docs-builder/buildApiUtils';
import findPagesMarkdown from '@mui-internal/api-docs-builder/utils/findPagesMarkdown';
export function getMaterialUiComponentInfo(filename: string): ComponentInfo {
const { name } = extractPackageFile(filename);
let srcInfo: null | ReturnType<ComponentInfo['readFile']> = null;
if (!name) {
throw new Error(`Could not find the component name from: ${filename}`);
}
return {
filename,
name,
muiName: getMuiName(name),
apiPathname: `/material-ui/api/${kebabCase(name)}/`,
apiPagesDirectory: path.join(process.cwd(), `docs/pages/material-ui/api`),
isSystemComponent: getSystemComponents().includes(name),
readFile: () => {
srcInfo = parseFile(filename);
return srcInfo;
},
getInheritance: (inheritedComponent = srcInfo?.inheritedComponent) => {
if (!inheritedComponent) {
return null;
}
// `inheritedComponent` node is coming from test files.
// `inheritedComponent` must include `Unstyled` suffix for parser to recognise that the component inherits Base UI component
// e.g., Joy Menu inherits Base UI Popper, and its test file uses the name `PopperUnstyled` so that we can recognise here that
// Joy Menu is inheriting a base component. In terms of documentation, we should no longer use the name `PopperUnstyled`, and hence
// we remove the suffix here.
return {
name: inheritedComponent.replace(/unstyled/i, ''),
apiPathname:
inheritedComponent === 'Transition'
? 'https://reactcommunity.org/react-transition-group/transition/#Transition-props'
: `/${
inheritedComponent.match(/unstyled/i) ? 'base-ui' : 'material-ui'
}/api/${kebabCase(inheritedComponent.replace(/unstyled/i, ''))}/`,
};
},
getDemos: () => {
const allMarkdowns = findPagesMarkdown().map((markdown) => {
const markdownContent = fs.readFileSync(markdown.filename, 'utf8');
const markdownHeaders = getHeaders(markdownContent);
return {
...markdown,
markdownContent,
components: markdownHeaders.components,
};
});
return allMarkdowns
.filter((page) => page.pathname.startsWith('/material') && page.components.includes(name))
.map((page) => ({
filePath: page.filename, // pathname of the markdown file
demoPageTitle: getTitle(page.markdownContent),
demoPathname: fixPathname(page.pathname),
}));
},
};
}

View File

@@ -0,0 +1,58 @@
import path from 'path';
import { LANGUAGES } from 'docs/config';
import { ProjectSettings } from '@mui-internal/api-docs-builder';
import findApiPages from '@mui-internal/api-docs-builder/utils/findApiPages';
import generateUtilityClass, { isGlobalState } from '@mui/utils/generateUtilityClass';
import { getMaterialUiComponentInfo } from './getMaterialUiComponentInfo';
const generateClassName = (componentName: string, slot: string, globalStatePrefix = 'Mui') => {
if (componentName === 'MuiSwipeableDrawer') {
// SwipeableDrawer uses Drawer classes without modifying them
return generateUtilityClass('MuiDrawer', slot, globalStatePrefix);
}
return generateUtilityClass(componentName, slot, globalStatePrefix);
};
export const projectSettings: ProjectSettings = {
output: {
apiManifestPath: path.join(process.cwd(), 'docs/data/material/pagesApi.js'),
},
typeScriptProjects: [
{
name: 'material',
rootPath: path.join(process.cwd(), 'packages/mui-material'),
entryPointPath: [
'src/index.d.ts',
'src/PigmentStack/PigmentStack.tsx',
'src/PigmentContainer/PigmentContainer.tsx',
'src/PigmentGrid/PigmentGrid.tsx',
],
},
{
name: 'lab',
rootPath: path.join(process.cwd(), 'packages/mui-lab'),
entryPointPath: 'src/index.d.ts',
},
],
getApiPages: () => findApiPages('docs/pages/material-ui/api'),
getComponentInfo: getMaterialUiComponentInfo,
translationLanguages: LANGUAGES,
skipComponent(filename: string) {
return filename.match(/(ThemeProvider|CssVarsProvider|DefaultPropsProvider)/) !== null;
},
translationPagesDirectory: 'docs/translations/api-docs',
generateClassName,
isGlobalClassName: isGlobalState,
// #host-reference
baseApiUrl: 'https://mui.com',
pagesManifestPath: path.join(process.cwd(), 'docs/data/material/pages.ts'),
nonComponentFolders: [
'material/getting-started',
'material/customization',
'material/experimental-api',
'material/guides',
'material/integrations',
'material/migration',
],
};

View File

@@ -0,0 +1,108 @@
import fs from 'fs';
import path from 'path';
import { kebabCase } from 'es-toolkit/string';
import { getHeaders, getTitle } from '@mui/internal-markdown';
import {
ComponentInfo,
extractPackageFile,
getMuiName,
parseFile,
fixPathname,
} from '@mui-internal/api-docs-builder/buildApiUtils';
import findPagesMarkdown from '@mui-internal/api-docs-builder/utils/findPagesMarkdown';
const migratedBaseComponents = [
'Badge',
'Button',
'ClickAwayListener',
'FocusTrap',
'Input',
'MenuItem',
'Menu',
'Modal',
'NoSsr',
'OptionGroup',
'Option',
'Popper',
'Portal',
'Select',
'Slider',
'Switch',
'TablePagination',
'TabPanel',
'TabsList',
'Tabs',
'Tab',
];
export function getSystemComponentInfo(filename: string): ComponentInfo {
const { name } = extractPackageFile(filename);
let srcInfo: null | ReturnType<ComponentInfo['readFile']> = null;
if (!name) {
throw new Error(`Could not find the component name from: ${filename}`);
}
return {
filename,
name,
muiName: getMuiName(name),
apiPathname: `/system/api/${kebabCase(name)}/`,
apiPagesDirectory: path.join(process.cwd(), `docs/pages/system/api`),
isSystemComponent: true,
readFile: () => {
srcInfo = parseFile(filename);
return srcInfo;
},
getInheritance() {
return null;
},
getDemos: () => {
const allMarkdowns = findPagesMarkdown()
.filter((markdown) => {
if (migratedBaseComponents.some((component) => filename.includes(component))) {
return markdown.filename.match(/[\\/]data[\\/]system[\\/]/);
}
return true;
})
.map((markdown) => {
const markdownContent = fs.readFileSync(markdown.filename, 'utf8');
const markdownHeaders = getHeaders(markdownContent);
return {
...markdown,
markdownContent,
components: markdownHeaders.components,
};
});
return allMarkdowns
.filter((page) => page.components.includes(name))
.map((page) => ({
filePath: page.filename, // pathname of the markdown file
demoPageTitle: pathToSystemTitle({
...page,
title: getTitle(page.markdownContent),
}),
demoPathname: fixPathname(page.pathname),
}));
},
};
}
interface PageMarkdown {
pathname: string;
title: string;
components: readonly string[];
}
function pathToSystemTitle(page: PageMarkdown) {
const defaultTitle = page.title;
if (page.pathname.startsWith('/material')) {
return `${defaultTitle} (Material UI)`;
}
if (page.pathname.startsWith('/system')) {
return `${defaultTitle} (MUI System)`;
}
if (page.pathname.startsWith('/joy')) {
return `${defaultTitle} (Joy UI)`;
}
return defaultTitle;
}

View File

@@ -0,0 +1,32 @@
import path from 'path';
import { LANGUAGES } from 'docs/config';
import { ProjectSettings } from '@mui-internal/api-docs-builder';
import findApiPages from '@mui-internal/api-docs-builder/utils/findApiPages';
import generateUtilityClass, { isGlobalState } from '@mui/utils/generateUtilityClass';
import { getSystemComponentInfo } from './getSystemComponentInfo';
export const projectSettings: ProjectSettings = {
output: {
apiManifestPath: path.join(process.cwd(), 'docs/data/system/pagesApi.js'),
},
typeScriptProjects: [
{
name: 'system',
rootPath: path.join(process.cwd(), 'packages/mui-system'),
entryPointPath: 'src/index.d.ts',
},
],
getApiPages: () => findApiPages('docs/pages/system/api'),
getComponentInfo: getSystemComponentInfo,
translationLanguages: LANGUAGES,
skipComponent(filename) {
return (
filename.match(
/(ThemeProvider|CssVarsProvider|DefaultPropsProvider|GlobalStyles|InitColorSchemeScript)/,
) !== null
);
},
translationPagesDirectory: 'docs/translations/api-docs',
generateClassName: generateUtilityClass,
isGlobalClassName: isGlobalState,
};

View File

@@ -0,0 +1,30 @@
{
"name": "@mui-internal/api-docs-builder-core",
"version": "1.0.0",
"description": "MUI Core-specific settings for API docs generator",
"private": "true",
"main": "./index.ts",
"scripts": {
"test": "pnpm --workspace-root test:unit --project \"*:@mui-internal/api-docs-builder-core\"",
"typescript": "tsc -p tsconfig.json"
},
"repository": {
"type": "git",
"url": "git+https://github.com/mui/material-ui.git",
"directory": "packages/api-docs-builder-core"
},
"dependencies": {
"@mui-internal/api-docs-builder": "workspace:^",
"@mui/internal-markdown": "workspace:^",
"docs": "workspace:^",
"es-toolkit": "^1.42.0"
},
"devDependencies": {
"@types/chai": "^5.2.3",
"@types/node": "^20.19.25",
"@types/sinon": "^17.0.4",
"chai": "^6.0.1",
"sinon": "^21.0.0",
"typescript": "^5.9.2"
}
}

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["node", "vitest/globals"]
},
"include": ["./**/*.ts", "./**/*.js"],
"exclude": ["node_modules", "vitest.config.ts"]
}

View File

@@ -0,0 +1,5 @@
// eslint-disable-next-line import/no-relative-packages
import sharedConfig from '../../vitest.shared.mts';
// eslint-disable-next-line import/no-default-export
export default sharedConfig(import.meta.url);