Skip to content

Commit

Permalink
Add patching ability to temporarily patch until they are solved by MDN
Browse files Browse the repository at this point in the history
  • Loading branch information
frenic committed Apr 17, 2018
1 parent aa33392 commit d18019b
Show file tree
Hide file tree
Showing 11 changed files with 427 additions and 120 deletions.
124 changes: 89 additions & 35 deletions index.d.ts

Large diffs are not rendered by default.

124 changes: 89 additions & 35 deletions index.js.flow

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@types/jest": "^22.1.1",
"@types/node": "^9.4.2",
"@types/prettier": "^1.10.0",
"chalk": "^2.3.2",
"chokidar": "^2.0.0",
"flow-bin": "^0.69.0",
"jest": "^22.2.1",
Expand Down
34 changes: 22 additions & 12 deletions src/data-types.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import * as syntaxes from 'mdn-data/css/syntaxes.json';
import { compatSyntax } from './compat';
import { getPropertySyntax, getSyntax } from './data';
import parse from './parser';
import typing, { addType, DataType, hasType, ResolvedType, Type, TypeType } from './typer';

const MIN_TYPES = 3;
import typing, { addType, DataType, hasType, IDataType, ResolvedType, Type, TypeType } from './typer';

const dataTypes: { [key: string]: ResolvedType[] } = {};

export default dataTypes;

