@triviality/core
Version:
Purely typed service container
293 lines • 15.5 kB
JavaScript
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spread = (this && this.__spread) || function () {
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
return ar;
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
var filehound_1 = require("filehound");
var fs = __importStar(require("fs"));
var os_1 = require("os");
var ramda_1 = require("ramda");
var yup = __importStar(require("yup"));
exports.generateRecurringString = ramda_1.curry(function (total, empty, createString, separator) { return ramda_1.range(empty ? 0 : 1, total + 1).map(createString).join(separator); });
exports.parseNumber = ramda_1.curryN(2, parseInt)(ramda_1.__, 10);
/**
* Return all generators of a single document.
*
* Generators need to be inside multi-doc block comment.
*/
exports.findAnnotationsString = function (annotation) { return ramda_1.match(new RegExp("^.*@" + annotation + "\\([\\s\\S.*]*?\\).*[.\\s\\S]*?\\*\\/$", 'gm')); };
/**
* Return all generators of a single document.
*
* Generators need to be inside multi-doc block comment.
*/
exports.findFunctionGeneratorAnnotationsString = exports.findAnnotationsString('typeGenerator');
var matchEmptyOrStatement = function () { return /(.*; *$|^ *$)/; };
var GeneratorTemplateSchema = yup.object({
length: yup.number().positive().default(10),
templates: yup.array(yup.string().required()).required(),
removeNextLines: yup.mixed().default(matchEmptyOrStatement),
empty: yup.boolean().default(false),
}).noUnknown();
/**
* Remove any doc block characters from a string
*
* //
*
* /**
* *
* * /
*/
exports.stripDocBlock = ramda_1.replace(/^ *(\/?\*+|\/\/)/gm, '');
/**
* Remove all ,< from empty generic functions.
*
* This:
* <>
* <,C extends string>
* <D extends number,, C extends string>
* <D extends number,, C extends string>(,dd: string)
* <D extends number,, C extends string>(dd: string,,d: string)
*
* Would be this:
* <C extends string>
* <D extends number, C extends string>
* <D extends number, C extends string>(dd: string)
* <D extends number, C extends string>(dd: string,d: string)
*/
var stripEmptyGeneric = ramda_1.replace(/<>|(<|\(), *|( *, *),|, *(\))/gm, '$1$2$3');
/**
* Parse generator function.
*
* @typeGenerator({ length: 10, templates: ["services<{K% extends keyof T}>({t%: K%}): [{T[K%]}];"], removeNextLines: /; *$/ })
*/
exports.parseFunctionGeneratorAnnotation = ramda_1.pipe(ramda_1.match(/@typeGenerator\(([\s\S.]*)\)/), ramda_1.pipe(ramda_1.nth(1), function (target) {
var striped = exports.stripDocBlock(target);
try {
// tslint:disable-next-line:no-eval
return eval("(" + striped + ");");
}
catch (e) {
throw new Error(e + " => (" + striped + ");");
}
}), GeneratorTemplateSchema.cast.bind(GeneratorTemplateSchema));
/**
* Find all tags "{t%: K%}" of a function template.
*/
exports.findGeneratorTagsStrings = ramda_1.match(/{{.+?}}/gm);
/**
* Parse a single tag and return variables.
*/
exports.parseGeneratorTag = ramda_1.pipe(ramda_1.match(/{{(.*?) *(-(.*))?}}/), ramda_1.applySpec({
tag: ramda_1.nth(0),
template: ramda_1.nth(1),
separator: ramda_1.pipe(ramda_1.nth(3), ramda_1.cond([
[ramda_1.is(String), ramda_1.identity],
[ramda_1.T, ramda_1.always(', ')],
])),
}));
exports.findGeneratorTags = ramda_1.pipe(exports.findGeneratorTagsStrings, ramda_1.map(exports.parseGeneratorTag));
/**
* Fill tag template.
* - Replaces % for i given.
* - Strip template tokens.
*/
var populateGeneratorTagTemplate = ramda_1.curry(function (template, i) { return ramda_1.replace(/%/g, i.toString(10), template); });
exports.generateFunction = ramda_1.curry(function (template, i) {
var g = exports.generateRecurringString(i, false);
var result = exports.findGeneratorTags(template).reduce(function (acc, _a) {
var tag = _a.tag, separator = _a.separator, t = _a.template;
var f = g(populateGeneratorTagTemplate(t), separator);
return acc.replace(tag, f);
}, template);
return i === 0 ? stripEmptyGeneric(result) : result;
});
function generateFunctions(length, emptyArgs, templates) {
return exports.generateRecurringString(length, emptyArgs, function (i) {
return templates.reduce(function (acc, template) {
if (!acc) {
return exports.generateFunction(template, i);
}
return acc + os_1.EOL + exports.generateFunction(template, i);
}, null) || '';
}, os_1.EOL);
}
exports.generateFunctions = generateFunctions;
function removeStartLine(text, removeNextLines) {
return ramda_1.match(/^.*$/gm, text)
.reduce(function (_a, line) {
var document = _a.document, done = _a.done;
if (done || !removeNextLines.test(line)) {
return {
document: document === '' ? line : document + os_1.EOL + line,
done: true,
};
}
return {
document: document,
done: done,
};
}, { document: '', done: false }).document;
}
function generateFunctionsInDocument(document) {
return exports.findFunctionGeneratorAnnotationsString(document)
.reduce(function (acc, generator) {
var _a = exports.parseFunctionGeneratorAnnotation(generator), templates = _a.templates, length = _a.length, removeNextLines = _a.removeNextLines, empty = _a.empty;
var index = acc.indexOf(generator);
var end = index + generator.length;
var cleaned = acc.slice(0, end) + os_1.EOL + removeStartLine(acc.slice(end), removeNextLines);
return cleaned.replace(generator, generator + os_1.EOL + generateFunctions(length, empty, templates));
}, document);
}
exports.generateFunctionsInDocument = generateFunctionsInDocument;
/**
* Replaces multiple generators of set of files.
*/
function generateFunctionsOfTemplatesInDirectory() {
var _a;
var directories = [];
for (var _i = 0; _i < arguments.length; _i++) {
directories[_i] = arguments[_i];
}
(_a = filehound_1.create()).paths.apply(_a, __spread(directories)).ext(['ts', 'tsx'])
.discard(/__test__/)
.findSync()
.forEach(function (file) {
var content = fs.readFileSync(file).toString();
var updates = generateFunctionsInDocument(content);
if (content !== updates) {
fs.writeFileSync(file, updates);
}
});
}
exports.generateFunctionsOfTemplatesInDirectory = generateFunctionsOfTemplatesInDirectory;
it('generateRecurringString should generate string based on length and template', function () {
var g = exports.generateRecurringString(3, false);
expect(g(function (i) { return "[" + i + "]"; }, ', ')).toEqual('[1], [2], [3]');
var gE = exports.generateRecurringString(3, true);
expect(gE(function (i) { return "[" + i + "]"; }, ', ')).toEqual('[0], [1], [2], [3]');
});
it('parseNumber', function () {
expect(exports.parseNumber('10')).toEqual(10);
expect(exports.parseNumber('-10')).toEqual(-10);
});
it('findFunctionGeneratorAnnotationsString', function () {
expect(exports.findFunctionGeneratorAnnotationsString("\n /**\n * @typeGenerator({ length: 10, templates: [\"services<{K% extends keyof T}>({t%: K%}): [{T[K%]}];\"] })\n */\n export function servives(tags: any): any[];\n\n /**\n * @typeGenerator({ length: 10, templates: [\"export function toInt({a%i: number}): void;\"], removeNextLines: /;/})\n */\n export toInt(...args: number[]): void;\n ")).toMatchSnapshot();
});
it('findFunctionGeneratorAnnotationsString multiline', function () {
expect(exports.findFunctionGeneratorAnnotationsString("\n /**\n * @typeGenerator({ length: 10, templates:\n * [\"services<{K% extends keyof T}>({t%: K%}): [{T[K%]}];\"]\n * } )\n */\n ")).toMatchSnapshot();
});
it('parseFunctionGeneratorAnnotation', function () {
expect(exports.parseFunctionGeneratorAnnotation('@typeGenerator({ empty: true, length: 10, templates: [\'export function toInt({a%i: number}): void;\'], removeNextLines: /;/ })')).toEqual({
empty: true,
length: 10,
removeNextLines: /;/,
templates: ['export function toInt({a%i: number}): void;'],
});
});
it('parseFunctionGeneratorAnnotation with defaults', function () {
expect(exports.parseFunctionGeneratorAnnotation('@typeGenerator({ length: 5, templates: [\'export function toInt({a%i: number}): void;\'] })')).toEqual({
empty: false,
length: 5,
removeNextLines: /(.*; *$|^ *$)/,
templates: ['export function toInt({a%i: number}): void;'],
});
});
it('findGeneratorTagsStrings', function () {
expect(exports.findGeneratorTagsStrings('export function toInt({{a%i: number}}): { data: {{a%i}} };')).toEqual([
'{{a%i: number}}',
'{{a%i}}',
]);
});
it('parseGeneratorTag', function () {
expect(exports.parseGeneratorTag('{{a%}}')).toEqual({
template: 'a%',
separator: ', ',
tag: '{{a%}}',
});
expect(exports.parseGeneratorTag('{{b%- | }}')).toEqual({
template: 'b%',
separator: ' | ',
tag: '{{b%- | }}',
});
});
it('findGeneratorTags', function () {
expect(exports.findGeneratorTags('foobar({{a%: number}}): { data: [{{typeof a%}}] };')).toEqual([
{
template: 'a%: number',
separator: ', ',
tag: '{{a%: number}}',
},
{
template: 'typeof a%',
separator: ', ',
tag: '{{typeof a%}}',
},
]);
});
it('populateGeneratorTagTemplate', function () {
expect(populateGeneratorTagTemplate('a%: number', 2)).toEqual('a2: number');
expect(populateGeneratorTagTemplate('c: typeof a%, a%: number', 5)).toEqual('c: typeof a5, a5: number');
});
it('generateFunction', function () {
expect(exports.generateFunction('foobar({{a%: number}}): { data: [{{typeof a%}}] };', 2)).toEqual('foobar(a1: number, a2: number): { data: [typeof a1, typeof a2] };');
});
it('generateFunctions', function () {
expect(generateFunctions(2, false, ['foo({{a%: string}}): void;', 'bar({{b%:number}}): void;']))
.toEqual("foo(a1: string): void;\nbar(b1:number): void;\nfoo(a1: string, a2: string): void;\nbar(b1:number, b2:number): void;");
});
it('generateFunctions with empty function', function () {
expect(generateFunctions(2, true, ['foo({{a%: string}}): void;', 'bar({{b%:number}}): void;']))
.toEqual("foo(): void;\nbar(): void;\nfoo(a1: string): void;\nbar(b1:number): void;\nfoo(a1: string, a2: string): void;\nbar(b1:number, b2:number): void;");
});
it('generateFunctions should remove empty generic types <>', function () {
expect(generateFunctions(1, true, ['foo<{{A% extends string}}>({{a%: A1}}): void;']))
.toEqual("foo(): void;\nfoo<A1 extends string>(a1: A1): void;");
expect(generateFunctions(1, true, ['foo<{{A% extends string}}, D extends number>({{a%: A1}}, d: D): void;']))
.toEqual("foo<D extends number>(d: D): void;\nfoo<A1 extends string, D extends number>(a1: A1, d: D): void;");
expect(generateFunctions(1, true, ['foo<C extends Object,{{ A% extends string}}, D extends number>({{a%: A1}}, d: D, c: C): void;']))
.toEqual("foo<C extends Object, D extends number>(d: D, c: C): void;\nfoo<C extends Object, A1 extends string, D extends number>(a1: A1, d: D, c: C): void;");
});
it('generateFunctionsInDocument', function () {
expect(generateFunctionsInDocument("\n/**\n * @typeGenerator({ length: 4, templates: [\"compose<{{t% extends keyof T}}}, F extends ({{d%: SFT<T[t%]>}}) => S, S>(f: F, {{k%: t%}}}): () => S;\"] })\n */\n"))
.toEqual("\n/**\n * @typeGenerator({ length: 4, templates: [\"compose<{{t% extends keyof T}}}, F extends ({{d%: SFT<T[t%]>}}) => S, S>(f: F, {{k%: t%}}}): () => S;\"] })\n */\ncompose<t1 extends keyof T}, F extends (d1: SFT<T[t1]>) => S, S>(f: F, k1: t1}): () => S;\ncompose<t1 extends keyof T, t2 extends keyof T}, F extends (d1: SFT<T[t1]>, d2: SFT<T[t2]>) => S, S>(f: F, k1: t1, k2: t2}): () => S;\ncompose<t1 extends keyof T, t2 extends keyof T, t3 extends keyof T}, F extends (d1: SFT<T[t1]>, d2: SFT<T[t2]>, d3: SFT<T[t3]>) => S, S>(f: F, k1: t1, k2: t2, k3: t3}): () => S;\ncompose<t1 extends keyof T, t2 extends keyof T, t3 extends keyof T, t4 extends keyof T}, F extends (d1: SFT<T[t1]>, d2: SFT<T[t2]>, d3: SFT<T[t3]>, d4: SFT<T[t4]>) => S, S>(f: F, k1: t1, k2: t2, k3: t3, k4: t4}): () => S;\n");
});
it('generateFunctionsInDocument multiline', function () {
expect(generateFunctionsInDocument("\n/**\n * @typeGenerator({ length: 2, templates: [\n * \"Foo{{%}}\",\n * \"Bar{{%}}\",\n * ]})\n */\n"))
.toEqual("\n/**\n * @typeGenerator({ length: 2, templates: [\n * \"Foo{{%}}\",\n * \"Bar{{%}}\",\n * ]})\n */\nFoo1\nBar1\nFoo1, 2\nBar1, 2\n");
});
it('removeStartLine', function () {
expect(removeStartLine("d;\nddsad;\n\nsad;;\n\nDo not remove!\n\nDon't ignore this;\n", matchEmptyOrStatement())).toEqual("Do not remove!\n\nDon't ignore this;\n");
});
it('generateFunctionsInDocument should remove old lines', function () {
expect(generateFunctionsInDocument("\n/**\n * @typeGenerator({ length: 4, templates: [\"compose<{{t% extends keyof T}}}, F extends ({{d%: SFT<T[t%]>}}) => S, S>(f: F, {{k%: t%}}}): () => S;\"] })\n */\n compose<t1 extends keyof T, t2 extends keyof T}, F extends (d1: SFT<T[t1]>, d2: SFT<T[t2]>) => S, S>(f: F, k1: t1, k2: t2}): () => S;\nRemove this;\ncompose<t1 extends keyof T, t2 extends keyof T, t3 extends keyof T, t4 extends keyof T}, F extends (d1: SFT<T[t1]>, d2: SFT<T[t2]>, d3: SFT<T[t3]>, d4: SFT<T[t4]>) => S, S>(f: F, k1: t1, k2: t2, k3: t3, k4: t4}): () => S;\n\npreserve this\n\n"))
.toEqual("\n/**\n * @typeGenerator({ length: 4, templates: [\"compose<{{t% extends keyof T}}}, F extends ({{d%: SFT<T[t%]>}}) => S, S>(f: F, {{k%: t%}}}): () => S;\"] })\n */\ncompose<t1 extends keyof T}, F extends (d1: SFT<T[t1]>) => S, S>(f: F, k1: t1}): () => S;\ncompose<t1 extends keyof T, t2 extends keyof T}, F extends (d1: SFT<T[t1]>, d2: SFT<T[t2]>) => S, S>(f: F, k1: t1, k2: t2}): () => S;\ncompose<t1 extends keyof T, t2 extends keyof T, t3 extends keyof T}, F extends (d1: SFT<T[t1]>, d2: SFT<T[t2]>, d3: SFT<T[t3]>) => S, S>(f: F, k1: t1, k2: t2, k3: t3}): () => S;\ncompose<t1 extends keyof T, t2 extends keyof T, t3 extends keyof T, t4 extends keyof T}, F extends (d1: SFT<T[t1]>, d2: SFT<T[t2]>, d3: SFT<T[t3]>, d4: SFT<T[t4]>) => S, S>(f: F, k1: t1, k2: t2, k3: t3, k4: t4}): () => S;\npreserve this\n\n");
});
it('!!', function () {
generateFunctionsOfTemplatesInDirectory(__dirname + "/../../");
});
//# sourceMappingURL=ServicesContext.generator.test.js.map
;