Skip to content

Commit 598c505

Browse files
authored
chore: add script for creating hyperfunction templates (timescale#1850)
Add script to simplify creating a new hyperfunction template directory from scratch (only works with two-step aggregate pattern for now)
1 parent 1ca5b64 commit 598c505

File tree

6 files changed

+189
-8
lines changed

6 files changed

+189
-8
lines changed

.helper-scripts/mkdir-hyperfn.mjs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import * as fs from "fs/promises";
2+
import * as path from "path";
3+
import * as readline from "node:readline/promises";
4+
5+
const HYPERFUNCTION_DIRECTORY = path.join(process.cwd(), "api/_hyperfunctions");
6+
const TEMPLATE_DIRECTORY = path.join(
7+
process.cwd(),
8+
"api/.templates/hyperfunctions"
9+
);
10+
11+
const SIMPLE_FRONTMATTER = `
12+
---
13+
section: hyperfunction
14+
subsection: <AGGREGATE>()
15+
---
16+
`.trim();
17+
18+
const writeFunctionTemplates = async (
19+
apiName,
20+
{ aggName, category, directory, functionType, isExperimental, version }
21+
) => {
22+
try {
23+
const baseTemplate = await fs.readFile(
24+
path.join(TEMPLATE_DIRECTORY, `${functionType}.md`),
25+
"utf-8"
26+
);
27+
28+
let newTemplate = baseTemplate
29+
.replace("<FUNCTION NAME, INCLUDING BRACKETS>", `${apiName}()`)
30+
.replace("<AGGREGATE NAME, INCLUDING BRACKETS>", `${aggName}()`)
31+
.replace("<BOOL>", isExperimental)
32+
.replace(
33+
"<LARGER CATEGORY FOR THIS FUNCTION, E.G., STATISTICAL AND REGRESSION ANALYSIS>",
34+
category
35+
);
36+
37+
if (isExperimental) {
38+
newTemplate = newTemplate
39+
.replace("<VERSION WHEN EXPERIMENTAL FUNCTION INTRODUCED>", version)
40+
.replace(
41+
/\n\s*stable: <VERSION WHEN STABILIZED, REMOVE IF NOT STABLE>\n/,
42+
"\n"
43+
);
44+
} else {
45+
newTemplate = newTemplate
46+
.replace("<VERSION WHEN STABILIZED, REMOVE IF NOT STABLE>", version)
47+
.replace(
48+
/\n\s*experimental: <VERSION WHEN EXPERIMENTAL FUNCTION INTRODUCED>\n/,
49+
"\n"
50+
);
51+
}
52+
53+
return fs.writeFile(path.join(directory, `${apiName}.md`), newTemplate);
54+
} catch {
55+
console.log(`Could not create template for ${apiName}`);
56+
}
57+
};
58+
59+
const rl = readline.createInterface({
60+
input: process.stdin,
61+
output: process.stdout,
62+
});
63+
64+
const twoStep = await rl.question(
65+
"Are you creating a hyperfunction group that uses the two-step aggregation pattern? (Y/n)\n"
66+
);
67+
68+
if (twoStep.match(/no?/i)) {
69+
console.log(
70+
"This tool only supports two-step hyperfunction groups for now. Sorry!"
71+
);
72+
process.exit();
73+
}
74+
75+
const aggregate = (
76+
await rl.question(
77+
"What is the name of the aggregate function in this group? (e.g., stats_agg)\n"
78+
)
79+
).replace(/\(\)$/, "");
80+
console.log(`Searching for existing directory for: ${aggregate}\n`);
81+
82+
const existingFiles = await fs.readdir(HYPERFUNCTION_DIRECTORY);
83+
const matchingFile = existingFiles.find((file) => file === aggregate);
84+
85+
if (matchingFile) {
86+
console.log(
87+
"A directory already exists for that aggregate. Add any new functions in the existing directory!"
88+
);
89+
process.exit();
90+
}
91+
92+
console.log("Directory not found. Creating directory...");
93+
const aggDirectory = path.join(HYPERFUNCTION_DIRECTORY, aggregate);
94+
await fs.mkdir(aggDirectory);
95+
96+
const promises = ["intro.md", "examples.md"].map((file) => {
97+
try {
98+
return fs.writeFile(
99+
path.join(aggDirectory, file),
100+
SIMPLE_FRONTMATTER.replace("<AGGREGATE>", aggregate)
101+
);
102+
} catch {
103+
console.log(`Could not write file ${file}`);
104+
}
105+
});
106+
107+
const isExperimental = !(
108+
await rl.question("Are these functions experimental? (Y/n)\n")
109+
).match(/no?/i);
110+
111+
const version = await rl.question(
112+
"What version are these functions being introduced in?\n"
113+
);
114+
115+
const category = await rl.question(
116+
"What larger category do these functions belong in? (e.g., percentile approximation)\n"
117+
);
118+
119+
const accessors = (
120+
await rl.question(
121+
"What are all the accessors in this group? List them separated by commas\n"
122+
)
123+
)
124+
.split(",")
125+
.map((accessor) => accessor.trim().replace(/\(\)$/, ""))
126+
.filter(Boolean);
127+
128+
const hasRollup = !(
129+
await rl.question("Does this group contain a rollup? (Y/n)\n")
130+
).match(/no?/i);
131+
132+
rl.close();
133+
134+
console.log("Creating templates...\n");
135+
136+
promises.push(
137+
writeFunctionTemplates(aggregate, {
138+
aggName: aggregate,
139+
category,
140+
directory: aggDirectory,
141+
functionType: "aggregate",
142+
isExperimental,
143+
version,
144+
})
145+
);
146+
accessors.forEach((accessor) =>
147+
promises.push(
148+
writeFunctionTemplates(accessor, {
149+
aggName: aggregate,
150+
category,
151+
directory: aggDirectory,
152+
functionType: "accessor",
153+
isExperimental,
154+
version,
155+
})
156+
)
157+
);
158+
if (hasRollup) {
159+
promises.push(
160+
writeFunctionTemplates("rollup", {
161+
aggName: aggregate,
162+
category,
163+
directory: aggDirectory,
164+
functionType: "rollup",
165+
isExperimental,
166+
version,
167+
})
168+
);
169+
}
170+
171+
await Promise.all(promises);
172+
173+
console.log(
174+
"\nTemplates created! Fill them out and contact the docs team if you need help. Thank you. :)"
175+
);
176+
process.exit();

api/.templates/hyperfunctions/accessor.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
api_name: <ACCESSOR NAME, INCLUDING BRACKETS>
2+
api_name: <FUNCTION NAME, INCLUDING BRACKETS>
33
excerpt: <BRIEF DESCRIPTION, THIS SHOWS UP IN SUMMARY LISTS OF FUNCTIONS>
44
topics: [hyperfunctions]
55
api:
@@ -14,7 +14,7 @@ hyperfunction:
1414
family: <LARGER CATEGORY FOR THIS FUNCTION, E.G., STATISTICAL AND REGRESSION ANALYSIS>
1515
type: accessor
1616
aggregates:
17-
- <**AGGREGATE** NAME, INCLUDING BRACKETS>
17+
- <AGGREGATE NAME, INCLUDING BRACKETS>
1818
api_details:
1919
summary: <LONGER DESCRIPTION OF WHAT THIS FUNCTION DOES>
2020
signatures:

api/.templates/hyperfunctions/aggregate.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
api_name: <AGGREGATE NAME, INCLUDING BRACKETS>
2+
api_name: <FUNCTION NAME, INCLUDING BRACKETS>
33
excerpt: <BRIEF DESCRIPTION, THIS SHOWS UP IN SUMMARY LISTS OF FUNCTIONS>
44
topics: [hyperfunctions]
55
api:

api/.templates/hyperfunctions/rollup.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
api_name: <ROLLUP NAME, INCLUDING BRACKETS>
2+
api_name: <FUNCTION NAME, INCLUDING BRACKETS>
33
excerpt: <BRIEF DESCRIPTION, THIS SHOWS UP IN SUMMARY LISTS OF FUNCTIONS>
44
topics: [hyperfunctions]
55
api:
@@ -14,7 +14,7 @@ hyperfunction:
1414
family: <LARGER CATEGORY FOR THIS FUNCTION, E.G., STATISTICAL AND REGRESSION ANALYSIS>
1515
type: rollup
1616
aggregates:
17-
- <**AGGREGATE** NAME, INCLUDING BRACKETS>
17+
- <AGGREGATE NAME, INCLUDING BRACKETS>
1818
api_details:
1919
summary: <LONGER DESCRIPTION OF WHAT THIS FUNCTION DOES>
2020
signatures:

package-lock.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "timescale-docs",
3+
"version": "1.0.0",
4+
"description": "Documentation for TimescaleDB, Timescale Cloud, and related Timescale products",
5+
"scripts": {
6+
"template:hyperfunction": "node ./.helper-scripts/mkdir-hyperfn.mjs"
7+
}
8+
}

0 commit comments

Comments
 (0)