@@ -7,12 +7,15 @@ import {compileString} from 'sass';
77interface Token {
88 /** Name of the token. */
99 name : string ;
10- /** Prefix under which the token is exposed in the DOM. */
11- prefix : string ;
1210 /** Type of the token (color, typography etc.) */
1311 type : string ;
14- /** System token that it was derived from. */
15- derivedFrom ?: string ;
12+ /** Places from which the token with the specific name can originate. */
13+ sources : {
14+ /** Prefix of the source. */
15+ prefix : string ;
16+ /** System-level token from which the source dervies its value. */
17+ derivedFrom ?: string ;
18+ } [ ] ;
1619}
1720
1821/** Extracted map of tokens from the source Sass files. */
@@ -41,11 +44,17 @@ if (require.main === module) {
4144 throw new Error ( `Could not find theme files in ${ packagePath } ` ) ;
4245 }
4346
44- const themes : { name : string ; tokens : Token [ ] } [ ] = [ ] ;
47+ const themes : { name : string ; overridesMixin : string ; tokens : Token [ ] } [ ] = [ ] ;
4548
4649 themeFiles . forEach ( theme => {
4750 const tokens = extractTokens ( theme . filePath ) ;
48- themes . push ( { name : theme . mixinPrefix , tokens} ) ;
51+ themes . push ( {
52+ name : theme . mixinPrefix ,
53+ // This can be derived from the `name` already, but we want the source
54+ // of truth to be in this repo, instead of whatever page consumes the data.
55+ overridesMixin : `${ theme . mixinPrefix } -overrides` ,
56+ tokens,
57+ } ) ;
4958 } ) ;
5059
5160 writeFileSync ( outputPath , JSON . stringify ( themes ) ) ;
@@ -125,25 +134,46 @@ function extractTokens(themePath: string): Token[] {
125134 * @param groups Extracted data from the Sass file.
126135 */
127136function createTokens ( type : string , groups : ExtractedTokenMap ) : Token [ ] {
128- const result : Token [ ] = [ ] ;
137+ const data : Map < string , Map < string , string | null > > = new Map ( ) ;
129138
139+ // First step is to go through the whole data and group the tokens by their name. We need to
140+ // group the tokens, because the same name can be used by different prefixes (e.g. both
141+ // `mdc-filled-text-field` and `mdc-outlined-text-field` have a `label-text-color` token).
130142 Object . keys ( groups ) . forEach ( prefix => {
131143 const tokens = groups [ prefix ] ;
132144
133145 // The token data for some components may be null.
134146 if ( tokens ) {
135147 Object . keys ( tokens ) . forEach ( name => {
148+ let tokenData = data . get ( name ) ;
149+
150+ if ( ! tokenData ) {
151+ tokenData = new Map ( ) ;
152+ data . set ( name , tokenData ) ;
153+ }
154+
136155 const value = tokens [ name ] ;
137156 const derivedFrom = typeof value === 'string' ? textBetween ( value , 'var(' , ')' ) : null ;
138- const token : Token = { name, prefix, type} ;
139- if ( derivedFrom ) {
140- token . derivedFrom = derivedFrom . replace ( '--sys-' , '' ) ;
141- }
142- result . push ( token ) ;
157+ tokenData . set ( prefix , derivedFrom ? derivedFrom . replace ( '--sys-' , '' ) : null ) ;
143158 } ) ;
144159 }
145160 } ) ;
146161
162+ // After the tokens have been grouped, we can create the `Token` object for each one.
163+ const result : Token [ ] = [ ] ;
164+
165+ data . forEach ( ( tokenData , name ) => {
166+ const token : Token = { name, type, sources : [ ] } ;
167+
168+ tokenData . forEach ( ( derivedFrom , prefix ) => {
169+ // Set `derivedFrom` to `undefined` if it hasn't been set so it doesn't show up in the JSON.
170+ token . sources . push ( { prefix, derivedFrom : derivedFrom || undefined } ) ;
171+ } ) ;
172+
173+ result . push ( token ) ;
174+ } ) ;
175+
176+ // Sort the tokens by name so they're easier to scan.
147177 return result . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
148178}
149179
0 commit comments