Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retire sed #42

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
build:
docker:
# specify the version you desire here
- image: circleci/node:8.11.1
- image: circleci/node:12.16.3
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's to use async generators in scripts


# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
Expand Down
58 changes: 10 additions & 48 deletions scripts/make_doc.sh
Original file line number Diff line number Diff line change
@@ -1,73 +1,35 @@
#!/usr/bin/env bash
set -e

# we'll modify files in src/ and then revert our changes using
# git checkout src/* so make sure they have no local changes
if git status --porcelain | grep 'src/'; then
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this up and restricted the check to only files in src

echo "Can't generate the docs when the git checkout isn't clean"
exit 1
fi

npm install --no-package-lock
tsc -p tsconfig.docgen.json

# https://github.com/TypeStrong/typedoc/issues/564
# i would like typedoc to group functions in categories but it's not supported
# yet. So I hack it with their support for external modules...

# we'll modify the files and then revert our changes using
# git reset --HARD so make sure there are no local changes
if [[ $(git status --porcelain) ]]; then
echo "Can't generate the docs when the git checkout isn't clean"
exit 1
fi

# pre-process files
node dist/scripts/make_doc_extra/make_doc_preprocess.js

# trick for the 'Option' & 'Either' constants which typedoc skips as it clashes
# with the 'Option' & 'Either' type synomym
sed -i "s/const Option/const optionGlabiboulga/" src/Option.ts
sed -i "s/const Either/const eitherGlabiboulga/" src/Either.ts
sed -i "s/const LinkedList/const linkedListGlabiboulga/" src/LinkedList.ts
sed -i "s/const Stream/const streamGlabiboulga/" src/Stream.ts
sed -i "s/const Function0/const function0Glabiboulga/" src/Function.ts
sed -i "s/const Function1/const function1Glabiboulga/" src/Function.ts
sed -i "s/const Function2/const function2Glabiboulga/" src/Function.ts
sed -i "s/const Function3/const function3Glabiboulga/" src/Function.ts
sed -i "s/const Function4/const function4Glabiboulga/" src/Function.ts
sed -i "s/const Function5/const function5Glabiboulga/" src/Function.ts
sed -i "s/const Predicate/const predicateGlabiboulga/" src/Predicate.ts
node dist/scripts/make_doc_extra/replace_in_ts.js

# generate with typedoc
./node_modules/typedoc/bin/typedoc --exclude "**/make_doc_extra/*.ts" --mode file --out apidoc --excludePrivate --excludeExternals --excludeNotExported --ignoreCompilerErrors --tsconfig tsconfig.prepublish.json src/index.ts

# revert the 'Option' & 'Either' constant rename
find apidoc -name "*.html" -exec sed -i 's/optionglabiboulga/Option/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/option<wbr>Glabiboulga/Option/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/eitherglabiboulga/Either/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/either<wbr>Glabiboulga/Either/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/linkedlistglabiboulga/LinkedList/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/linked<wbr>List<wbr>Glabiboulga/LinkedList/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/streamglabiboulga/Stream/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/stream<wbr>Glabiboulga/Stream/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function0glabiboulga/Function0/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function0<wbr>Glabiboulga/Function0/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function1glabiboulga/Function1/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function1<wbr>Glabiboulga/Function1/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function2glabiboulga/Function2/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function2<wbr>Glabiboulga/Function2/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function3glabiboulga/Function3/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function3<wbr>Glabiboulga/Function3/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function4glabiboulga/Function4/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function4<wbr>Glabiboulga/Function4/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function5glabiboulga/Function5/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/function5<wbr>Glabiboulga/Function5/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/predicateglabiboulga/Predicate/g' \{\} \;
find apidoc -name "*.html" -exec sed -i 's/predicate<wbr>Glabiboulga/Predicate/g' \{\} \;

# modify the output to say 'File' instead of 'Module'
find apidoc -name "*.html" -exec sed -i 's/Module/File/g' \{\} \;
node dist/scripts/make_doc_extra/replace_in_html.js

# modify the paths to say 'files' instead of 'modules'
mv apidoc/modules apidoc/files
find apidoc -name "*.html" -exec sed -i 's/modules/files/g' \{\} \;

node dist/scripts/make_doc_extra/make_doc_extra.js

