Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@ForbesLindesay
Created January 10, 2017 17:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ForbesLindesay/630fc2bf122a68e72f4f166daa9b8bd4 to your computer and use it in GitHub Desktop.
Save ForbesLindesay/630fc2bf122a68e72f4f166daa9b8bd4 to your computer and use it in GitHub Desktop.
type Children = {
children: void | Array<Children>
message: [MessageBlame, MessageBlame, MessageBlame, MessageBlame, MessageComment, MessageBlame]
};
type Extra = {
children: void | Array<Children>
message: [MessageBlame, MessageBlame, MessageBlame, MessageBlame, MessageComment, MessageBlame] | [MessageBlame, MessageBlame]
};
type FlowErrorDuplicateProvider = {
kind: "duplicate provider"
level: string
message: [MessageBlame, MessageComment, MessageBlame]
};
type FlowErrorInfer1 = {
kind: "infer"
level: string
message: [MessageBlame]
};
type FlowErrorInfer2 = {
kind: "infer"
level: string
message: [MessageBlame, MessageComment]
};
type FlowErrorInfer3 = {
extra: void | Array<Extra>
kind: "infer"
level: string
message: [MessageBlame, MessageComment, MessageBlame]
operation: void | OperationBlame
trace: void | Array<TraceBlame>
};
type FlowErrorInfer4 = {
kind: "infer"
level: string
message: [MessageBlame, MessageBlame, MessageComment, MessageBlame]
operation: void | OperationBlame
trace: void | Array<TraceBlame>
};
type FlowErrorInternal = {
kind: "internal"
level: string
message: [MessageBlame]
};
type FlowErrorParse = {
kind: "parse"
level: string
message: [MessageBlame]
};
type LineLocation = {
column: number
line: number
offset: number
};
type Loc = {
end: LineLocation
source: string
start: LineLocation
type: "SourceFile" | "LibFile" | "JsonFile"
};
type MessageBlame = {
context: string | null
descr: string
end: number
endline: number
line: number
loc: Loc | void
path: string
start: number
type: "Blame"
};
type MessageComment = {
context: null
descr: string
end: number
endline: number
line: number
path: string
start: number
type: "Comment"
};
type OperationBlame = {
context: string
descr: string
end: number
endline: number
line: number
loc: Loc
path: string
start: number
type: "Blame"
};
type TraceBlame = {
context: null | string
descr: string
end: number
endline: number
line: number
loc: void | Loc
path: string
start: number
type: "Blame"
};
type FlowError = FlowErrorDuplicateProvider | FlowErrorInfer1 | FlowErrorInfer2 | FlowErrorInfer3 | FlowErrorInfer4 | FlowErrorInternal | FlowErrorParse;
import {readFileSync, readdirSync, writeFileSync} from 'fs';
const testCasesDir = __dirname + '/__test__/__test_cases__';
const allErrors = [];
readdirSync(testCasesDir).forEach(testCase => {
const json = JSON.parse(readFileSync(testCasesDir + '/' + testCase));
if (json.errors && json.errors.length) {
json.errors.forEach(error => allErrors.push(error));
}
});
function toPascalCase(str) {
return str[0].toUpperCase() + str.substr(1).replace(/ ([a-z])/g, (_, c) => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, '');
}
const namedObjects = new Map();
function generateFlowTypeForObject(value) {
return (
'{\n' +
Object.keys(value).sort().map(k => {
const name = toPascalCase(k);
const flowType = k === 'type' ? JSON.stringify(value[k]) : generateFlowType(value[k], name);
return ' ' + k + ': ' + flowType.replace(/\n/g, '\n ') + ',';
}).join('\n') +
'\n}'
);
}
function generateFlowType(value, name) {
if (typeof value === 'string') {
return 'string';
}
if (typeof value === 'number') {
return 'number';
}
if (typeof value === 'boolean') {
return 'boolean';
}
if (value === null) {
return 'null';
}
if (value === undefined) {
return 'void';
}
let obj = '';
if (Array.isArray(value)) {
const types = value.map(v => generateFlowType(v, name));
const typesSet = new Set(types);
if (typesSet.size === 0 || typesSet.size > 1 || types[0] === 'MessageBlame') {
return (
'[' +
types.join(', ') +
']'
);
} else {
return 'Array<' + [...typesSet].sort().join(' | ') + '>';
}
name = name + 'List';
} else if (typeof value === 'object') {
if (name === 'Start' || name === 'End') {
name = 'LineLocation';
}
if (typeof value.type === 'string' && name !== 'Loc') {
name += toPascalCase(value.type);
} else if (typeof value.kind === 'string') {
name += toPascalCase(value.kind);
}
if (value.kind === 'infer' && Array.isArray(value.message)) {
name += value.message.length;
}
if (!namedObjects.has(name)) {
namedObjects.set(name, []);
}
const typeObject = {};
Object.keys(value).forEach(key => {
const name = toPascalCase(key);
if ((key === 'type' || key === 'kind') && typeof value[key] === 'string') {
typeObject[key] = JSON.stringify(value[key]);
} else {
typeObject[key] = generateFlowType(value[key], name);
}
});
namedObjects.get(name).push(typeObject);
return name;
}
}
function generateNamedObjectTypes() {
let output = [];
namedObjects.forEach((instances, name) => {
const keys = new Set();
instances.forEach(instance => {
Object.keys(instance).forEach(key => keys.add(key));
});
const typeString = (
'{\n' +
[...keys].sort().map(k => {
const valueTypes = new Set(instances.map(i => i[k] || 'void'));
return ' ' + k + ': ' + [...valueTypes].join(' | ');
}).join('\n') +
'\n}'
);
output.push({name, typeString});
});
return output.sort(
(a, b) => a.name < b.name ? -1 : 1
).map(
({name, typeString}) => 'type ' + name + ' = ' + typeString + ';\n'
).join('');
}
const errorTypes = new Set();
allErrors.forEach(error => {
errorTypes.add(generateFlowType(error, 'FlowError'));
});
writeFileSync(__dirname + '/flow-error.js', generateNamedObjectTypes() + '\ntype FlowError = ' + [...errorTypes].sort().join(' | ') + ';');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment