Skip to content

Commit e3d6e05

Browse files
committed
refactor(material/core): sass module migration preparation
1 parent c68791d commit e3d6e05

File tree

11 files changed

+237
-195
lines changed

11 files changed

+237
-195
lines changed

BUILD.bazel

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package(default_visibility = ["//visibility:public"])
88

99
exports_files([
1010
"LICENSE",
11-
"scss-bundle.config.json",
1211
])
1312

1413
genrule(

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
"@types/parse5": "^5.0.0",
108108
"@types/resize-observer-browser": "^0.1.3",
109109
"@types/run-sequence": "^0.0.29",
110+
"@types/sass": "^1.16.0",
110111
"@types/semver": "^7.3.4",
111112
"@types/send": "^0.14.5",
112113
"@types/stylelint": "^9.10.1",
@@ -159,7 +160,6 @@
159160
"rollup-plugin-sourcemaps": "^0.4.2",
160161
"run-sequence": "^1.2.2",
161162
"sass": "^1.29.0",
162-
"scss-bundle": "^3.1.2",
163163
"selenium-webdriver": "^3.6.0",
164164
"semver": "^6.3.0",
165165
"send": "^0.17.1",

scripts/migrate-sass-modules.js

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
const childProcess = require('child_process');
2+
const path = require('path');
3+
const fs = require('fs');
4+
const {sync: glob} = require('glob');
5+
6+
const directory = path.join(__dirname, '../src');
7+
const migratedFiles = new Set();
8+
const ignorePatterns = [
9+
'**/*.import.scss',
10+
'**/test-theming-bundle.scss',
11+
'material/_theming.scss'
12+
];
13+
const materialPrefixes = [
14+
...getPrefixes('material', 'mat'),
15+
...getPrefixes('material/core', 'mat'),
16+
// Outliers that don't have a directory of their own.
17+
'mat-pseudo-checkbox-',
18+
'mat-elevation-',
19+
'mat-optgroup-',
20+
'mat-private-'
21+
];
22+
const mdcPrefixes = [
23+
...getPrefixes('material-experimental', 'mat'),
24+
...getPrefixes('material-experimental/mdc-core', 'mat'),
25+
// Outliers that don't have a directory of their own.
26+
'mat-mdc-optgroup-'
27+
].map(prefix => prefix === 'mat-' ? 'mat-mdc-' : prefix);
28+
const cdkPrefixes = getPrefixes('cdk', 'cdk');
29+
const cdkExperimentalPrefixes = getPrefixes('cdk-experimental', 'cdk');
30+
31+
// Restore the source directory to a clean state.
32+
run('git', ['clean', '-f', '-d'], false, true);
33+
run('git', ['checkout', '--', directory], false, true);
34+
35+
// --reset is a utility to easily restore the repo to its initial state.
36+
if (process.argv.indexOf('--reset') > -1) {
37+
process.exit(0);
38+
}
39+
40+
// Run the migrations.
41+
42+
// Migrate all the partials and forward any export symbols.
43+
migrate('cdk/**/_*.scss', cdkPrefixes, true);
44+
migrate('cdk-experimental/**/_*.scss', cdkExperimentalPrefixes, true);
45+
migrate('material/core/**/_*.scss', materialPrefixes, true, ['**/_all-*.scss', '**/_core.scss']);
46+
migrate('material/!(core)/**/_*.scss', materialPrefixes, true);
47+
migrate('material/core/**/_*.scss', materialPrefixes, true);
48+
49+
// Comment out all MDC imports since the migrator script doesn't know how to find them.
50+
commentOutMdc('material-experimental/**/*.scss');
51+
52+
// Migrate all of the MDC partials.
53+
migrate('material-experimental/mdc-helpers/**/_*.scss', mdcPrefixes, true);
54+
migrate('material-experimental/mdc-core/**/_*.scss', mdcPrefixes, true, ['**/_core.scss']);
55+
migrate('material-experimental/**/_*.scss', mdcPrefixes, true);
56+
57+
// Migrate everything else without forwarding.
58+
migrate('cdk/**/*.scss', cdkPrefixes);
59+
migrate('cdk-experimental/**/*.scss', cdkExperimentalPrefixes);
60+
migrate('material/**/*.scss', materialPrefixes);
61+
migrate('material-experimental/**/*.scss', mdcPrefixes);
62+
63+
// Migrate whatever is left in the source files, assuming that it's not a public API.
64+
migrate('**/*.scss');
65+
66+
// Restore the commented out MDC imports and sort `@use` above `@import`.
67+
restoreAndSortMdc('material-experimental/**/*.scss');
68+
69+
// Clear the files that we don't want.
70+
clearUnwatedFiles();
71+
72+
// Try to auto-fix some of the lint issues.
73+
run('yarn', ['stylelint', '--fix'], true, true);
74+
console.log(`Finished migrating ${migratedFiles.size} files.`);
75+
76+
function migrate(pattern, prefixes = [], forward = false, ignore = []) {
77+
const args = ['module'];
78+
forward && args.push('--forward=import-only');
79+
prefixes.length && args.push(`--remove-prefix=${prefixes.join(',')}`);
80+
81+
// Note that while the migrator allows for multiple files to be passed in, we start getting
82+
// some assertion errors along the way. Running it on a file-by-file basis works fine.
83+
const files = glob(pattern, {cwd: directory, ignore: [...ignore, ...ignorePatterns]})
84+
.filter(file => !migratedFiles.has(file));
85+
const message = `Migrating ${files.length} unmigrated files matching ${pattern}.`;
86+
console.log(ignore.length ? message + ` Ignoring ${ignore.join(', ')}.` : message);
87+
run('sass-migrator', [...args, ...files]);
88+
files.forEach(file => migratedFiles.add(file));
89+
}
90+
91+
function run(name, args, canFail = false, silent = false) {
92+
const result = childProcess.spawnSync(name, args, {shell: true, cwd: directory});
93+
const output = result.stdout.toString();
94+
!silent && output.length && console.log(output);
95+
96+
if (result.status !== 0 && !canFail) {
97+
console.error(`Script error: ${(result.stderr || result.stdout)}`);
98+
process.exit(1);
99+
}
100+
}
101+
102+
function getPrefixes(package, prefix) {
103+
return fs.readdirSync(path.join(directory, package), {withFileTypes: true})
104+
.filter(current => current.isDirectory())
105+
.map(current => current.name)
106+
.reduce((output, current) => [`${prefix}-${current}-`, ...output], [`${prefix}-`]);
107+
}
108+
109+
function commentOutMdc(pattern) {
110+
const files = glob(pattern, {cwd: directory, absolute: true});
111+
console.log(`Commenting out @material imports from ${files.length} files matching ${pattern}.`);
112+
files.forEach(file => {
113+
const content = fs.readFileSync(file, 'utf8');
114+
// Prefix the content with a marker so we know what to restore later.
115+
fs.writeFileSync(file, content.replace(/(@use|@import) '@material/g, m => '//🚀 ' + m));
116+
});
117+
}
118+
119+
function restoreAndSortMdc(pattern) {
120+
const files = glob(pattern, {cwd: directory, absolute: true});
121+
console.log(`Re-adding and sorting @material imports from ${files.length} ` +
122+
`files matching ${pattern}.`);
123+
124+
files.forEach(file => {
125+
// Remove the commented out lines with the marker from `commentOutMdc`.
126+
const content = fs.readFileSync(file, 'utf8').replace(/\/\/🚀 /g, '');
127+
const lines = content.split('\n');
128+
let headerStartIndex = -1;
129+
let headerEndIndex = -1;
130+
131+
// Find where the comments start and end.
132+
for (let i = lines.length - 1; i > -1; i--) {
133+
if (lines[i].startsWith('@use') || lines[i].startsWith('@import')) {
134+
headerStartIndex = i;
135+
136+
if (headerEndIndex === -1) {
137+
headerEndIndex = i + 1;
138+
}
139+
}
140+
}
141+
142+
// Sort the imports so that `@use` comes before `@import`. Otherwise Sass will throw an error.
143+
if (headerStartIndex > -1 && headerEndIndex > -1) {
144+
const headers = lines
145+
.splice(headerStartIndex, headerEndIndex - headerStartIndex)
146+
.sort((a, b) => a.startsWith('@use') && !b.startsWith('@use') ? -1 : 0);
147+
lines.splice(headerStartIndex, 0, ...headers);
148+
}
149+
150+
fs.writeFileSync(file, lines.join('\n'));
151+
});
152+
}
153+
154+
function clearUnwatedFiles() {
155+
// The migration script generates .import files even if we don't pass in the `--forward` when
156+
// a file has top-level variables matching a prefix. Since we still want such files to be
157+
// migrated, we clear the unwanted files afterwards.
158+
const files = glob('**/*.import.scss', {cwd: directory, absolute: true, ignore: ['**/_*.scss']});
159+
console.log(`Clearing ${files.length} unwanted files.`);
160+
files.forEach(file => fs.unlinkSync(file));
161+
}

scss-bundle.config.json

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,66 @@
11
@use 'sass:color';
22
@use '@material/theme/variables' as theme-variables;
3-
4-
@import '@material/textfield/variables.import';
3+
@use '@material/textfield/variables' as textfield-variables;
54

65
// Mixin that refreshes the MDC text-field theming variables. This mixin should be used when
76
// the base MDC theming variables have been explicitly updated, but the component specific
87
// theming-based variables are still based on the old MDC base theming variables. The mixin
98
// restores the previous values for the variables to avoid unexpected global side effects.
109
@mixin mat-mdc-private-text-field-refresh-theme-variables() {
11-
$_mdc-text-field-disabled-border-border: $mdc-text-field-disabled-border-border;
12-
$mdc-text-field-disabled-border: rgba(theme-variables.prop-value(on-surface), 0.06) !global;
13-
$_mdc-text-field-bottom-line-hover: $mdc-text-field-bottom-line-hover;
14-
$mdc-text-field-bottom-line-hover: rgba(theme-variables.prop-value(on-surface), 0.87) !global;
15-
$_mdc-text-field-bottom-line-idle: $mdc-text-field-bottom-line-idle;
16-
$mdc-text-field-bottom-line-idle: rgba(theme-variables.prop-value(on-surface), 0.42) !global;
17-
$_mdc-text-field-label: $mdc-text-field-label;
18-
$mdc-text-field-label: rgba(theme-variables.prop-value(on-surface), 0.6) !global;
19-
$_mdc-text-field-ink-color: $mdc-text-field-ink-color;
20-
$mdc-text-field-ink-color: rgba(theme-variables.prop-value(on-surface), 0.87) !global;
21-
$_mdc-text-field-focused-label-color: $mdc-text-field-focused-label-color;
22-
$mdc-text-field-focused-label-color: rgba(theme-variables.prop-value(primary), 0.87) !global;
23-
$_mdc-text-field-placeholder-ink-color: $mdc-text-field-placeholder-ink-color;
24-
$mdc-text-field-placeholder-ink-color: rgba(theme-variables.prop-value(on-surface), 0.54) !global;
25-
$_mdc-text-field-disabled-label-color: $mdc-text-field-disabled-label-color;
26-
$mdc-text-field-disabled-label-color: rgba(theme-variables.prop-value(on-surface), 0.38) !global;
27-
$_mdc-text-field-disabled-ink-color: $mdc-text-field-disabled-ink-color;
28-
$mdc-text-field-disabled-ink-color: rgba(theme-variables.prop-value(on-surface), 0.38) !global;
29-
$_mdc-text-field-disabled-placeholder-ink-color: $mdc-text-field-disabled-placeholder-ink-color;
30-
$mdc-text-field-disabled-placeholder-ink-color:
31-
rgba(theme-variables.prop-value(on-surface), 0.38) !global;
32-
$_mdc-text-field-background: $mdc-text-field-background;
33-
$mdc-text-field-background: color.mix(
34-
theme-variables.prop-value(on-surface), theme-variables.prop-value(surface), 4%) !global;
35-
$_mdc-text-field-disabled-background: $mdc-text-field-disabled-background;
36-
$mdc-text-field-disabled-background: color.mix(
37-
theme-variables.prop-value(on-surface), theme-variables.prop-value(surface), 2%) !global;
38-
$_mdc-text-field-outlined-idle-border: $mdc-text-field-outlined-idle-border;
39-
$mdc-text-field-outlined-idle-border: rgba(theme-variables.prop-value(on-surface), 0.38) !global;
40-
$_mdc-text-field-outlined-disabled-border: $mdc-text-field-outlined-disabled-border;
41-
$mdc-text-field-outlined-disabled-border:
42-
rgba(theme-variables.prop-value(on-surface), 0.06) !global;
43-
$_mdc-text-field-outlined-hover-border: $mdc-text-field-outlined-hover-border;
44-
$mdc-text-field-outlined-hover-border: rgba(theme-variables.prop-value(on-surface), 0.87) !global;
10+
$_mdc-text-field-disabled-border: textfield-variables.$disabled-border;
11+
textfield-variables.$disabled-border: rgba(theme-variables.prop-value(on-surface), 0.06);
12+
$_mdc-text-field-bottom-line-hover: textfield-variables.$bottom-line-hover;
13+
textfield-variables.$bottom-line-hover: rgba(theme-variables.prop-value(on-surface), 0.87);
14+
$_mdc-text-field-bottom-line-idle: textfield-variables.$bottom-line-idle;
15+
textfield-variables.$bottom-line-idle: rgba(theme-variables.prop-value(on-surface), 0.42);
16+
$_mdc-text-field-label: textfield-variables.$label;
17+
textfield-variables.$label: rgba(theme-variables.prop-value(on-surface), 0.6);
18+
$_mdc-text-field-ink-color: textfield-variables.$ink-color;
19+
textfield-variables.$ink-color: rgba(theme-variables.prop-value(on-surface), 0.87);
20+
$_mdc-text-field-focused-label-color: textfield-variables.$focused-label-color;
21+
textfield-variables.$focused-label-color: rgba(theme-variables.prop-value(primary), 0.87);
22+
$_mdc-text-field-placeholder-ink-color: textfield-variables.$placeholder-ink-color;
23+
textfield-variables.$placeholder-ink-color: rgba(theme-variables.prop-value(on-surface), 0.54);
24+
$_mdc-text-field-disabled-label-color: textfield-variables.$disabled-label-color;
25+
textfield-variables.$disabled-label-color: rgba(theme-variables.prop-value(on-surface), 0.38);
26+
$_mdc-text-field-disabled-ink-color: textfield-variables.$disabled-ink-color;
27+
textfield-variables.$disabled-ink-color: rgba(theme-variables.prop-value(on-surface), 0.38);
28+
$_mdc-text-field-disabled-placeholder-ink-color:
29+
textfield-variables.$disabled-placeholder-ink-color;
30+
textfield-variables.$disabled-placeholder-ink-color:
31+
rgba(theme-variables.prop-value(on-surface), 0.38);
32+
$_mdc-text-field-background: textfield-variables.$background;
33+
textfield-variables.$background: color.mix(
34+
theme-variables.prop-value(on-surface), theme-variables.prop-value(surface), 4%);
35+
$_mdc-text-field-disabled-background: textfield-variables.$disabled-background;
36+
textfield-variables.$disabled-background: color.mix(
37+
theme-variables.prop-value(on-surface), theme-variables.prop-value(surface), 2%);
38+
$_mdc-text-field-outlined-idle-border: textfield-variables.$outlined-idle-border;
39+
textfield-variables.$outlined-idle-border: rgba(theme-variables.prop-value(on-surface), 0.38);
40+
$_mdc-text-field-outlined-disabled-border: textfield-variables.$outlined-disabled-border;
41+
textfield-variables.$outlined-disabled-border:
42+
rgba(theme-variables.prop-value(on-surface), 0.06);
43+
$_mdc-text-field-outlined-hover-border: textfield-variables.$outlined-hover-border;
44+
textfield-variables.$outlined-hover-border: rgba(theme-variables.prop-value(on-surface), 0.87);
4545

4646
// The content will be generated with the refreshed MDC text-field theming variables.
4747
@content;
4848

4949
// Reset all variables to ensure that this mixin does not cause unexpected side effects.
50-
$mdc-text-field-disabled-border-border: $_mdc-text-field-disabled-border-border !global;
51-
$mdc-text-field-bottom-line-hover: $_mdc-text-field-bottom-line-hover !global;
52-
$mdc-text-field-bottom-line-idle: $_mdc-text-field-bottom-line-idle !global;
53-
$mdc-text-field-label: $_mdc-text-field-label !global;
54-
$mdc-text-field-ink-color: $_mdc-text-field-ink-color !global;
55-
$mdc-text-field-focused-label-color: $_mdc-text-field-focused-label-color !global;
56-
$mdc-text-field-placeholder-ink-color: $_mdc-text-field-placeholder-ink-color !global;
57-
$mdc-text-field-disabled-label-color: $_mdc-text-field-disabled-label-color !global;
58-
$mdc-text-field-disabled-ink-color: $_mdc-text-field-disabled-ink-color !global;
59-
$mdc-text-field-disabled-placeholder-ink-color:
60-
$_mdc-text-field-disabled-placeholder-ink-color !global;
61-
$mdc-text-field-background: $_mdc-text-field-background !global;
62-
$mdc-text-field-disabled-background: $_mdc-text-field-disabled-background !global;
63-
$mdc-text-field-outlined-idle-border: $_mdc-text-field-outlined-idle-border !global;
64-
$mdc-text-field-outlined-disabled-border: $_mdc-text-field-outlined-disabled-border !global;
65-
$mdc-text-field-outlined-hover-border: $_mdc-text-field-outlined-hover-border !global;
50+
textfield-variables.$disabled-border: $_mdc-text-field-disabled-border;
51+
textfield-variables.$bottom-line-hover: $_mdc-text-field-bottom-line-hover;
52+
textfield-variables.$bottom-line-idle: $_mdc-text-field-bottom-line-idle;
53+
textfield-variables.$label: $_mdc-text-field-label;
54+
textfield-variables.$ink-color: $_mdc-text-field-ink-color;
55+
textfield-variables.$focused-label-color: $_mdc-text-field-focused-label-color;
56+
textfield-variables.$placeholder-ink-color: $_mdc-text-field-placeholder-ink-color;
57+
textfield-variables.$disabled-label-color: $_mdc-text-field-disabled-label-color;
58+
textfield-variables.$disabled-ink-color: $_mdc-text-field-disabled-ink-color;
59+
textfield-variables.$disabled-placeholder-ink-color:
60+
$_mdc-text-field-disabled-placeholder-ink-color;
61+
textfield-variables.$background: $_mdc-text-field-background;
62+
textfield-variables.$disabled-background: $_mdc-text-field-disabled-background;
63+
textfield-variables.$outlined-idle-border: $_mdc-text-field-outlined-idle-border;
64+
textfield-variables.$outlined-disabled-border: $_mdc-text-field-outlined-disabled-border;
65+
textfield-variables.$outlined-hover-border: $_mdc-text-field-outlined-hover-border;
6666
}

src/material/BUILD.bazel

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
load("@npm//scss-bundle:index.bzl", "scss_bundle")
2-
load("//src/cdk:config.bzl", "CDK_SCSS_LIBS")
31
load(
42
"//src/material:config.bzl",
53
"MATERIAL_ENTRYPOINTS",
64
"MATERIAL_SCSS_LIBS",
75
"MATERIAL_TARGETS",
86
"MATERIAL_TESTING_TARGETS",
97
)
10-
load("//tools:defaults.bzl", "ng_package", "ts_library")
8+
load("//tools:defaults.bzl", "ng_package", "sass_library", "ts_library")
119

1210
package(default_visibility = ["//visibility:public"])
1311

@@ -22,24 +20,11 @@ filegroup(
2220
srcs = ["//src/material/%s:overview" % name for name in MATERIAL_ENTRYPOINTS],
2321
)
2422

25-
scss_bundle(
23+
# Makes the theming bundle available in the release output as `angular/material/theming`.
24+
sass_library(
2625
name = "theming_bundle",
27-
outs = ["_theming.scss"],
28-
args = [
29-
"--entryFile=$(execpath :theming-bundle.scss)",
30-
"--outFile=$(execpath :_theming.scss)",
31-
32-
# The config file has to be passed in explicitly, otherwise the
33-
# bundler will still run, but produce massive bundle files.
34-
"--config=scss-bundle.config.json",
35-
],
36-
data = CDK_SCSS_LIBS + MATERIAL_SCSS_LIBS + [
37-
"theming-bundle.scss",
38-
"//src/material/core:theming_scss_lib",
39-
# Config file is required by "scss-bundle" and will be automatically
40-
# loaded by the CLI. It expects the config to be in the execroot.
41-
"//:scss-bundle.config.json",
42-
],
26+
srcs = ["_theming.scss"],
27+
deps = ["//src/material/core:theming_scss_lib"],
4328
)
4429

4530
# Creates the @angular/material package published to npm.

src/material/_theming.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Forwards all public API mixins so they can be imported from a single entry point.
2+
// Note that we have to forward the `.import` files for backwards-compatibility with
3+
// projects that don't use Sass modules and include the `mat-`-prefixed mixins.
4+
5+
@forward './core/color/all-color.import';
6+
@forward './core/density/private/all-density.import';
7+
@forward './core/theming/all-theme.import';
8+
@forward './core/typography/all-typography.import';

src/material/core/theming/_theming.scss

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import 'palette';
2+
@import '../density/private/compatibility';
23

34
// Whether duplication warnings should be disabled. Warnings enabled by default.
45
$mat-theme-ignore-duplication-warnings: false !default;
@@ -333,14 +334,15 @@ $_mat-theme-emitted-density: () !default;
333334
// styles **once** and at root. This matches the old behavior where density styles were
334335
// part of the base component styles (that did not use view encapsulation).
335336
// TODO: Remove this compatibility logic when the legacy theming API is removed.
336-
$mat-private-density-generate-at-root: mat-private-is-legacy-constructed-theme($theme) !global;
337-
$mat-private-density-generate-styles: not $duplicate-legacy-density !global;
337+
$mat-private-density-generate-at-root: mat-private-is-legacy-constructed-theme($theme);
338+
$mat-private-density-generate-styles: not $duplicate-legacy-density;
338339

339340
@content;
340341
$mat-theme-ignore-duplication-warnings: $orig-mat-theme-ignore-duplication-warnings !global;
341342

342-
$mat-private-density-generate-at-root: false !global;
343-
$mat-private-density-generate-styles: true !global;
343+
// TODO: after the migration is finished, these need to refer to the `compatibility` import.
344+
$mat-private-density-generate-at-root: false;
345+
$mat-private-density-generate-styles: true;
344346
}
345347

346348
// Checks whether the given value resolves to a theme object. Theme objects are always

0 commit comments

Comments
 (0)