Skip to content

Commit ae061d7

Browse files
authored
build: add CI check to ensure consistent exports for MDC packages (#20960)
Adds a new script that checks whether an MDC package exports the same set of symbols as its non-MDC counterpart. Also fixes all of the failures.
1 parent 69ffc47 commit ae061d7

File tree

16 files changed

+211
-13
lines changed

16 files changed

+211
-13
lines changed

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ jobs:
366366
- run: yarn stylelint
367367
- run: yarn tslint
368368
- run: yarn -s ts-circular-deps:check
369+
- run: yarn check-mdc-exports
369370

370371
- *slack_notify_on_failure
371372
- *save_cache

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
"integration-tests": "bazel test --test_tag_filters=-view-engine-only --build_tests_only -- //integration/... -//integration/size-test/...",
4747
"integration-tests:view-engine": "bazel test --test_tag_filters=view-engine-only --build_tests_only -- //integration/... -//integration/size-test/...",
4848
"integration-tests:size-test": "bazel test //integration/size-test/...",
49-
"check-mdc-tests": "ts-node --project scripts/tsconfig.json scripts/check-mdc-tests.ts"
49+
"check-mdc-tests": "ts-node --project scripts/tsconfig.json scripts/check-mdc-tests.ts",
50+
"check-mdc-exports": "ts-node --project scripts/tsconfig.json scripts/check-mdc-exports.ts"
5051
},
5152
"version": "11.1.0-next.0",
5253
"dependencies": {

scripts/check-mdc-exports-config.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
export const config = {
2+
// The MDC sidenav hasn't been implemented yet.
3+
skippedPackages: ['mdc-sidenav'],
4+
skippedExports: {
5+
'mdc-chips': [
6+
// These components haven't been implemented for MDC due to a different accessibility pattern.
7+
'MatChipListChange',
8+
'MatChipList'
9+
],
10+
'mdc-autocomplete': [
11+
// Private base classes that are only exported for MDC.
12+
'_MatAutocompleteBase',
13+
'_MatAutocompleteTriggerBase',
14+
'_MatAutocompleteOriginBase'
15+
],
16+
'mdc-core': [
17+
// Private base classes that are only exported for MDC.
18+
'_MatOptionBase',
19+
'_MatOptgroupBase'
20+
],
21+
'mdc-dialog': [
22+
// Private base classes and utility function that are only exported for MDC.
23+
'_MatDialogBase',
24+
'_MatDialogContainerBase',
25+
'_closeDialogVia',
26+
],
27+
'mdc-input': [
28+
// TODO: an MDC version of this directive has to be implemented.
29+
'MatTextareaAutosize'
30+
],
31+
'mdc-menu': [
32+
// Private base class that is only exported for MDC.
33+
'_MatMenuBase'
34+
],
35+
'mdc-paginator': [
36+
// Private base class that is only exported for MDC.
37+
'_MatPaginatorBase'
38+
],
39+
'mdc-radio': [
40+
// Private base classes that are only exported for MDC.
41+
'_MatRadioGroupBase',
42+
'_MatRadioButtonBase',
43+
],
44+
'mdc-select': [
45+
// Private base class that is only exported for MDC.
46+
'_MatSelectBase'
47+
],
48+
'mdc-slide-toggle': [
49+
// Private module used to provide some common functionality.
50+
'_MatSlideToggleRequiredValidatorModule'
51+
],
52+
'mdc-snack-bar': [
53+
// Private interface used to ensure consistency for MDC package.
54+
'_SnackBarContainer'
55+
],
56+
'mdc-tabs': [
57+
// Private base classes that are only exported for MDC.
58+
'_MatTabBodyBase',
59+
'_MatTabHeaderBase',
60+
'_MatTabNavBase',
61+
'_MatTabLinkBase',
62+
'_MatTabGroupBase'
63+
]
64+
}
65+
};

scripts/check-mdc-exports.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {join} from 'path';
2+
import {readdirSync, existsSync} from 'fs';
3+
import * as ts from 'typescript';
4+
import chalk from 'chalk';
5+
import {config} from './check-mdc-exports-config';
6+
7+
// Script which ensures that a particular MDC package exports all of the same symbols as its
8+
// non-MDC counterparts. Only looks at symbol names, not their signatures. Exceptions
9+
// can be configured through the `check-mdc-exports-config.ts` file.
10+
11+
let hasFailed = false;
12+
13+
readdirSync(join(__dirname, '../src/material'), {withFileTypes: true})
14+
.filter(entity => entity.isDirectory())
15+
.map(entity => entity.name)
16+
.filter(name => !config.skippedPackages.includes(`mdc-${name}`))
17+
.filter(hasCorrespondingMdcPackage)
18+
.forEach(name => {
19+
const missingSymbols = getMissingSymbols(name, config.skippedExports[`mdc-${name}`] || []);
20+
21+
if (missingSymbols.length) {
22+
console.log(chalk.redBright(`\nMissing symbols from mdc-${name}:`));
23+
console.log(missingSymbols.join('\n'));
24+
hasFailed = true;
25+
}
26+
});
27+
28+
if (hasFailed) {
29+
console.log(chalk.redBright(
30+
'\nDetected one or more MDC packages that do not export the same set of symbols from\n' +
31+
'public-api.ts as their non-MDC counterpart.\nEither implement the missing symbols or ' +
32+
're-export them from the Material package,\nor add them to the `skippedExports` list in ' +
33+
`scripts/check-mdc-exports-config.ts.`
34+
));
35+
process.exit(1);
36+
} else {
37+
console.log(chalk.green(
38+
'All MDC packages export the same public API symbols as their non-MDC counterparts.'));
39+
process.exit(0);
40+
}
41+
42+
/**
43+
* Gets the names of symbols that are present in a Material package,
44+
* but not its MDC counterpart.
45+
*/
46+
function getMissingSymbols(name: string, skipped: string[]): string[] {
47+
const mdcExports = getExports(`material-experimental/mdc-${name}`);
48+
const materialExports = getExports(`material/${name}`);
49+
50+
if (!mdcExports.length) {
51+
throw Error(`Could not resolve exports in mdc-${name}`);
52+
}
53+
54+
if (!materialExports.length) {
55+
throw Error(`Could not resolve exports in ${name}`);
56+
}
57+
58+
return materialExports.filter(exportName => {
59+
return !skipped.includes(exportName) && !mdcExports.includes(exportName);
60+
});
61+
}
62+
63+
/**
64+
* Gets the name of the exported symbols from a particular package.
65+
* Based on https://github.com/angular/angular/blob/master/tools/ts-api-guardian/lib/serializer.ts
66+
*/
67+
function getExports(name: string): string[] {
68+
const entryPoint = join(__dirname, '../src', name, 'public-api.ts');
69+
const program = ts.createProgram([entryPoint], {
70+
// This is a bit faster than the default and seems to produce identical results.
71+
moduleResolution: ts.ModuleResolutionKind.Classic
72+
});
73+
const sourceFile = program.getSourceFiles().find(f => f.fileName.endsWith('public-api.ts'))!;
74+
const typeChecker = program.getTypeChecker();
75+
const mainSymbol = typeChecker.getSymbolAtLocation(sourceFile);
76+
77+
return (mainSymbol ? (typeChecker.getExportsOfModule(mainSymbol) || []) : []).map(symbol => {
78+
// tslint:disable-next-line:no-bitwise
79+
if (symbol.flags & ts.SymbolFlags.Alias) {
80+
const resolvedSymbol = typeChecker.getAliasedSymbol(symbol);
81+
return (!resolvedSymbol.valueDeclaration && !resolvedSymbol.declarations) ?
82+
symbol : resolvedSymbol;
83+
} else {
84+
return symbol;
85+
}
86+
}).map(symbol => symbol.name);
87+
}
88+
89+
/** Checks whether a particular Material package has an MDC-based equivalent. */
90+
function hasCorrespondingMdcPackage(name: string): boolean {
91+
return existsSync(join(__dirname, '../src/material-experimental', 'mdc-' + name));
92+
}

src/material-experimental/mdc-checkbox/public-api.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {_MatCheckboxRequiredValidatorModule} from '@angular/material/checkbox';
10-
119
export * from './checkbox';
1210
export * from './module';
1311

@@ -21,4 +19,7 @@ export {
2119
* @breaking-change 9.0.0
2220
*/
2321
TransitionCheckState,
22+
MAT_CHECKBOX_DEFAULT_OPTIONS_FACTORY,
23+
MatCheckboxDefaultOptions,
24+
MAT_CHECKBOX_DEFAULT_OPTIONS,
2425
} from '@angular/material/checkbox';

src/material-experimental/mdc-dialog/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ export {
1919
throwMatDialogContentAlreadyAttachedError,
2020
DialogRole,
2121
DialogPosition,
22+
MAT_DIALOG_SCROLL_STRATEGY_FACTORY
2223
} from '@angular/material/dialog';

src/material-experimental/mdc-form-field/public-api.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
export {
10-
MAT_FORM_FIELD,
11-
MatFormFieldControl,
12-
getMatFormFieldDuplicatedHintError,
13-
getMatFormFieldMissingControlError,
14-
} from '@angular/material/form-field';
15-
169
export * from './directives/label';
1710
export * from './directives/error';
1811
export * from './directives/hint';
1912
export * from './directives/prefix';
2013
export * from './directives/suffix';
2114
export * from './form-field';
2215
export * from './module';
16+
17+
export {
18+
MAT_FORM_FIELD,
19+
MatFormFieldControl,
20+
getMatFormFieldDuplicatedHintError,
21+
getMatFormFieldMissingControlError,
22+
getMatFormFieldPlaceholderConflictError,
23+
_MAT_HINT,
24+
MatPlaceholder,
25+
matFormFieldAnimations,
26+
} from '@angular/material/form-field';

src/material-experimental/mdc-input/public-api.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
export {getMatInputUnsupportedTypeError, MAT_INPUT_VALUE_ACCESSOR} from '@angular/material/input';
109
export {MatInput} from './input';
1110
export {MatInputModule} from './module';
11+
12+
export {
13+
getMatInputUnsupportedTypeError,
14+
MAT_INPUT_VALUE_ACCESSOR,
15+
} from '@angular/material/input';

src/material-experimental/mdc-list/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ ng_module(
2929
"//src/cdk/collections",
3030
"//src/material-experimental/mdc-core",
3131
"//src/material/divider",
32+
"//src/material/list",
3233
"@npm//@angular/core",
3334
"@npm//@angular/forms",
3435
"@npm//@material/list",

src/material-experimental/mdc-list/public-api.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export * from './nav-list';
1313
export * from './selection-list';
1414
export * from './list-option';
1515
export * from './list-styling';
16-
1716
export {MatListOptionCheckboxPosition} from './list-option-types';
1817
export {MatListOption} from './list-option';
18+
19+
export {
20+
MAT_LIST,
21+
MAT_NAV_LIST,
22+
MAT_SELECTION_LIST_VALUE_ACCESSOR,
23+
} from '@angular/material/list';

src/material-experimental/mdc-menu/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ export {
2424
MenuPositionX,
2525
MenuPositionY,
2626
transformMenu,
27+
MAT_MENU_CONTENT,
2728
} from '@angular/material/menu';

src/material-experimental/mdc-progress-spinner/public-api.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@
88

99
export * from './progress-spinner';
1010
export * from './module';
11+
12+
export {
13+
MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS,
14+
MatProgressSpinnerDefaultOptions,
15+
MAT_PROGRESS_SPINNER_DEFAULT_OPTIONS_FACTORY,
16+
} from '@angular/material/progress-spinner';

src/material-experimental/mdc-radio/public-api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@
88

99
export * from './radio';
1010
export * from './module';
11+
12+
export {
13+
MAT_RADIO_DEFAULT_OPTIONS_FACTORY,
14+
MatRadioDefaultOptions,
15+
} from '@angular/material/radio';

src/material-experimental/mdc-slide-toggle/public-api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@
99
export * from './slide-toggle';
1010
export * from './slide-toggle-config';
1111
export * from './module';
12+
13+
export {
14+
MAT_SLIDE_TOGGLE_REQUIRED_VALIDATOR,
15+
MatSlideToggleRequiredValidator,
16+
} from '@angular/material/slide-toggle';

src/material-experimental/mdc-snack-bar/public-api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@ export {
1919
SimpleSnackBar,
2020
MAT_SNACK_BAR_DATA,
2121
MAT_SNACK_BAR_DEFAULT_OPTIONS,
22+
MAT_SNACK_BAR_DEFAULT_OPTIONS_FACTORY,
23+
MatSnackBarHorizontalPosition,
24+
MatSnackBarVerticalPosition,
25+
TextOnlySnackBar,
26+
matSnackBarAnimations,
2227
} from '@angular/material/snack-bar';

src/material-experimental/mdc-tabs/public-api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
export * from './module';
10-
export {MatTabBodyPortal} from './tab-body';
10+
export {MatTabBodyPortal, MatTabBody} from './tab-body';
1111
export {MatTabContent} from './tab-content';
1212
export {MatTabLabel} from './tab-label';
1313
export {MatTabLabelWrapper} from './tab-label-wrapper';
@@ -28,4 +28,5 @@ export {
2828
MatTabsConfig,
2929
MAT_TABS_CONFIG,
3030
MAT_TAB_GROUP,
31+
ScrollDirection,
3132
} from '@angular/material/tabs';

0 commit comments

Comments
 (0)