Skip to content

Commit

Permalink
[core] Deeper import of modules
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari committed Sep 4, 2023
1 parent ee23000 commit 833cd63
Show file tree
Hide file tree
Showing 15 changed files with 107 additions and 89 deletions.
9 changes: 1 addition & 8 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,6 @@ module.exports = {
{
patterns: [
'@mui/*/*/*',
// Begin block: Packages with files instead of packages in the top level
// Importing from the top level pulls in CommonJS instead of ES modules
// Allowing /icons as to reduce cold-start of dev builds significantly.
// There's nothing to tree-shake when importing from /icons this way:
// '@mui/icons-material/*/',
'@mui/utils/*',
// End block
// Macros are fine since their import path is transpiled away
'!@mui/utils/macros',
'@mui/utils/macros/*',
Expand Down Expand Up @@ -333,7 +326,7 @@ module.exports = {
'error',
{
patterns: [
// Allow deeper imports for TypeScript types. TODO?
// Allow deeper imports for TypeScript types. TODO remove
'@mui/*/*/*/*',
// Macros are fine since they're transpiled into something else
'!@mui/utils/macros/*.macro',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { useAutocomplete } from '@mui/base/useAutocomplete';
import { Popper } from '@mui/base/Popper';
import { styled } from '@mui/system';
import { unstable_useForkRef as useForkRef } from '@mui/utils';
import useForkRef from '@mui/utils/useForkRef'; // TODO import from @mui/base, private package

const Autocomplete = React.forwardRef(function Autocomplete(props, ref) {
const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { useAutocomplete, UseAutocompleteProps } from '@mui/base/useAutocomplete';
import { Popper } from '@mui/base/Popper';
import { styled } from '@mui/system';
import { unstable_useForkRef as useForkRef } from '@mui/utils';
import useForkRef from '@mui/utils/useForkRef'; // TODO import from @mui/base, private package

const Autocomplete = React.forwardRef(function Autocomplete(
props: UseAutocompleteProps<(typeof top100Films)[number], false, false, false>,
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/FormControl/FormControl.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { unstable_useControlled as useControlled } from '@mui/utils';
import useControlled from '@mui/utils/useControlled'; // TODO import from ../useControlled
import { PolymorphicComponent } from '../utils/PolymorphicComponent';
import { FormControlContext } from './FormControlContext';
import { getFormControlUtilityClass } from './formControlClasses';
Expand Down
3 changes: 2 additions & 1 deletion packages/mui-base/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"rootDir": "./src"
},
"include": ["src/**/*.ts*"],
"exclude": ["src/**/*.spec.ts*", "src/**/*.test.ts*"]
"exclude": ["src/**/*.spec.ts*", "src/**/*.test.ts*"],
"references": [{ "path": "../mui-utils/tsconfig.build.json" }]
}
2 changes: 1 addition & 1 deletion packages/mui-lab/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"@mui/system": "^5.14.7",
"@mui/types": "^7.2.4",
"@mui/utils": "^5.14.7",
"@mui/x-tree-view": "https://pkg.csb.dev/mui/mui-x/commit/1f23b33d/@mui/x-tree-view",
"@mui/x-tree-view": "^6.0.0-alpha.1",
"clsx": "^2.0.0",
"prop-types": "^15.8.1",
"react-is": "^18.2.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-lab/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
// This config is for emitting declarations (.d.ts) only
// Actual .ts source files are transpiled via babel
"extends": "./tsconfig.json",
"extends": "./tsconfig",
"compilerOptions": {
"noEmit": false,
"declaration": true,
Expand Down
4 changes: 4 additions & 0 deletions packages/mui-material/scripts/rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ const nestedFolder = {
return resolveNestedImport('mui-base', importee);
}

if (importee.indexOf('@mui/utils/') === 0) {
return resolveNestedImport('mui-utils', importee);
}

if (importee.indexOf('@mui/private-theming/') === 0) {
return resolveNestedImport('mui-private-theming', importee);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { unstable_generateUtilityClasses as generateUtilityClasses } from '@mui/utils';
import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; // TODO import from @mui/base, private package
import generateUtilityClass from '../generateUtilityClass';

export interface ListSubheaderClasses {
Expand Down
3 changes: 2 additions & 1 deletion packages/mui-system/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
"rootDir": "./src"
},
"include": ["src/**/*.ts*"],
"exclude": ["src/**/*.spec.ts*", "src/**/*.test.ts*"]
"exclude": ["src/**/*.spec.ts*", "src/**/*.test.ts*"],
"references": [{ "path": "../mui-utils/tsconfig.build.json" }]
}
15 changes: 15 additions & 0 deletions packages/mui-utils/src/integerPropType.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import PropTypes from 'prop-types';

declare function integerPropType(
props: { [key: string]: any },
propName: string,
componentName: string,
location: string,
propFullName: string,
): Error | null;

declare module integerPropType {
let isRequired: PropTypes.Validator<number>;
}

export default integerPropType;
1 change: 1 addition & 0 deletions packages/mui-utils/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Actual .ts source files are transpiled via babel
"extends": "./tsconfig",
"compilerOptions": {
"composite": true,
"declaration": true,
"noEmit": false,
"emitDeclarationOnly": true,
Expand Down
134 changes: 67 additions & 67 deletions scripts/buildTypes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,73 @@ function rewriteImportPath(importPath) {
throw new Error(`Don't know where to rewrite '${importPath}' to`);
}

async function rewriteImportPaths(declarationFile, publishDir) {
const code = await fse.readFile(declarationFile, { encoding: 'utf8' });
const basename = path.basename(declarationFile);

if (
// Only consider React components
basename[0] === basename[0].toUpperCase() &&
code.indexOf("import PropTypes from 'prop-types';") !== -1
) {
throw new Error(
[
`${declarationFile} imports from 'prop-types', this is wrong.`,
"It's likely missing a cast to any on the propTypes declaration:",
'ComponentName.propTypes = { /* prop */ } as any;',
].join('\n'),
);
}

let fixedCode = code;
const changes = [];

// find all type `import()`
// not to be confused with `import type`
const importTypeRegExp = /import\(([^)]+)\)/g;

let importTypeMatch;
// eslint-disable-next-line no-cond-assign -- Waiting for RegExp.prototype.matchAll
while ((importTypeMatch = importTypeRegExp.exec(code)) !== null) {
// First and last character are quotes.
// TypeScript mixes single and double quotes.
const importPath = importTypeMatch[1].slice(1, -1);
// In filesystem semantics `@mui/material` is a relative path.
// But when resolving imports these specifiers are considered "bare specifiers" and work differently.
// We're only interested in imports that are considered "relative path imports".
const isBareImportSpecifier = !importPath.startsWith('.');
if (!isBareImportSpecifier) {
const resolvedImport = path.resolve(declarationFile, importPath);
const importPathFromPublishDir = path.relative(publishDir, resolvedImport);
const isImportReachableWhenPublished = !importPathFromPublishDir.startsWith('.');

if (!isImportReachableWhenPublished) {
try {
const fixedImportPath = rewriteImportPath(
// ensure relative POSIX path
importPathFromPublishDir.replace(/\\/g, '/'),
);
const originalImportType = importTypeMatch[0];
const fixedImportType = importTypeMatch[0].replace(importPath, fixedImportPath);

// Make it easy to visually scan for the created lines.
changes.push(`-${chalk.bgRed(originalImportType)}\n+${chalk.bgGreen(fixedImportType)}`);
fixedCode = fixedCode.replace(originalImportType, fixedImportType);
} catch (error) {
throw new Error(`${declarationFile}: ${error}`);
}
}
}
}

const changed = changes.length > 0;
if (changed) {
await fse.writeFile(declarationFile, fixedCode);
}

return changes;
}

async function main() {
const packageRoot = process.cwd();

Expand All @@ -52,73 +119,6 @@ async function main() {
throw new Error(`Unable to find declaration files in '${publishDir}'`);
}

async function rewriteImportPaths(declarationFile) {
const code = await fse.readFile(declarationFile, { encoding: 'utf8' });
const basename = path.basename(declarationFile);

if (
// Only consider React components
basename[0] === basename[0].toUpperCase() &&
code.indexOf("import PropTypes from 'prop-types';") !== -1
) {
throw new Error(
[
`${declarationFile} imports from 'prop-types', this is wrong.`,
"It's likely missing a cast to any on the propTypes declaration:",
'ComponentName.propTypes = { /* prop */ } as any;',
].join('\n'),
);
}

let fixedCode = code;
const changes = [];

// find all type `import()`
// not to be confused with `import type`
const importTypeRegExp = /import\(([^)]+)\)/g;

let importTypeMatch;
// eslint-disable-next-line no-cond-assign -- Waiting for RegExp.prototype.matchAll
while ((importTypeMatch = importTypeRegExp.exec(code)) !== null) {
// First and last character are quotes.
// TypeScript mixes single and double quotes.
const importPath = importTypeMatch[1].slice(1, -1);
// In filesystem semantics `@mui/material` is a relative path.
// But when resolving imports these specifiers are considered "bare specifiers" and work differently.
// We're only interested in imports that are considered "relative path imports".
const isBareImportSpecifier = !importPath.startsWith('.');
if (!isBareImportSpecifier) {
const resolvedImport = path.resolve(declarationFile, importPath);
const importPathFromPublishDir = path.relative(publishDir, resolvedImport);
const isImportReachableWhenPublished = !importPathFromPublishDir.startsWith('.');

if (!isImportReachableWhenPublished) {
try {
const fixedImportPath = rewriteImportPath(
// ensure relative POSIX path
importPathFromPublishDir.replace(/\\/g, '/'),
);
const originalImportType = importTypeMatch[0];
const fixedImportType = importTypeMatch[0].replace(importPath, fixedImportPath);

// Make it easy to visually scan for the created lines.
changes.push(`-${chalk.bgRed(originalImportType)}\n+${chalk.bgGreen(fixedImportType)}`);
fixedCode = fixedCode.replace(originalImportType, fixedImportType);
} catch (error) {
throw new Error(`${declarationFile}: ${error}`);
}
}
}
}

const changed = changes.length > 0;
if (changed) {
await fse.writeFile(declarationFile, fixedCode);
}

return changes;
}

let rewrittenTally = 0;
let errorTally = 0;
await Promise.all(
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"@mui/types": ["./packages/mui-types"],
"@mui/base": ["./packages/mui-base/src"],
"@mui/base/*": ["./packages/mui-base/src/*"],
"@mui/utils": ["./packages/mui-utils/src"],
"@mui/utils/*": ["./packages/mui-utils/src/*"],
"@mui/docs": ["./packages/mui-docs/src"],
"@mui/docs/*": ["./packages/mui-docs/src/*"],
"@mui/material-next": ["./packages/mui-material-next/src"],
Expand Down
13 changes: 7 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2199,14 +2199,15 @@
"@babel/runtime" "^7.22.6"
"@mui/utils" "^5.13.7"

"@mui/x-tree-view@https://pkg.csb.dev/mui/mui-x/commit/1f23b33d/@mui/x-tree-view":
version "6.0.0-alpha.0"
resolved "https://pkg.csb.dev/mui/mui-x/commit/1f23b33d/@mui/x-tree-view#93cbc61c818ed2d6c40554cdbada969f7dcbaab1"
"@mui/x-tree-view@^6.0.0-alpha.1":
version "6.0.0-alpha.1"
resolved "https://registry.yarnpkg.com/@mui/x-tree-view/-/x-tree-view-6.0.0-alpha.1.tgz#fe499f8c43c01d28aca95cfb17491746ffcc3080"
integrity sha512-JUG3HmBrmGEALbCFg1b+i7h726e1dWYZs4db3syO1j+Q++E3nbvE4Lehp5yGTFm+8esH0Tny50tuJaa4WX6VSA==
dependencies:
"@babel/runtime" "^7.22.6"
"@mui/utils" "^5.13.7"
"@mui/utils" "^5.14.3"
"@types/react-transition-group" "^4.4.6"
clsx "^1.2.1"
clsx "^2.0.0"
prop-types "^15.8.1"
react-transition-group "^4.4.5"

Expand Down Expand Up @@ -5829,7 +5830,7 @@ clone@^1.0.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==

clsx@^1.1.0, clsx@^1.1.1, clsx@^1.2.1:
clsx@^1.1.0, clsx@^1.1.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
Expand Down

0 comments on commit 833cd63

Please sign in to comment.