diff --git a/tools/extract-tokens/BUILD.bazel b/tools/extract-tokens/BUILD.bazel index ab58b0f7c342..1877ba74e2b3 100644 --- a/tools/extract-tokens/BUILD.bazel +++ b/tools/extract-tokens/BUILD.bazel @@ -10,6 +10,7 @@ ts_library( devmode_module = "commonjs", tsconfig = ":tsconfig.json", deps = [ + "//tools/highlight-files:sources", "@npm//@types/node", "@npm//sass", ], @@ -19,6 +20,7 @@ nodejs_binary( name = "extract-tokens", data = [ ":extract_tokens_lib", + "@npm//highlight.js", "@npm//sass", ], entry_point = ":extract-tokens.ts", diff --git a/tools/extract-tokens/extract-tokens.ts b/tools/extract-tokens/extract-tokens.ts index 11acd6b831dc..fdbcb471db60 100644 --- a/tools/extract-tokens/extract-tokens.ts +++ b/tools/extract-tokens/extract-tokens.ts @@ -2,6 +2,7 @@ import {readFileSync, writeFileSync} from 'fs'; import {pathToFileURL} from 'url'; import {relative, join, dirname} from 'path'; import {compileString} from 'sass'; +import {highlightCodeBlock} from '../highlight-files/highlight-code-block'; /** Information extracted for a single token from the theme. */ interface ExtractedToken { @@ -31,6 +32,16 @@ interface Token { derivedFrom?: string; } +/** Information extracted from a theme file. */ +interface ThemeData { + /** Name of the theme file. */ + name: string; + /** Name of the `overrides` mixin within the file. */ + overridesMixin: string; + /** Tokens that can be used in the `overrides` mixin. */ + tokens: Token[]; +} + // Script that extracts the tokens from a specific Bazel target. if (require.main === module) { const [packagePath, outputPath, ...inputFiles] = process.argv.slice(2); @@ -52,20 +63,19 @@ if (require.main === module) { throw new Error(`Could not find theme files in ${packagePath}`); } - const themes: {name: string; overridesMixin: string; tokens: Token[]}[] = []; + const themes: ThemeData[] = []; themeFiles.forEach(theme => { - const tokens = extractTokens(theme.filePath); themes.push({ name: theme.mixinPrefix, // This can be derived from the `name` already, but we want the source // of truth to be in this repo, instead of whatever page consumes the data. overridesMixin: `${theme.mixinPrefix}-overrides`, - tokens, + tokens: extractTokens(theme.filePath), }); }); - writeFileSync(outputPath, JSON.stringify(themes)); + writeFileSync(outputPath, JSON.stringify({example: getUsageExample(themes), themes})); } /** @@ -137,6 +147,41 @@ function extractTokens(themePath: string): Token[] { }); } +/** + * Generates a highlighted code snippet that illustrates how an overrides mixin can be used. + * @param themes Themes that were extracted from a specific entrypoint. One of these themes will + * be used as an example. + */ +function getUsageExample(themes: ThemeData[]): string | null { + const mixin = themes.find(theme => theme.tokens.length > 0); + + if (!mixin) { + return null; + } + + // Pick out a couple of color tokens to show as examples. + const firstToken = mixin.tokens.find(token => token.type === 'color'); + const secondToken = mixin.tokens.find(token => token.type === 'color' && token !== firstToken); + + if (!firstToken) { + return null; + } + + const lines = [ + `@use '@angular/material' as mat;`, + ``, + `// Customize the entire app. Change :root to your selector if you want to scope the styles.`, + `:root {`, + ` @include mat.${mixin.overridesMixin}((`, + ` ${firstToken.overridesName}: orange,`, + ...(secondToken ? [` ${secondToken.overridesName}: red,`] : []), + ` ));`, + `}`, + ]; + + return highlightCodeBlock(lines.join('\n'), 'scss'); +} + /** * Generates the code that can be added around a theme file in order to extract its tokens. * @param srcPath Absolute path to the source root.