# we're happy with the output now, revert the changes I made
# to the files to make typedoc think they're external modules
git reset --hard HEAD
git checkout src/*
36 changes: 36 additions & 0 deletions scripts/make_doc_extra/replace_in_html.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as fs from 'fs';
import * as path from 'path';
import { FILE_REPLACEMENTS, replaceInFile, SimpleReplacement } from './replacements';

async function main() {
const allHtmlReplacements = FILE_REPLACEMENTS
.flatMap(([_, constReplacement]) => constReplacement.htmlReplacements)
.appendAll([
new SimpleReplacement('Module', 'File'),
new SimpleReplacement('modules', 'files'),
]);

const promises: Promise<void>[] = [];
for await (const htmlFile of findHtml('apidoc')) {
promises.push(replaceInFile(htmlFile, allHtmlReplacements));
}
return Promise.all(promises);
}

async function* findHtml(dir: string): AsyncIterableIterator<string> {
const children = await fs.promises.readdir(dir, {withFileTypes: true});
for (const child of children) {
if (child.isDirectory()) {
yield *findHtml(path.join(dir, child.name))
} else if (child.isFile() && child.name.endsWith('.html')) {
yield path.join(dir, child.name);
}
}
}

main()
.then()
.catch(err => {
console.error(err);
process.exit(1);
});
18 changes: 18 additions & 0 deletions scripts/make_doc_extra/replace_in_ts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { replaceInFiles, FILE_REPLACEMENTS, SimpleReplacement } from './replacements';
import { Vector } from '../../src';


function main() {
return FILE_REPLACEMENTS
.map<[string, Vector<SimpleReplacement>]>(
([file, replacement]) => [file, replacement.tsReplacements]
)
.transform(replaceInFiles);
}

main()
.then()
.catch(err => {
console.error(err);
process.exit(1);
});
101 changes: 101 additions & 0 deletions scripts/make_doc_extra/replacements.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import * as fs from 'fs';
import * as readline from 'readline';
import { Vector } from '../../src';
import * as path from 'path';

export class SimpleReplacement {
readonly src: RegExp;
readonly trg: string;

constructor(_src: string, _trg: string) {
if (!_src.match(/^[a-zA-Z0-9<> ]+$/)) {
throw new Error('Not safe for regex: ' + _src);
}
this.src = new RegExp(_src, 'g');
this.trg = _trg;
}
}

const SUFFIX = 'Glabiboulga';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, what's this? :)


export class ConstReplacement {
readonly tsReplacements: Vector<SimpleReplacement>;
readonly htmlReplacements: Vector<SimpleReplacement>;

constructor(constNames: string | Vector<string>) {
const constNamesVector = constNames instanceof Vector ? constNames : Vector.of(constNames);
this.tsReplacements = constNamesVector
.map(constName => new SimpleReplacement(
`const ${constName}`,
`const ${constName}${SUFFIX}`,
));

this.htmlReplacements = constNamesVector
.flatMap(constName => Vector.of(
new SimpleReplacement(
`${constName}${SUFFIX}`.toLowerCase(),
constName,
),
new SimpleReplacement(
this.insertWbrBetweenParts(`${constName}${SUFFIX}`),
constName,
)
));
}

/**
* CamelCase42Name -> Camel<wbr>Case42<wbr>Name
*/
private insertWbrBetweenParts(text: string): string {
if (!text.match(/^[A-Z]/)) {
throw new Error('Must start with capital letter: ' + text);
}

const part = /[A-Z][^A-Z]*/g;
function* parts(): IterableIterator<string> {
for ( ; ; ) {
const res = part.exec(text);
if (!res) {
return;
}
yield res[0];
}
}
return Vector.ofIterable(parts()).mkString('<wbr>');
}
}

export const FILE_REPLACEMENTS = Vector.of<[string, ConstReplacement]>(
[path.join('src', 'Option.ts'), new ConstReplacement('Option')],
[path.join('src', 'Either.ts'), new ConstReplacement('Either')],
[path.join('src', 'LinkedList.ts'), new ConstReplacement('LinkedList')],
[path.join('src', 'Stream.ts'), new ConstReplacement('Stream')],
[path.join('src', 'Function.ts'), new ConstReplacement(Vector.of(0, 1, 2, 3, 4, 5).map(i => `Function${i}`))],
[path.join('src', 'Predicate.ts'), new ConstReplacement('Predicate')],
)

export function replaceInFiles(fileReplacements: Vector<[string, Vector<SimpleReplacement>]>): Promise<void[]> {
const promises = fileReplacements
.map(([file, replacements]) => replaceInFile(file, replacements));
return Promise.all(promises);
}

export async function replaceInFile(file: string, replacements: Vector<SimpleReplacement>): Promise<void> {
return new Promise<string>(async (resolve, reject) => {
const inInterface = readline.createInterface({input: fs.createReadStream(file)});
try {
const lines: string[] = [];
for await (const line of inInterface) {
lines.push(
replacements
.foldLeft(line, (l, repl) => l.replace(repl.src, repl.trg))
);
}
resolve(lines.map(l => l + '\n').join(''));
} catch (err) {
reject(err);
} finally {
inInterface.close();
}
}).then(lines => fs.promises.writeFile(file, lines));
}
2 changes: 1 addition & 1 deletion tsconfig.benchmarks.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "./tsconfig.base.json",
"extends": "./tsconfig.json",
"include": [
"src/**/*",
"benchmarks/**/*"
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.docgen.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "./tsconfig.base.json",
"extends": "./tsconfig.json",
"include": [
"src/**/*",
"scripts/**/*"
Expand Down
7 changes: 5 additions & 2 deletions tsconfig.base.json → tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"outDir": "./dist",
"declaration": true,
"sourceMap": true,
"lib": ["es2015", "dom"] // I need DOM because of typedoc..
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually ChromeDevToolFormatters use window so I propose removing the comment

}
"lib": ["es2015", "dom", "esnext.asynciterable"]
Copy link
Contributor Author

@aleksei-berezkin aleksei-berezkin May 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't check all files after previous contribution bringing this name, and that was a mistake. This time I found out that my IDE got lost and couldn't read any config, so it reported a lot of compilation errors. I don't know what's your favorite IDE but I suppose if you browse a lot you also gonna encounter something strange. I experimented with configs inheritance and found out that, in order to get maximum support from IDE (like code completion, jump to .d.ts), it's better to have base tsfonfig.json which is like “all-in-one”, declaring all libs and covering all files. Then, other configs may inherit from it, reducing libs and sources. So I propose this fix to configs layout.

},
"include": [
"**/*"
]
}
5 changes: 4 additions & 1 deletion tsconfig.prepublish.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"extends": "./tsconfig.base.json",
"extends": "./tsconfig.json",
"compilerOptions": {
"lib": ["es2015", "dom"]
},
"include": [
"src/**/*"
]
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "./tsconfig.base.json",
"extends": "./tsconfig.json",
"include": [
"src/**/*",
"tests/**/*"
Expand Down