Skip to content

Commit

Permalink
Refactor type postprocessing script. Add type tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
martincizek committed May 30, 2020
1 parent 270f379 commit f38437c
Show file tree
Hide file tree
Showing 9 changed files with 1,998 additions and 409 deletions.
1 change: 0 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
dist
scripts
2,201 changes: 1,857 additions & 344 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "union-replacer",
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.2",
"description": "One-pass String.prototype.replace-like processor with multiple regexps and replacements",
"main": "dist/union-replacer.cjs.js",
"module": "dist/union-replacer.esm.js",
"browser": "dist/union-replacer.umd.js",
"types": "types",
"scripts": {
"lint": "eslint .",
"lint": "eslint . && dtslint types",
"build": "rollup -c",
"postbuild": "npm run update-types",
"pretest": "npm run build && npm run lint",
Expand All @@ -22,18 +22,19 @@
"url": "git+https://github.com/orchitech/union-replacer.git"
},
"devDependencies": {
"@babel/core": "^7.10.1",
"@babel/core": "^7.10.2",
"@babel/plugin-transform-runtime": "^7.10.1",
"@babel/preset-env": "^7.10.1",
"eslint": "^6.8.0",
"@babel/preset-env": "^7.10.2",
"dtslint": "^3.6.9",
"eslint": "^7.1.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-jsdoc": "^25.4.3",
"eslint-plugin-jsdoc": "^26.0.0",
"jasmine": "^3.5.0",
"jasmine-diff": "^0.1.3",
"jsdoc": "^3.6.4",
"np": "^6.2.3",
"rollup": "^1.32.1",
"rollup": "^2.11.2",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-node-resolve": "^5.2.0",
"tsd-jsdoc": "^2.5.0"
Expand Down
55 changes: 55 additions & 0 deletions scripts/lib/tsdConvertTupleArrays.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// While TypesSript compiler might be a tool-of-choice for this task,
// we've indeed decided to let UnionReplacer show off its power.

const UnionReplacer = require('../../dist/union-replacer.cjs');

const TUPLE_ARRAY_TYPE_RE = /^([ \t]*(?:declare\s+)?type\s+(\w+)\s*=\s*)\(([^;\r\n]*)\)\[\];/gm;
const OP_BRACKETS = ['[', '{', '<', '('];
const CL_BRACKETS = [']', '}', '>', ')'];
const BRACKET_MAP = new Map(
CL_BRACKETS.map((bracket, index) => [bracket, OP_BRACKETS[index]]),
);
const [OP_BRACKETS_RE, CL_BRACKETS_RE] = [OP_BRACKETS, CL_BRACKETS].map(
(list) => new RegExp(`[${list.map((br) => `\\${br}`).join('')}]`),
);
const unionToListReplacer = new UnionReplacer([
[/=>/, '$&'],
[OP_BRACKETS_RE, function opBracket(m) { this.open(m); return m; }],
[CL_BRACKETS_RE, function clBracket(m) { this.close(m); return m; }],
[/\s*\|\s*/, function separator(m) { return this.atRoot() ? ', ' : m; }],
]);

/* eslint-disable lines-between-class-members */
class UnionToListConverter {
constructor() { this.nestLevels = {}; }
open(bracket) { this.nestLevels[bracket] += 1; }
close(bracket) { this.nestLevels[BRACKET_MAP[bracket]] -= 1; }
atRoot() { return Object.values(this.nestLevels).every((count) => count === 0); }
convert(unionTypeDef) {
OP_BRACKETS.forEach((bracket) => { this.nestLevels[bracket] = 0; });
return unionToListReplacer.replace(unionTypeDef, this);
}
}

/**
* Convert tuple-like arrays to tuples.
*
* @param {string} tsd - Typescript declarations to perform conversion on.
* @param {RegExp} nameRe - Pattern determining tuple type names to convert.
* @returns {string} Converted typescript declarations.
*/
function tsdConvertTupleArrays(tsd, nameRe) {
const unionToListConverter = new UnionToListConverter();
return tsd.replace(TUPLE_ARRAY_TYPE_RE, (m, declarator, name, unionTypeDef) => {
if (!nameRe.test(name)) {
return m;
}
const typeList = unionToListConverter.convert(unionTypeDef);
if (!unionToListConverter.atRoot()) {
throw new SyntaxError(`Unbalanced brackets in union type definition '${unionTypeDef}'`);
}
return `${declarator}[${typeList}];`;
});
}

module.exports = tsdConvertTupleArrays;
63 changes: 9 additions & 54 deletions scripts/tsd-postprocess.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,21 @@
// Postprocess generated type definitions to introduce correctly typed tuples.
// While TypesSript compiler might be a tool-of-choice for this task,
// we've indeed decided to let UnionReplacer show off its power.
// Postprocess generated type definitions:
// - introduce correctly typed tuples
// - add header and correct whitespace
// - rename to index.d.ts

const fs = require('fs');
const path = require('path');
const UnionReplacer = require('../dist/union-replacer.cjs');
const pkg = require('../package.json');

const TUPLE_ARRAY_TYPE_RE = /^([ \t]*(?:declare\s+)?type\s+ReplaceWith\w+\s*=\s*)\(([^;\r\n]*)\)\[\];/gm;
const OP_BRACKETS = ['[', '{', '<', '('];
const CL_BRACKETS = [']', '}', '>', ')'];
const BRACKET_MAP = CL_BRACKETS.reduce((map, bracket, index) => {
map[bracket] = OP_BRACKETS[index];
return map;
}, {});
const [OP_BRACKETS_RE, CL_BRACKETS_RE] = [OP_BRACKETS, CL_BRACKETS].map((list) =>
new RegExp(`[${list.map((br) => `\\${br}`).join('')}]`)
);
const unionToListReplacer = new UnionReplacer([
[/=>/, '$&'],
[OP_BRACKETS_RE, function(m) { this.open(m); return m; }],
[CL_BRACKETS_RE, function(m) { this.close(m); return m; }],
[/\s*\|\s*/, function (m) { return this.atRoot() ? ', ' : m; }]
]);

class UnionToListConverter {
constructor() {
this.nestLevels = {};
}

open(bracket) {
this.nestLevels[bracket] += 1;
}

close(bracket) {
this.nestLevels[BRACKET_MAP[bracket]] -= 1;
}

atRoot() {
return Object.values(this.nestLevels).every(count => count === 0)
}

convert(unionTypeDef) {
OP_BRACKETS.forEach((bracket) => { this.nestLevels[bracket] = 0; });
return unionToListReplacer.replace(unionTypeDef, this);
}
}
const UnionReplacer = require('../dist/union-replacer.cjs');
const tsdConvertTupleArrays = require('./lib/tsdConvertTupleArrays');

const origFileName = `types/${pkg.name}.d.ts`;
const tsd = fs.readFileSync(origFileName, 'utf8');
const unionToListConverter = new UnionToListConverter();
const converted = tsd.replace(TUPLE_ARRAY_TYPE_RE, (m, declare, unionTypeDef) => {
const typeList = unionToListConverter.convert(unionTypeDef);
if (!unionToListConverter.atRoot()) {
throw new Error(`Unbalanced brackets in union type definition '${unionTypeDef}'`);
}
return `${declare}[${typeList}];`;
});
const converted = tsdConvertTupleArrays(tsd, /^ReplaceWith/).replace(/\s*$/, '\n');

const intro = [
`// Type definitions for ${pkg.name} ${pkg.version}`,
`// File generated by tsd-jsdoc and ${path.relative(process.cwd(), __filename)}.`,
`// Generated type definitions for ${pkg.name} ${pkg.version}`,
`// File created by tsd-jsdoc and ${path.relative(process.cwd(), __filename)}.`,
'// Do not modify directly.',
'',
`export = ${UnionReplacer.name};`,
Expand Down
5 changes: 2 additions & 3 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Type definitions for union-replacer 2.0.0-beta.1
// File generated by tsd-jsdoc and scripts/tsd-postprocess.js.
// Generated type definitions for union-replacer 2.0.0-beta.2
// File created by tsd-jsdoc and scripts/tsd-postprocess.js.
// Do not modify directly.

export = UnionReplacer;
Expand Down Expand Up @@ -203,4 +203,3 @@ declare namespace UnionReplacer {
build(): T;
}
}

40 changes: 40 additions & 0 deletions types/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import UnionReplacer = require('union-replacer');

new UnionReplacer([]);
new UnionReplacer(); // $ExpectError
new UnionReplacer([/foo/, 'bar']); // $ExpectError
new UnionReplacer([['foo', 'bar']]); // $ExpectError

new UnionReplacer([[/foo/, 'bar']]);
new UnionReplacer([[/foo/, 'bar', true]]); // $ExpectError
new UnionReplacer([[/foo/, 'bar', false]]); // $ExpectError

new UnionReplacer([[/foo/, (m: string, index: number): string => '']]);
new UnionReplacer([[/foo/, (m: string, index: number): number => 1]]); // $ExpectError
new UnionReplacer([[/foo/, (m: string): string => m, false]]);
new UnionReplacer([[/foo/, (m: string): string => m, true]]); // $ExpectError

new UnionReplacer([[/foo/, (ctx: UnionReplacer.MatchingContext): string => '', true]]);
new UnionReplacer([[/foo/, (ctx: UnionReplacer.MatchingContext): number => 1, true]]); // $ExpectError
new UnionReplacer([[/foo/, (ctx: UnionReplacer.MatchingContext): string => '', false]]); // $ExpectError

const replacer: UnionReplacer = new UnionReplacer([
[/foo/, (m, index) => {
m; // $ExpectType string
return '';
}],
[/bar/, (ctx: UnionReplacer.MatchingContext) => {
ctx; // $ExpectType MatchingContext
ctx.match; // $ExpectType RegExpExecArray | null
return '';
}, true],
]);

replacer.replace('foobar'); // $ExpectType string

class MyBuilder implements UnionReplacer.ReplacementBuilder<number> {
addSubjectSlice(subject: string, start: number, end: number) {}
addReplacedString(string: string) {}
build() { return 1; }
}
replacer.replace('foobar', {}, new MyBuilder()); // $ExpectType number
18 changes: 18 additions & 0 deletions types/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noEmit": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"types": [],
"paths": { "union-replacer": ["."] }
}
}
9 changes: 9 additions & 0 deletions types/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "dtslint/dtslint.json",
"rules": {
"strict-export-declare-modifiers": false,
"no-unnecessary-qualifier": false,
"max-line-length": false,
"no-redundant-jsdoc": false
}
}

0 comments on commit f38437c

Please sign in to comment.