From 8821ce817830ec069161d7ba1c7f9951c91bf5f6 Mon Sep 17 00:00:00 2001 From: Johannes Lumpe Date: Sun, 11 Nov 2018 18:51:15 -0500 Subject: [PATCH] feat: length generics --- package.json | 4 +- src/constants.ts | 4 + src/customSyntaxes.ts | 46 ++++++++++++ src/{index.ts => generateAllTypes.ts} | 75 +++++++------------ src/generateTypeNodes.ts | 39 +++++++--- src/generateTypesFromMdnData.ts | 70 +++++++++++------ src/generateUnitTypeSourceFiles.ts | 39 ++++++---- src/types.ts | 1 - .../createBrandedTypeInterfaceForUnit.ts | 5 +- ...generateComponentMultiplierPermutations.ts | 3 +- src/utils/generateComponentPermutations.ts | 32 +++++--- src/utils/generateTypeCombinations.ts | 20 +++-- src/utils/generateUnitValueInterface.ts | 3 + src/utils/getUnits.ts | 6 +- src/utils/groupTokensByCombinatorPredence.ts | 4 +- types/mdn-data/index.d.ts | 34 +++++---- 16 files changed, 245 insertions(+), 140 deletions(-) create mode 100644 src/customSyntaxes.ts rename src/{index.ts => generateAllTypes.ts} (85%) create mode 100644 src/utils/generateUnitValueInterface.ts diff --git a/package.json b/package.json index 26e2755..bccfe6f 100644 --- a/package.json +++ b/package.json @@ -2,15 +2,13 @@ "name": "@johanneslumpe/css-types", "version": "0.1.0", "description": "CSS types and value helpers generated from MDN data", - "main": "lib/index.js", - "module": "es/index.js", "types": "lib/index.d.ts", "scripts": { "test": "tsc -p ./tsconfig.cjs.json --noEmit && jest", "lint": "tslint --project tsconfig.json", "format": "prettier --write \"src/**/*.ts\"", "test:watch": "jest --watch", - "build": "rimraf ./lib && rimraf ./es && tsc -p ./tsconfig.cjs.json && tsc -p ./tsconfig.esm.json", + "build": "rimraf ./lib && npx ts-node src/generateAllTypes", "prepare": "npm run build && npm run docs", "prepublishOnly": "npm test && npm run lint", "preversion": "npm run lint", diff --git a/src/constants.ts b/src/constants.ts index e2fb534..7f30bb0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1 +1,5 @@ export const TYPES_BUILD_DIR = '../lib/'; +export const LENGTH_GENERIC_ARGUMENT = 'TLength'; +export const LENGTH_TYPE_NAME = 'Length'; +export const UNIT_TYPES_FILE = 'unitTypes.ts'; +export const UNIT_UTILS_FILE = 'unitUtils.ts'; diff --git a/src/customSyntaxes.ts b/src/customSyntaxes.ts new file mode 100644 index 0000000..6f37763 --- /dev/null +++ b/src/customSyntaxes.ts @@ -0,0 +1,46 @@ +export const customSyntaxes = { + // https://developer.mozilla.org/en-US/docs/Web/CSS/angle + angle: { + syntax: ' | | | ', + }, + // https://developer.mozilla.org/en-US/docs/Web/CSS/color + color: { + syntax: '', + }, + // https://developer.mozilla.org/en-US/docs/Web/CSS/flex_value + flex: { + syntax: '', + }, + // https://developer.mozilla.org/en-US/docs/Web/CSS/frequency + frequency: { + syntax: ' | ', + }, + 'inset()': { + syntax: '', + }, + integer: { + syntax: '', + }, + number: { + syntax: '', + }, + percentage: { + syntax: '', + }, + // https://developer.mozilla.org/en-US/docs/Web/CSS/resolution + resolution: { + syntax: ' | | | ', + }, + shape: { + syntax: '', + }, + // https://developer.mozilla.org/en-US/docs/Web/CSS/time + time: { + // hacky fix to get the interface name generation function + // to play nice with single letter camel case + syntax: ' | ', + }, + 'track-repeat': { + syntax: '', + }, +}; diff --git a/src/index.ts b/src/generateAllTypes.ts similarity index 85% rename from src/index.ts rename to src/generateAllTypes.ts index eae07e4..b293709 100644 --- a/src/index.ts +++ b/src/generateAllTypes.ts @@ -1,13 +1,13 @@ -import { writeFileSync } from 'fs'; import * as fs from 'fs'; -import { filter, find } from 'lodash/fp'; +import { find, forEach, map } from 'lodash/fp'; import properties from 'mdn-data/css/properties.json'; import syntaxes from 'mdn-data/css/syntaxes.json'; import * as path from 'path'; import rimraf from 'rimraf'; import ts from 'typescript'; -import { TYPES_BUILD_DIR } from './constants'; +import { LENGTH_TYPE_NAME, TYPES_BUILD_DIR } from './constants'; +import { customSyntaxes } from './customSyntaxes'; import { generateBaseDataTypes, generateTypesFromMdnData, @@ -16,8 +16,8 @@ import { generateCombinedLengthType, generateUnitTypesSourceFiles, } from './generateUnitTypeSourceFiles'; +import { generateUnitvalueInterface } from './utils/generateUnitvalueInterface'; -// create directory to store out types in const outputDir = path.join(__dirname, TYPES_BUILD_DIR); rimraf.sync(outputDir); fs.mkdirSync(outputDir); @@ -25,52 +25,24 @@ fs.mkdirSync(outputDir); const isSelectorKey = (key: string) => key.includes('selector'); // generate sources +const { + unitTypes, + sourceFiles: unitSourceFiles, +} = generateUnitTypesSourceFiles(); // custom types to prevent invalid references // after removing function types -const customSyntaxes = { - // https://developer.mozilla.org/en-US/docs/Web/CSS/angle - angle: { - syntax: ' | | | ', - }, - color: { - syntax: '', - }, - // https://developer.mozilla.org/en-US/docs/Web/CSS/flex_value - flex: { - syntax: '', - }, - 'inset()': { - syntax: '', - }, - integer: { - syntax: '', - }, - percentage: { - syntax: '', - }, - shape: { - syntax: '', - }, - 'track-repeat': { - syntax: '', - }, -}; - const customTypes = generateTypesFromMdnData(customSyntaxes, { availableTypes: [ - 'IDegValue', - 'IGradValue', - 'IRadValue', - 'ITurnValue', - 'IFrValue', - 'IPercentageValue', + ...map(unitType => generateUnitvalueInterface(unitType.name), unitTypes), + 'i-s-value', + 'i-x-value', ], }); const baseTypes = generateBaseDataTypes([ // ignore types which clash with custom added types - 'length', + LENGTH_TYPE_NAME.toLowerCase(), ...Object.keys(customSyntaxes), // ignore all properties and syntaxes ...Object.keys(syntaxes), @@ -94,7 +66,6 @@ const propertyTypes = generateTypesFromMdnData(properties, { }, typeSuffix: 'Property', }); -const unitSourceFiles = generateUnitTypesSourceFiles(); const typesOutputSource = ts.updateSourceFileNode( ts.createSourceFile( @@ -117,18 +88,24 @@ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, }); -[...unitSourceFiles, typesOutputSource].forEach(sourceFile => { - writeFileSync( - path.join(__dirname, TYPES_BUILD_DIR, sourceFile.fileName), - printer.printFile(sourceFile), - ); -}); +forEach( + sourceFile => { + fs.writeFileSync( + path.join(__dirname, TYPES_BUILD_DIR, sourceFile.fileName), + printer.printFile(sourceFile), + ); + }, + [...unitSourceFiles, typesOutputSource], +); // TODO -// DONE - collapse nested tuples with same length into tuples with unions -// - handle special types like `string`, `number`, `percentage`, `length-percentage`, `length`, `flex`, etc. +// - length type generics +// - fold similar tuples into one // - clean up type `generateTypeCombinations` // - handle single data-type with curly braces multiplier. doesn't seem to be repeated?! +// - create color function utils (rgb, hsla, etc) from css grammar to be used within `color` type +// - support finite version of `+` and `*` +// - faciliate generation of combined keywords if a syntax is only made up of keywords and data types which contain only keywords // const grammar = ' | '; // console.log('parsing grammar:', grammar); diff --git a/src/generateTypeNodes.ts b/src/generateTypeNodes.ts index 5b0692e..0d60319 100644 --- a/src/generateTypeNodes.ts +++ b/src/generateTypeNodes.ts @@ -1,7 +1,8 @@ import { ICssTokenType } from '@johanneslumpe/css-value-declaration-grammer-lexer'; -import { compact, filter, flatMap, map, reject } from 'lodash'; +import { compact, filter, flatMap, map, reduce, reject } from 'lodash/fp'; import ts from 'typescript'; +import { LENGTH_TYPE_NAME } from './constants'; import { ComponentTypeRepresentation, ComponentTypes, @@ -10,11 +11,21 @@ import { } from './types'; import { generateTypeName } from './utils/generateTypeName'; +const LENGTH_GENERIC_ARGUMENT = 'TLength'; + const typeReferenceOverrideMap: { [index: string]: string } = { + // all lengths need to use generic in order to allow customization by the consumer + [LENGTH_TYPE_NAME.toLowerCase()]: LENGTH_GENERIC_ARGUMENT, number: 'number', string: 'string', }; +const genericOverrideMap: { [index: string]: string } = { + // length percentages need to pass through the generic argument to allow customization + // to flow through the type + 'length-percentage': LENGTH_GENERIC_ARGUMENT, +}; + export const generateTsNode = (component: IComponent) => { switch (component.type) { case ICssTokenType.KEYWORD: @@ -31,7 +42,14 @@ export const generateTsNode = (component: IComponent) => { return ts.createTypeReferenceNode( typeReferenceOverrideMap[withoutAngleBrackets] || generateTypeName(withoutAngleBrackets), - [], + genericOverrideMap[withoutAngleBrackets] + ? [ + ts.createTypeReferenceNode( + genericOverrideMap[withoutAngleBrackets], + [], + ), + ] + : [], ); default: @@ -45,9 +63,9 @@ export const generateTsNode = (component: IComponent) => { export const generateTypesNodes = (data: INestedComponentArray): any => { const { representation } = data; const componentsWithoutVoid = filter( - data, item => Array.isArray(item) ? !!item.length : item.type !== ComponentTypes.VOID, + data, ); switch (representation) { @@ -59,13 +77,13 @@ export const generateTypesNodes = (data: INestedComponentArray): any => { return ts.createTupleTypeNode( compact( - flatMap(componentsWithoutVoid, item => { + flatMap(item => { if (!Array.isArray(item)) { return generateTypesNodes([item]); } // nested tuple nodes need to be lifted up into their parent tuples - const flattenedTuples = item.reduce( + const flattenedTuples = reduce( (acc, entry) => { if (!Array.isArray(entry)) { acc.push(entry); @@ -80,9 +98,10 @@ export const generateTypesNodes = (data: INestedComponentArray): any => { return acc; }, [] as any[], + item, ); return generateTypesNodes(flattenedTuples); - }), + }, componentsWithoutVoid), ), ); } @@ -90,13 +109,13 @@ export const generateTypesNodes = (data: INestedComponentArray): any => { case ComponentTypeRepresentation.UNION: const nestedValues = compact( reject( - map(componentsWithoutVoid, item => { + item => Array.isArray(item) && !item.length, + map(item => { if (Array.isArray(item)) { return generateTypesNodes(item); } return generateTsNode(item); - }), - item => Array.isArray(item) && !item.length, + }, componentsWithoutVoid), ), ); // TODO fold tuples @@ -131,8 +150,8 @@ export const generateTypesNodes = (data: INestedComponentArray): any => { ? generateTsNode(first) : compact( map( - componentsWithoutVoid, item => (Array.isArray(item) ? undefined : generateTsNode(item)), + componentsWithoutVoid, ), ); } diff --git a/src/generateTypesFromMdnData.ts b/src/generateTypesFromMdnData.ts index 415025a..61679bd 100644 --- a/src/generateTypesFromMdnData.ts +++ b/src/generateTypesFromMdnData.ts @@ -3,12 +3,20 @@ import { ICssTokenType, lexValueDeclarationGrammar, } from '@johanneslumpe/css-value-declaration-grammer-lexer'; -import { difference, filter, map, omit } from 'lodash/fp'; -import { IRawSyntax, IRawSyntaxes } from 'mdn-data'; -import { IRawProperties } from 'mdn-data/css/properties.json'; +import { + difference, + filter, + forEach, + map, + omit, + reduce, + some, +} from 'lodash/fp'; +import { IRawProperties, IRawSyntax, IRawSyntaxes } from 'mdn-data'; import mdnCssTypes from 'mdn-data/css/types.json'; import ts, { TypeAliasDeclaration } from 'typescript'; +import { LENGTH_TYPE_NAME } from './constants'; import { generateTypesNodes } from './generateTypeNodes'; import { INestedComponentArray, RawToken } from './types'; import { convertRawTokensToComponents } from './utils/convertRawTokensToComponents'; @@ -23,7 +31,7 @@ const globalDataTypes = Object.keys(mdnCssTypes); const getDataTypeKey = (str: string) => str.replace(/<\'?(.*?)\'?>/, '$1'); const appendSuffixToDataTypes = (tokens: RawToken[], suffix: string) => { - tokens.forEach(token => { + forEach(token => { if (token.type !== ICssTokenType.DATA_TYPE) { return; } @@ -32,7 +40,7 @@ const appendSuffixToDataTypes = (tokens: RawToken[], suffix: string) => { } token.value = parenthesesToFunction(token.value); - }); + }, tokens); }; // function isPlusMultiplier(token: RawToken) { @@ -68,7 +76,7 @@ const appendSuffixToDataTypes = (tokens: RawToken[], suffix: string) => { // } const containsUnsupportedSyntax = (tokens: RawToken[]) => - tokens.some( + some( token => token.type === ICssTokenType.FUNCTION || token.type === ICssTokenType.LITERAL || @@ -77,6 +85,7 @@ const containsUnsupportedSyntax = (tokens: RawToken[]) => token.data && token.data.subType === ICssMultiplierTokenType.HASH_MARK ), + tokens, ); const isDataTypeToken = (token: RawToken) => @@ -87,7 +96,7 @@ const removeCircularAndUnsupportedGrammar = ( ) => { const keysPerSource = Object.keys(sourceObject); const keysToRemove: string[] = []; - keysPerSource.forEach(key => { + forEach(key => { if (globalDataTypes.includes(key)) { return; } @@ -100,7 +109,7 @@ const removeCircularAndUnsupportedGrammar = ( const dataTypes = filter(isDataTypeToken, emittedTokens); - dataTypes.forEach(dataType => { + forEach(dataType => { const dataTypeKey = getDataTypeKey(dataType.value); if (dataTypeKey === key) { keysToRemove.push(key); @@ -113,11 +122,11 @@ const removeCircularAndUnsupportedGrammar = ( ) { checkCircularKeys(sourceObject[dataTypeKey]); } - }); + }, dataTypes); }; checkCircularKeys(sourceObject[key]); - }); + }, keysPerSource); return omit(keysToRemove, sourceObject); }; @@ -132,7 +141,7 @@ const removeInvalidDataTypeReferences = ( do { const keysToRemove: string[] = []; - Object.keys(newObject).forEach(key => { + forEach(key => { const syntax = newObject[key]; const { emittedTokens } = lexValueDeclarationGrammar(syntax.syntax); const dataTypeKeys = map( @@ -146,7 +155,7 @@ const removeInvalidDataTypeReferences = ( if (invalidDataTypes.length) { keysToRemove.push(key); } - }); + }, Object.keys(newObject)); keys = keysToRemove; newObject = omit(keysToRemove, newObject); @@ -173,18 +182,41 @@ const removeInvalidDataTypeReferences = ( // suffix: string = '', // ) => generateTypeReferenceDeclaration(key, 'string', suffix); +const findDataTypeInTree = ( + tree: INestedComponentArray, + dataType: string, +): boolean => { + return tree.some( + node => + Array.isArray(node) + ? findDataTypeInTree(node, dataType) + : node.type === ICssTokenType.DATA_TYPE && + node.value.includes(dataType), + ); +}; + const generateTypesForKey = ( key: string, suffix: string, value: INestedComponentArray, -) => - ts.createTypeAliasDeclaration( +) => { + const usesLength = findDataTypeInTree(value, LENGTH_TYPE_NAME.toLowerCase()); + return ts.createTypeAliasDeclaration( [], [ts.createModifier(ts.SyntaxKind.ExportKeyword)], typeNameWithSuffix(parenthesesToFunction(key), suffix), - [], + usesLength + ? [ + ts.createTypeParameterDeclaration( + 'TLength', + undefined, + ts.createTypeReferenceNode(LENGTH_TYPE_NAME, []), + ), + ] + : [], generateTypesNodes(value), ); +}; export const generateBaseDataTypes = (keysToIgnore: string[]) => [ map( @@ -203,11 +235,6 @@ export const generateTypesFromMdnData = ( sourceObject: IRawSyntaxes, options: IGenerateTypesFromMdnDataOptions = {}, ) => { - // TODO - // - figure out how to keep types like `color` in, which require functions etc. - // - faciliate generation of combined keywords if a syntax is only made up of keywords and data types which contain only keywords - // - faciliate proper parsing of infinite multipliers by replacing them with finite counterparts - const { availableTypes = [], typeSuffix = '' } = options; const withoutCircular = removeCircularAndUnsupportedGrammar(sourceObject); const withoutInvalidReferences = removeInvalidDataTypeReferences( @@ -215,7 +242,7 @@ export const generateTypesFromMdnData = ( availableTypes, ); - const typeDeclarations = Object.keys(withoutInvalidReferences).reduce( + const typeDeclarations = reduce( (acc, key) => { if (options.blacklistPredicate && options.blacklistPredicate(key)) { return acc; @@ -254,6 +281,7 @@ export const generateTypesFromMdnData = ( return acc; }, [] as TypeAliasDeclaration[], + Object.keys(withoutInvalidReferences), ); return { diff --git a/src/generateUnitTypeSourceFiles.ts b/src/generateUnitTypeSourceFiles.ts index 719adad..aa54308 100644 --- a/src/generateUnitTypeSourceFiles.ts +++ b/src/generateUnitTypeSourceFiles.ts @@ -1,15 +1,17 @@ import { flatten, map } from 'lodash/fp'; -import { CSSUnitGroups } from 'mdn-data'; +import { CSSUnitGroups, IUnit } from 'mdn-data'; import ts from 'typescript'; +import { + LENGTH_TYPE_NAME, + UNIT_TYPES_FILE, + UNIT_UTILS_FILE, +} from './constants'; import { createBrandedTypeInterfaceForUnit } from './utils/createBrandedTypeInterfaceForUnit'; import { createUnitFunctionDeclaration } from './utils/createUnitFunctionDeclaration'; import { generateTypeName } from './utils/generateTypeName'; import { getUnits, lengthValueTags } from './utils/getUnits'; -const UNIT_TYPES_FILE = 'unitTypes.ts'; -const UNIT_UTILS_FILE = 'unitUtils.ts'; - const getUnitsWithAdditionalTypes = (tagFilterArray?: CSSUnitGroups[]) => [ ...getUnits(tagFilterArray), { @@ -19,11 +21,11 @@ const getUnitsWithAdditionalTypes = (tagFilterArray?: CSSUnitGroups[]) => [ }, ]; -export const generateUnitInterfaces = ( +export function generateUnitInterfaces( withAdditionalTypes: boolean, tagFilterArray?: CSSUnitGroups[], -) => - flatten( +) { + return flatten( map( unit => createBrandedTypeInterfaceForUnit(unit.unit, unit.name, unit.brandKey), @@ -32,6 +34,7 @@ export const generateUnitInterfaces = ( : getUnits(tagFilterArray), ), ); +} export const generateUnitTypesImportStatement = ( interfaces: ts.InterfaceDeclaration[], @@ -45,10 +48,10 @@ export const generateUnitTypesImportStatement = ( map(val => ts.createImportSpecifier(undefined, val.name), interfaces), ), ), - ts.createStringLiteral(`./${UNIT_TYPES_FILE}`), + ts.createStringLiteral(`./${UNIT_TYPES_FILE.replace('.ts', '')}`), ); -export const generateUnitTypesSourceFiles = () => { +export function generateUnitTypesSourceFiles() { const utilsSrc = ts.createSourceFile( UNIT_UTILS_FILE, '', @@ -67,16 +70,20 @@ export const generateUnitTypesSourceFiles = () => { const interfaces = generateUnitInterfaces(true); const typesImportDeclaration = generateUnitTypesImportStatement(interfaces); + const unitTypes = getUnitsWithAdditionalTypes(); // native map because lodash/fp does not give us access to the index property - const unitUtils = getUnitsWithAdditionalTypes().map((unit, index) => + const unitUtils = unitTypes.map((unit, index) => createUnitFunctionDeclaration(interfaces[index], unit.unit, unit.name), ); - return [ - ts.updateSourceFileNode(utilsSrc, [typesImportDeclaration, ...unitUtils]), - ts.updateSourceFileNode(typesSrc, interfaces), - ]; -}; + return { + sourceFiles: [ + ts.updateSourceFileNode(utilsSrc, [typesImportDeclaration, ...unitUtils]), + ts.updateSourceFileNode(typesSrc, interfaces), + ], + unitTypes, + }; +} export const generateCombinedLengthType = () => { const interfaces = generateUnitInterfaces(false, lengthValueTags); @@ -86,7 +93,7 @@ export const generateCombinedLengthType = () => { const types = [ { - name: 'length', + name: LENGTH_TYPE_NAME.toLowerCase(), values: interfaces, }, ]; diff --git a/src/types.ts b/src/types.ts index 84f33a6..b2f4e96 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,6 @@ import { IBaseLexToken } from '@johanneslumpe/basic-lexer'; import { - formatTokens, IAdditionalTokenData, ICssCombinatorTokenType, ICssMultiplierTokenType, diff --git a/src/utils/createBrandedTypeInterfaceForUnit.ts b/src/utils/createBrandedTypeInterfaceForUnit.ts index 515677b..c188825 100644 --- a/src/utils/createBrandedTypeInterfaceForUnit.ts +++ b/src/utils/createBrandedTypeInterfaceForUnit.ts @@ -1,5 +1,6 @@ import ts from 'typescript'; +import { generateUnitvalueInterface } from './generateUnitvalueInterface'; export function createBrandedTypeInterfaceForUnit( unit: string, name: string, @@ -8,9 +9,7 @@ export function createBrandedTypeInterfaceForUnit( return ts.createInterfaceDeclaration( [], [ts.createModifier(ts.SyntaxKind.ExportKeyword)], - ts.createIdentifier( - `I${name.substr(0, 1).toUpperCase()}${name.substr(1)}Value`, - ), + ts.createIdentifier(generateUnitvalueInterface(name)), [], [ ts.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ diff --git a/src/utils/generateComponentMultiplierPermutations.ts b/src/utils/generateComponentMultiplierPermutations.ts index 5df5baf..f1a4274 100644 --- a/src/utils/generateComponentMultiplierPermutations.ts +++ b/src/utils/generateComponentMultiplierPermutations.ts @@ -16,9 +16,10 @@ export function generateComponentMultiplierPermutations( const component = arr[0]; if (!component.multiplier) { - return nestedResult.reduce( + return reduce( (acc, componentArr) => acc.concat([[component].concat(componentArr)]), result, + nestedResult, ); } let min: number = 0; diff --git a/src/utils/generateComponentPermutations.ts b/src/utils/generateComponentPermutations.ts index 220b11f..595d0af 100644 --- a/src/utils/generateComponentPermutations.ts +++ b/src/utils/generateComponentPermutations.ts @@ -1,4 +1,5 @@ import { ICssMultiplierTokenType } from '@johanneslumpe/css-value-declaration-grammer-lexer'; +import { forEach, reduce } from 'lodash/fp'; import { IComponent, INestedComponentArray } from '../types'; import { generateComponentMultiplierPermutations } from './generateComponentMultiplierPermutations'; @@ -22,16 +23,23 @@ export function generateComponentPermutations( const nestedResult = generateComponentPermutations(arrays.slice(1)); const result: IComponent[][] = []; - return generateComponentMultiplierPermutations( - arrays[0] as IComponent[], - ).reduce((acc, arr) => { - if (isCurlyBraceMultiplierArray(arr)) { - nestedResult.forEach(nested => acc.push(arr.concat(nested))); - } else { - arr.forEach(component => - nestedResult.forEach(nested => acc.push([component].concat(nested))), - ); - } - return acc; - }, result); + return reduce( + (acc, arr) => { + if (isCurlyBraceMultiplierArray(arr)) { + forEach(nested => acc.push(arr.concat(nested)), nestedResult); + } else { + forEach( + component => + forEach( + nested => acc.push([component].concat(nested)), + nestedResult, + ), + arr, + ); + } + return acc; + }, + result, + generateComponentMultiplierPermutations(arrays[0] as IComponent[]), + ); } diff --git a/src/utils/generateTypeCombinations.ts b/src/utils/generateTypeCombinations.ts index bd2760d..b2690e2 100644 --- a/src/utils/generateTypeCombinations.ts +++ b/src/utils/generateTypeCombinations.ts @@ -3,7 +3,7 @@ import { ICssMultiplierTokenType, ICssTokenType, } from '@johanneslumpe/css-value-declaration-grammer-lexer'; -import { compact, every, find, flatten, map, reject } from 'lodash/fp'; +import { compact, every, find, flatten, map, reduce, reject } from 'lodash/fp'; import { ComponentArray, @@ -97,11 +97,15 @@ export function generateTypeCombinations( const r1 = generateTypeCombinations(entity.entities); // we lift the nested representation up when resolving the group - const type: ComponentTypeRepresentation = r1.reduce((acc, item) => { - return Array.isArray(item) - ? acc || item.representation || ComponentTypeRepresentation.NONE - : acc; - }, ComponentTypeRepresentation.NONE); + const type: ComponentTypeRepresentation = reduce( + (acc, item) => { + return Array.isArray(item) + ? acc || item.representation || ComponentTypeRepresentation.NONE + : acc; + }, + ComponentTypeRepresentation.NONE, + r1, + ); const combinations = flatten( generateTypeCombinations(entity.entities), @@ -115,6 +119,10 @@ export function generateTypeCombinations( final.representation = type; return final; } + + default: { + throw new Error('unsupported case hit'); + } } }, entities), ); diff --git a/src/utils/generateUnitValueInterface.ts b/src/utils/generateUnitValueInterface.ts new file mode 100644 index 0000000..330dcc5 --- /dev/null +++ b/src/utils/generateUnitValueInterface.ts @@ -0,0 +1,3 @@ +export function generateUnitvalueInterface(name: string) { + return `I${name.substr(0, 1).toUpperCase()}${name.substr(1)}Value`; +} diff --git a/src/utils/getUnits.ts b/src/utils/getUnits.ts index d3e169f..dd60351 100644 --- a/src/utils/getUnits.ts +++ b/src/utils/getUnits.ts @@ -1,3 +1,4 @@ +import { reduce } from 'lodash/fp'; import { CSSUnitGroups } from 'mdn-data'; import units from 'mdn-data/css/units.json'; @@ -18,14 +19,14 @@ export const lengthValueTags: CSSUnitGroups[] = [ 'CSS Lengths', ]; -interface IUnit { +export interface IUnit { name: string; unit: string; brandKey?: string; } export function getUnits(tagsToFilterFor?: CSSUnitGroups[]) { - return Object.keys(units).reduce( + return reduce( (acc, key) => { const lowerCaseKey = key.toLowerCase(); const unit = units[key]; @@ -43,5 +44,6 @@ export function getUnits(tagsToFilterFor?: CSSUnitGroups[]) { return acc; }, [] as IUnit[], + Object.keys(units), ); } diff --git a/src/utils/groupTokensByCombinatorPredence.ts b/src/utils/groupTokensByCombinatorPredence.ts index 38b32d3..ebcdb3a 100644 --- a/src/utils/groupTokensByCombinatorPredence.ts +++ b/src/utils/groupTokensByCombinatorPredence.ts @@ -2,6 +2,7 @@ import { ICssCombinatorTokenType, ICssTokenType, } from '@johanneslumpe/css-value-declaration-grammer-lexer'; +import { reduce } from 'lodash/fp'; import { ComponentArray, @@ -103,7 +104,7 @@ export function groupTokensByCombinatorPredence( const result: ComponentArray = []; const combinatorGroup = createCombinatorGroup( combinator, - groupedComponentValues.reduce( + reduce( (acc, tokenGroup) => [ ...acc, ...groupTokensByCombinatorPredence( @@ -112,6 +113,7 @@ export function groupTokensByCombinatorPredence( ), ], result, + groupedComponentValues, ), ); return [combinatorGroup]; diff --git a/types/mdn-data/index.d.ts b/types/mdn-data/index.d.ts index e68c342..a4837c4 100644 --- a/types/mdn-data/index.d.ts +++ b/types/mdn-data/index.d.ts @@ -23,18 +23,30 @@ declare module 'mdn-data' { groups: string[]; }; } -} - -declare module 'mdn-data/css/units.json' { - import { CSSUnitGroups, CSSStatus, IRawSyntaxes } from 'mdn-data'; - interface IUnit { + export interface IUnit { groups: CSSUnitGroups[]; status: CSSStatus; } - interface IUnits { + + export interface IUnits { [index: string]: IUnit; } + + export type PropertyStatus = 'standard' | 'nonstandard' | 'experimental'; + + export interface IRawProperty extends IRawSyntax { + status: PropertyStatus; + } + + export interface IRawProperties { + [index: string]: IRawProperty; + } +} + +declare module 'mdn-data/css/units.json' { + import { IUnits } from 'mdn-data'; + const value: IUnits; export default value; } @@ -46,16 +58,8 @@ declare module 'mdn-data/css/syntaxes.json' { } declare module 'mdn-data/css/properties.json' { - import { IRawSyntax } from 'mdn-data'; - - type PropertyStatus = 'standard' | 'nonstandard' | 'experimental'; - export interface IRawProperty extends IRawSyntax { - status: PropertyStatus; - } + import { IRawProperties } from 'mdn-data'; - export interface IRawProperties { - [index: string]: IRawProperty; - } const value: IRawProperties; export default value; }