export function resolveDataTypes(
types: TypeType[],
resolver: (name: string) => ResolvedType[] = dataTypeResolver,
resolver: (dataType: IDataType) => ResolvedType[] = simpleDataTypeResolver,
min = 3,
): ResolvedType[] {
let resolvedDataTypes: ResolvedType[] = [];

for (const type of types) {
switch (type.type) {
case Type.DataType: {
const resolvedDataType = resolver(type.name);
const resolvedDataType = resolver(type);

if (resolvedDataType.length >= MIN_TYPES) {
if (resolvedDataType.length >= min) {
// Dissolve data type if it's too small
resolvedDataTypes = addType(resolvedDataTypes, addDataType(type.name, resolvedDataType));
} else {
Expand All @@ -30,7 +30,7 @@ export function resolveDataTypes(
break;
}
case Type.PropertyReference: {
const resolvedProperty = resolver(type.name);
const resolvedProperty = resolver(type);

// Dissolve property references completely
for (const resolvedType of resolvedProperty) {
Expand All @@ -46,10 +46,20 @@ export function resolveDataTypes(
return resolvedDataTypes;
}

function dataTypeResolver(name: string): ResolvedType[] {
return name in syntaxes
? resolveDataTypes(typing(parse(syntaxes[name].syntax)), dataTypeResolver)
: [{ type: Type.String }];
function simpleDataTypeResolver(dataType: IDataType): ResolvedType[] {
const syntax = getSyntax(dataType.name);
return syntax ? resolveDataTypes(typing(parse(syntax)), simpleDataTypeResolver) : [{ type: Type.String }];
}

export function createPropertyDataTypeResolver(data: MDN.CompatData | null, min?: number) {
const resolver: (dataType: IDataType) => ResolvedType[] = dataType => {
const syntax = dataType.type === Type.DataType ? getSyntax(dataType.name) : getPropertySyntax(dataType.name);
return syntax
? resolveDataTypes(data ? typing(compatSyntax(data, parse(syntax))) : typing(parse(syntax)), resolver)
: [{ type: Type.String }];
};

return resolver;
}

function addDataType(name: string, types: ResolvedType[], index = 0): DataType {
Expand Down
106 changes: 106 additions & 0 deletions src/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import * as rawProperties from 'mdn-data/css/properties.json';
import * as rawSyntaxes from 'mdn-data/css/syntaxes.json';
import { getPropertyData, getTypesData } from './compat';
import { createPropertyDataTypeResolver, resolveDataTypes } from './data-types';
import { IExtendedProperty, properties as patchedProperties, syntaxes as patchedSyntaxes } from './data/patches';
import { error, warn } from './logger';
import parse from './parser';
import typing, { hasType } from './typer';

export const properties: { [property: string]: IExtendedProperty } = {};

for (const name in rawProperties) {
properties[name] = {
...rawProperties[name],
syntax: getPropertySyntax(name),
shorthand:
name in patchedProperties && typeof patchedProperties[name].shorthand === 'boolean'
? patchedProperties[name].shorthand
: Array.isArray(rawProperties[name].computed),
};
}

for (const name in patchedProperties) {
if (!(name in properties)) {
properties[name] = patchedProperties[name];
}
}

const validatedPropertySyntaxes: string[] = [];

export function getPropertySyntax(name: string) {
const patch = patchedProperties[name];

if (patch && patch.syntax) {
if (!validatedPropertySyntaxes.includes(name) && rawProperties[name] && rawProperties[name].syntax) {
const compatibilityData = getPropertyData(name);

if (isEqual(compatibilityData, patch.syntax, rawProperties[name].syntax)) {
error();
}

validatedPropertySyntaxes.push(name);
}

return patch.syntax;
}

if (!rawProperties[name]) {
warn('Data for property `%s` is missing', name);
}

return rawProperties[name] && rawProperties[name].syntax;
}

const validatedSyntaxes: string[] = [];

export function getSyntax(name: string) {
const patch = patchedSyntaxes[name];

if (patch && patch.syntax) {
if (!validatedSyntaxes.includes(name) && rawSyntaxes[name] && rawSyntaxes[name].syntax) {
const compatibilityData = getTypesData(name);

if (isEqual(compatibilityData, patch.syntax, rawSyntaxes[name].syntax)) {
error();
}

validatedSyntaxes.push(name);
}

return patch.syntax;
}

if (!rawSyntaxes[name]) {
warn('Data for syntax `%s` is missing', name);
}

return rawSyntaxes[name] && rawSyntaxes[name].syntax;
}

function isEqual(compat: MDN.CompatData | null, syntaxA: string, syntaxB: string): boolean {
// Dissolve all data types to check whether it already exists or not
const dissolvedTypesA = resolveDataTypes(
typing(parse(syntaxA)),
createPropertyDataTypeResolver(compat, Infinity),
Infinity,
);
const dissolvedTypesB = resolveDataTypes(
typing(parse(syntaxA)),
createPropertyDataTypeResolver(compat, Infinity),
Infinity,
);

for (const type of dissolvedTypesA) {
if (!hasType(dissolvedTypesB, type)) {
return false;
}
}
for (const type of dissolvedTypesB) {
if (!hasType(dissolvedTypesA, type)) {
return false;
}
}

return true;
}
76 changes: 76 additions & 0 deletions src/data/patches.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
export interface IExtendedProperty extends Partial<MDN.Property> {
shorthand: boolean;
}

export const properties: {
[property: string]: IExtendedProperty;
} = {
/**
* https://drafts.csswg.org/css-overflow-3/#max-lines
* Should be added to `mdn-browser-compat-data` when https://github.com/mdn/data/pull/207 is merged
*/
'-webkit-line-clamp': {
syntax: 'none | <integer>',
shorthand: true,
},
/**
* https://drafts.csswg.org/css-overflow-3/#block-ellipsis
* https://github.com/mdn/data/pull/207
*/
'block-overflow': {
syntax: 'clip | ellipsis | <string>',
shorthand: false,
},
/**
* https://drafts.csswg.org/css-overflow-3/#line-clamp
* https://github.com/mdn/data/pull/207
*/
'line-clamp': {
syntax: 'none | <integer>',
shorthand: true,
},
/**
* https://drafts.csswg.org/css-overflow-3/#max-lines
* https://github.com/mdn/data/pull/207
*/
'max-lines': {
syntax: 'none | <integer>',
shorthand: false,
},
/**
* https://drafts.csswg.org/css-scroll-anchoring/#exclusion-api
* https://github.com/mdn/data/pull/206
*/
'overflow-anchor': {
syntax: 'auto | none',
shorthand: false,
},
/**
* https://drafts.csswg.org/css-overflow-3/#logical
* https://github.com/mdn/data/pull/207
*/
'overflow-block': {
syntax: "<'overflow'>",
shorthand: false,
},
/**
* https://drafts.csswg.org/css-overflow-3/#logical
* https://github.com/mdn/data/pull/207
*/
'overflow-inline': {
syntax: "<'overflow'>",
shorthand: false,
},
};

export const syntaxes: {
[property: string]: MDN.Syntax;
} = {
/**
* https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius
* https://github.com/mdn/data/pull/208
*/
'outline-radius': {
syntax: '<length> | <percentage>',
},
};
3 changes: 3 additions & 0 deletions src/data/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,7 @@ export const syntaxes: MDN.Syntaxes = {
paint: {
syntax: 'none | child | child(<integer>) | <color> | <url> [ none | <color> ]? | context-fill | context-stroke',
},
dasharray: {
syntax: '[ <length> | <percentage> | <number> ]#',
},
};
11 changes: 11 additions & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import chalk from 'chalk';

export const error: typeof console.error = (message, ...params) => {
// Complete the build process but exit with failure when done
process.exitCode = 1;
console.error(typeof message === 'string' ? chalk.magenta('ERROR! ' + message) : message, ...params);
};

export const warn: typeof console.warn = (message, ...params) => {
console.info(typeof message === 'string' ? chalk.yellow(message) : message, ...params);
};
45 changes: 16 additions & 29 deletions src/properties.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as properties from 'mdn-data/css/properties.json';
import * as syntaxes from 'mdn-data/css/syntaxes.json';
import {
compatNames,
compatSyntax,
Expand All @@ -9,16 +7,19 @@ import {
isAddedBySome,
isDeprecated,
} from './compat';
import { resolveDataTypes } from './data-types';
import { properties as rawSvgProperties, syntaxes as svgSyntaxes } from './data/svg';
import { getPropertySyntax, properties } from './data';
import { createPropertyDataTypeResolver, resolveDataTypes } from './data-types';
import { properties as rawSvgProperties, syntaxes as rawSvgSyntaxes } from './data/svg';
import parse from './parser';
import typing, { ResolvedType, Type } from './typer';
import typing, { IDataType, ResolvedType } from './typer';

const ALL = 'all';

const IGNORES = [
// Custom properties
'--*',
// We define this manually
'all',
ALL,
];

const REGEX_VENDOR_PROPERTY = /^-/;
Expand All @@ -36,7 +37,7 @@ if (!globalCompatibilityData) {
// The CSS-wide keywords are identical to the `all` property
// https://www.w3.org/TR/2016/CR-css-cascade-4-20160114/#all-shorthand
export const globals: ResolvedType[] = resolveDataTypes(
typing(compatSyntax(globalCompatibilityData, parse(properties.all.syntax))),
typing(compatSyntax(globalCompatibilityData, parse(getPropertySyntax(ALL)))),
);

export const standardLonghandProperties: { [name: string]: IProperty } = {};
Expand All @@ -58,7 +59,7 @@ for (const originalName in properties) {
}

// Default values
let entities = parse(properties[originalName].syntax);
let entities = parse(getPropertySyntax(originalName));
let currentNames: string[] = [originalName];
let obsoleteNames: string[] = [];

Expand All @@ -85,11 +86,11 @@ for (const originalName in properties) {

const property: IProperty = {
name: originalName,
types: resolveDataTypes(typing(entities), createDataTypeResolver(compatibilityData)),
types: resolveDataTypes(typing(entities), createPropertyDataTypeResolver(compatibilityData)),
};

for (const name of currentNames) {
const isShorthand = Array.isArray(properties[originalName].computed);
const isShorthand = properties[originalName].shorthand;

if (isVendorProperty(name)) {
if (isShorthand) {
Expand Down Expand Up @@ -131,26 +132,12 @@ function filterMissingProperties(names: string[]) {
return names.filter(name => !(name in properties));
}

export function createDataTypeResolver(data: MDN.CompatData | null) {
const resolver: (dataTypeName: string) => ResolvedType[] = dataTypeName => {
const syntax = syntaxes[dataTypeName] || properties[dataTypeName];
return syntax
? resolveDataTypes(
data ? typing(compatSyntax(data, parse(syntax.syntax))) : typing(parse(syntax.syntax)),
resolver,
)
: [{ type: Type.String }];
};

return resolver;
}

function createSvgDataTypeResolver(data: MDN.CompatData | null) {
const resolver = createDataTypeResolver(data);
const svgResolver: (dataTypeName: string) => ResolvedType[] = (dataTypeName: string) =>
dataTypeName in svgSyntaxes
? resolveDataTypes(typing(parse(svgSyntaxes[dataTypeName].syntax)), svgResolver)
: resolver(dataTypeName);
const resolver = createPropertyDataTypeResolver(data);
const svgResolver: (dataType: IDataType) => ResolvedType[] = (dataType: IDataType) =>
dataType.name in rawSvgSyntaxes
? resolveDataTypes(typing(parse(rawSvgSyntaxes[dataType.name].syntax)), svgResolver)
: resolver(dataType);

return svgResolver;
}

0 comments on commit d18019b

Please sign in to comment.