<commit_before>export interface Style {
	key?: string;
	parent?: Style;
}

export interface StyleRegistry {
	styles() : {[name: string] : Style} 
	add(style: Style) : void;
}

class StyleRegistryImpl implements StyleRegistry {
	private styleMap: {[name: string]: Style};

	constructor() {
		this.styleMap = {};
	}

	add(style: Style) {
		this.styleMap[style.key] = style;
	}

	styles() {
		return this.styleMap;
	}
}

export var registry: StyleRegistry = new StyleRegistryImpl();

function addKeys(tree: any) {
	Object.keys(tree).forEach((k) => {
		var prop = tree[k];
		if (typeof prop === 'object' &&
		    prop !== tree &&
			prop.key === undefined &&
			prop.parent === undefined) {
			addKeys(prop);

			prop.key = k;
			prop.parent = tree;
		}
	});
}

export function create<T>(tree: T) : T {
	addKeys(tree);

	Object.keys(tree).forEach((k) => {
		var style = <Style>(<any>tree)[k];
		if (style.key) {
			registry.add(style);
		}
	});

	return tree;
}

function className(style: Style) : string {
	var name = style.key ? style.key : '';
	if (style.parent) {
		var parentClass = className(style.parent);
		if (parentClass) {
			name = parentClass + '-' + name;
		}
	}
	return name;
}

export function classes<T>(...objects: T[]) : string {
	var classNames = '';
	objects.forEach((object) => {
		if (!object) {
			return;
		}
		var compiled = <Style>object;
		if (classNames.length > 0) {
			classNames += ' ';
		}
		classNames += className(compiled);
	});
	return classNames;
}

function cssPropName(name: string) {
	// adapted from React's hyphenate.js
	var uppercasePattern = /([A-Z])/g;
	return name.replace(uppercasePattern, '-$1').toLowerCase();
}

function cssPropValue(value: any) {
	if (typeof value == 'number') {
		return value + 'px';
	} else {
		return value.toString();
	}
}

function cssClass(name: string, exprs: string[]) {
	var css = '.' + name + ' {\n';
	exprs.forEach((expr, index) => {
		css += '  ' + expr + ';\n';
	});
	css += '}';
	return css;
}

export function compile<T>(tree: T) : string {
	var classes: string[] = [];
	var cssProps: string[] = [];
	Object.keys(tree).forEach((k) => {
		if (k === 'key' || k === 'parent') {
			return;
		}

		var prop = (<any>tree)[k];
		if (typeof prop == 'object') {
			classes.push(compile(prop));
		} else {
			cssProps.push(cssPropName(k) + ': ' + cssPropValue(prop));
		}
	});
	
	var style: Style = tree;
	var css = style.key ? cssClass(className(style), cssProps) : '';
	return [css].concat(classes).join('\n\n');
}
<commit_msg>Add documentation for style.ts<commit_after>/*
   style.ts provides a minimalistic way to define CSS
   classes using JavaScript structures.

   For the rationale for using JS as a CSS generator, see
   https://speakerdeck.com/vjeux/react-css-in-js .

   In addition to the benefits discussed there,
   defining them in TypeScript allows compile-time
   checking of style references in components.

   Unlike the implementation described in that presentation,
   this implementation does not use inline styles but
   instead generates CSS classes in a very simple way.
   
   The resulting CSS is consequently easy to read/tweak/post-process
   and work with in browser development tools.
*/

export interface Style {
	key?: string;
	parent?: Style;
}

export interface StyleRegistry {
	styles() : {[name: string] : Style} 
	add(style: Style) : void;
}

class StyleRegistryImpl implements StyleRegistry {
	private styleMap: {[name: string]: Style};

	constructor() {
		this.styleMap = {};
	}

	add(style: Style) {
		this.styleMap[style.key] = style;
	}

	styles() {
		return this.styleMap;
	}
}

export var registry: StyleRegistry = new StyleRegistryImpl();

function addKeys(tree: any) {
	Object.keys(tree).forEach((k) => {
		var prop = tree[k];
		if (typeof prop === 'object' &&
		    prop !== tree &&
			prop.key === undefined &&
			prop.parent === undefined) {
			addKeys(prop);

			prop.key = k;
			prop.parent = tree;
		}
	});
}

/** Given an object tree that defines styles
  * and their properties, returns a tree of Style
  * objects that can later be passed to compile()
  * to generate CSS or className() to get the class
  * names for a style.
  *
  * eg:
  *   var styles = style.create({
  *     classA: {
  *       backgroundColor: 'white',
  *       width: 100
  *     }
  *   });
  *   style.compile(styles);
  *
  * Generates:
  *
  *  .classA {
  *    background-color: 'white';
  *    width: 100px;
  *  }
  */
export function create<T>(tree: T) : T {
	addKeys(tree);

	Object.keys(tree).forEach((k) => {
		var style = <Style>(<any>tree)[k];
		if (style.key) {
			registry.add(style);
		}
	});

	return tree;
}

function className(style: Style) : string {
	var name = style.key ? style.key : '';
	if (style.parent) {
		var parentClass = className(style.parent);
		if (parentClass) {
			name = parentClass + '-' + name;
		}
	}
	return name;
}

/** Given a list of objects from a style tree generated
  * by style.create(), returns a space-separated list
  * of class-names for those styles in the CSS generated
  * by compile()
  */
export function classes<T>(...objects: T[]) : string {
	var classNames = '';
	objects.forEach((object) => {
		if (!object) {
			return;
		}
		var compiled = <Style>object;
		if (classNames.length > 0) {
			classNames += ' ';
		}
		classNames += className(compiled);
	});
	return classNames;
}

function cssPropName(name: string) {
	// adapted from React's hyphenate.js
	var uppercasePattern = /([A-Z])/g;
	return name.replace(uppercasePattern, '-$1').toLowerCase();
}

function cssPropValue(value: any) {
	if (typeof value == 'number') {
		return value + 'px';
	} else {
		return value.toString();
	}
}

function cssClass(name: string, exprs: string[]) {
	var css = '.' + name + ' {\n';
	exprs.forEach((expr, index) => {
		css += '  ' + expr + ';\n';
	});
	css += '}';
	return css;
}

/** Given a style tree generated by create(),
  * returns the CSS for the classes defined
  * in that tree.
  */
export function compile<T>(tree: T) : string {
	var classes: string[] = [];
	var cssProps: string[] = [];
	Object.keys(tree).forEach((k) => {
		if (k === 'key' || k === 'parent') {
			return;
		}

		var prop = (<any>tree)[k];
		if (typeof prop == 'object') {
			classes.push(compile(prop));
		} else {
			cssProps.push(cssPropName(k) + ': ' + cssPropValue(prop));
		}
	});
	
	var style: Style = tree;
	var css = style.key ? cssClass(className(style), cssProps) : '';
	return [css].concat(classes).join('\n\n');
}

{'fraction_non_alphanumeric': 0.16586724431378616, 'fraction_numerical': 0.0015472690700912889, 'mean_word_length': 3.338255033557047, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 29, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3582726', 'n_tokens_mistral': 2323, 'n_tokens_neox': 2181, 'n_words': 588}