Skip to content

Commit

Permalink
feat: length generics
Browse files Browse the repository at this point in the history
  • Loading branch information
johanneslumpe committed Nov 11, 2018
1 parent a8f27c0 commit 8821ce8
Show file tree
Hide file tree
Showing 16 changed files with 245 additions and 140 deletions.
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -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';
46 changes: 46 additions & 0 deletions src/customSyntaxes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export const customSyntaxes = {
// https://developer.mozilla.org/en-US/docs/Web/CSS/angle
angle: {
syntax: '<IDegValue> | <IGradValue> | <IRadValue> | <ITurnValue>',
},
// https://developer.mozilla.org/en-US/docs/Web/CSS/color
color: {
syntax: '<string>',
},
// https://developer.mozilla.org/en-US/docs/Web/CSS/flex_value
flex: {
syntax: '<IFrValue>',
},
// https://developer.mozilla.org/en-US/docs/Web/CSS/frequency
frequency: {
syntax: '<IHzValue> | <IKhzValue>',
},
'inset()': {
syntax: '<string>',
},
integer: {
syntax: '<number>',
},
number: {
syntax: '<number>',
},
percentage: {
syntax: '<IPercentageValue>',
},
// https://developer.mozilla.org/en-US/docs/Web/CSS/resolution
resolution: {
syntax: '<IDpiValue> | <IDpcmValue> | <IDppxValue> | <i-x-value>',
},
shape: {
syntax: '<string>',
},
// 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: '<i-s-value> | <IMsValue>',
},
'track-repeat': {
syntax: '<string>',
},
};
75 changes: 26 additions & 49 deletions src/index.ts → src/generateAllTypes.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -16,61 +16,33 @@ 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);

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: '<IDegValue> | <IGradValue> | <IRadValue> | <ITurnValue>',
},
color: {
syntax: '<string>',
},
// https://developer.mozilla.org/en-US/docs/Web/CSS/flex_value
flex: {
syntax: '<IFrValue>',
},
'inset()': {
syntax: '<string>',
},
integer: {
syntax: '<number>',
},
percentage: {
syntax: '<IPercentageValue>',
},
shape: {
syntax: '<string>',
},
'track-repeat': {
syntax: '<string>',
},
};

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),
Expand All @@ -94,7 +66,6 @@ const propertyTypes = generateTypesFromMdnData(properties, {
},
typeSuffix: 'Property',
});
const unitSourceFiles = generateUnitTypesSourceFiles();

const typesOutputSource = ts.updateSourceFileNode(
ts.createSourceFile(
Expand All @@ -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 = '<length> | <percentage>';
// console.log('parsing grammar:', grammar);
Expand Down
39 changes: 29 additions & 10 deletions src/generateTypeNodes.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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:
Expand All @@ -31,7 +42,14 @@ export const generateTsNode = (component: IComponent) => {
return ts.createTypeReferenceNode(
typeReferenceOverrideMap[withoutAngleBrackets] ||
generateTypeName(withoutAngleBrackets),
[],
genericOverrideMap[withoutAngleBrackets]
? [
ts.createTypeReferenceNode(
genericOverrideMap[withoutAngleBrackets],
[],
),
]
: [],
);

default:
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -80,23 +98,24 @@ export const generateTypesNodes = (data: INestedComponentArray): any => {
return acc;
},
[] as any[],
item,
);
return generateTypesNodes(flattenedTuples);
}),
}, componentsWithoutVoid),
),
);
}

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
Expand Down Expand Up @@ -131,8 +150,8 @@ export const generateTypesNodes = (data: INestedComponentArray): any => {
? generateTsNode(first)
: compact(
map(
componentsWithoutVoid,
item => (Array.isArray(item) ? undefined : generateTsNode(item)),
componentsWithoutVoid,
),
);
}
Expand Down
Loading

0 comments on commit 8821ce8

Please sign in to comment.