mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2024-05-11 05:55:12 +00:00
Provide TypeScript typings for dnsconfig.js (#1830)
Co-authored-by: Jeffrey Cafferata <jeffrey@jcid.nl> Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
This commit is contained in:
@@ -16,7 +16,7 @@ var goos = flag.String("os", "", "OS to build (linux, windows, or darwin) Defaul
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
flags := fmt.Sprintf(`-s -w -X main.SHA="%s" -X main.BuildTime=%d`, getVersion(), time.Now().Unix())
|
||||
flags := fmt.Sprintf(`-s -w -X "main.SHA=%s" -X main.BuildTime=%d`, getVersion(), time.Now().Unix())
|
||||
pkg := "github.com/StackExchange/dnscontrol/v3"
|
||||
|
||||
build := func(out, goos string) {
|
||||
|
28
build/generate/dtsFile.go
Normal file
28
build/generate/dtsFile.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func generateDTSFile(funcs string) error {
|
||||
names := []string{
|
||||
"base-types",
|
||||
"fetch",
|
||||
"others",
|
||||
}
|
||||
|
||||
combined := []string{
|
||||
"// WARNING: These type definitions are experimental and subject to change in future releases.",
|
||||
}
|
||||
for _, name := range names {
|
||||
content, err := os.ReadFile(join("types", "src", name+".d.ts"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
combined = append(combined, string(content))
|
||||
}
|
||||
combined = append(combined, funcs)
|
||||
os.WriteFile(join("types", "dnscontrol.d.ts"), []byte(strings.Join(combined, "\n\n")), 0644)
|
||||
return nil
|
||||
}
|
219
build/generate/functionTypes.go
Normal file
219
build/generate/functionTypes.go
Normal file
@@ -0,0 +1,219 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func join(parts ...string) string {
|
||||
return strings.Join(parts, string(os.PathSeparator))
|
||||
}
|
||||
|
||||
func fixRuns(s string) string {
|
||||
lines := strings.Split(s, "\n")
|
||||
var out []string
|
||||
for _, line := range lines {
|
||||
if len(line) == 0 {
|
||||
if len(out) > 0 && len(out[len(out)-1]) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
out = append(out, line)
|
||||
}
|
||||
return strings.Join(out, "\n")
|
||||
}
|
||||
|
||||
var delimiterRegex = regexp.MustCompile(`(?m)^---\n`)
|
||||
|
||||
func parseFrontMatter(content string) (map[string]interface{}, string, error) {
|
||||
delimiterIndices := delimiterRegex.FindAllStringIndex(content, 2)
|
||||
startIndex := delimiterIndices[0][0]
|
||||
endIndex := delimiterIndices[1][0]
|
||||
yamlString := content[startIndex+4 : endIndex]
|
||||
var frontMatter map[string]interface{}
|
||||
err := yaml.Unmarshal([]byte(yamlString), &frontMatter)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return frontMatter, content[endIndex+4:], nil
|
||||
}
|
||||
|
||||
var returnTypes = map[string]string{
|
||||
"domain": "DomainModifier",
|
||||
"global": "void",
|
||||
"record": "RecordModifier",
|
||||
}
|
||||
|
||||
func generateFunctionTypes() (string, error) {
|
||||
funcs := []Function{}
|
||||
|
||||
srcRoot := join("docs", "_functions")
|
||||
types, err := os.ReadDir(srcRoot)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, t := range types {
|
||||
if !t.IsDir() {
|
||||
return "", errors.New("not a directory: " + join(srcRoot, t.Name()))
|
||||
}
|
||||
tPath := join(srcRoot, t.Name())
|
||||
funcNames, err := os.ReadDir(tPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, f := range funcNames {
|
||||
fPath := join(tPath, f.Name())
|
||||
if f.IsDir() {
|
||||
return "", errors.New("not a file: " + fPath)
|
||||
}
|
||||
// println("Processing", fPath)
|
||||
content, err := os.ReadFile(fPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
frontMatter, body, err := parseFrontMatter(string(content))
|
||||
if err != nil {
|
||||
println("Error parsing front matter in", fPath)
|
||||
return "", err
|
||||
}
|
||||
if frontMatter["ts_ignore"] == true {
|
||||
continue
|
||||
}
|
||||
|
||||
body = body + "\n"
|
||||
body = strings.ReplaceAll(body, "{{site.github.url}}", "https://dnscontrol.org/")
|
||||
body = strings.ReplaceAll(body, "{% capture example %}", "")
|
||||
body = strings.ReplaceAll(body, "{% capture example2 %}", "")
|
||||
body = strings.ReplaceAll(body, "{% endcapture %}", "")
|
||||
body = strings.ReplaceAll(body, "{% include example.html content=example %}", "")
|
||||
body = strings.ReplaceAll(body, "{% include example.html content=example2 %}", "")
|
||||
body = strings.ReplaceAll(body, "](#", "](https://dnscontrol.org/js#")
|
||||
body = fixRuns(body)
|
||||
|
||||
paramNames := []string{}
|
||||
if frontMatter["parameters"] != nil {
|
||||
for _, p := range frontMatter["parameters"].([]interface{}) {
|
||||
paramNames = append(paramNames, p.(string))
|
||||
}
|
||||
}
|
||||
|
||||
suppliedParamTypes := map[string]string{}
|
||||
if frontMatter["parameter_types"] != nil {
|
||||
rawTypes := frontMatter["parameter_types"].(map[string]interface{})
|
||||
for k, v := range rawTypes {
|
||||
suppliedParamTypes[k] = v.(string)
|
||||
}
|
||||
}
|
||||
|
||||
params := []Param{}
|
||||
for _, p := range paramNames {
|
||||
// start with supplied type, fall back to defaultParamType
|
||||
paramType := suppliedParamTypes[p]
|
||||
if paramType == "" {
|
||||
println("WARNING:", fPath+":", "no type for parameter ", "'"+p+"'")
|
||||
paramType = "unknown"
|
||||
}
|
||||
params = append(params, Param{Name: p, Type: paramType})
|
||||
}
|
||||
|
||||
returnType := returnTypes[t.Name()]
|
||||
if frontMatter["ts_return"] != nil {
|
||||
returnType = frontMatter["ts_return"].(string)
|
||||
} else if frontMatter["return"] != nil {
|
||||
returnType = frontMatter["return"].(string)
|
||||
}
|
||||
|
||||
if len(params) == 0 {
|
||||
if frontMatter["ts_is_function"] != true {
|
||||
params = nil
|
||||
}
|
||||
}
|
||||
|
||||
funcs = append(funcs, Function{
|
||||
Name: frontMatter["name"].(string),
|
||||
Params: params,
|
||||
ObjectParam: frontMatter["parameters_object"] == true,
|
||||
Deprecated: frontMatter["deprecated"] == true,
|
||||
ReturnType: returnType,
|
||||
Description: strings.TrimSpace(body),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
content := ""
|
||||
for _, f := range funcs {
|
||||
content += f.String()
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
type Function struct {
|
||||
Name string
|
||||
Params []Param
|
||||
ObjectParam bool
|
||||
Deprecated bool
|
||||
ReturnType string
|
||||
Description string
|
||||
}
|
||||
|
||||
type Param struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
var caser = cases.Title(language.AmericanEnglish)
|
||||
|
||||
func (f Function) formatParams() string {
|
||||
var params []string
|
||||
for _, p := range f.Params {
|
||||
name := p.Name
|
||||
if strings.HasSuffix(name, "...") {
|
||||
name = "..." + name[:len(name)-3]
|
||||
}
|
||||
if strings.Contains(name, " ") {
|
||||
name = strings.ReplaceAll(caser.String(name), " ", "")
|
||||
name = strings.ToLower(name[:1]) + name[1:]
|
||||
}
|
||||
|
||||
typeName := p.Type
|
||||
if strings.HasSuffix(typeName, "?") {
|
||||
typeName = typeName[:len(typeName)-1]
|
||||
name += "?"
|
||||
}
|
||||
|
||||
params = append(params, fmt.Sprintf("%s: %s", name, typeName))
|
||||
}
|
||||
if f.ObjectParam {
|
||||
return "opts: { " + strings.Join(params, "; ") + " }"
|
||||
} else {
|
||||
return strings.Join(params, ", ")
|
||||
}
|
||||
}
|
||||
|
||||
func (f Function) docs() string {
|
||||
content := f.Description
|
||||
if f.Deprecated {
|
||||
content += "\n\n@deprecated"
|
||||
}
|
||||
content += "\n\n@see https://dnscontrol.org/js#" + f.Name
|
||||
return "/**\n * " + strings.ReplaceAll(content, "\n", "\n * ") + "\n */"
|
||||
}
|
||||
|
||||
func (f Function) formatMain() string {
|
||||
if f.Params == nil {
|
||||
return fmt.Sprintf("declare const %s: %s", f.Name, f.ReturnType)
|
||||
}
|
||||
return fmt.Sprintf("declare function %s(%s): %s", f.Name, f.formatParams(), f.ReturnType)
|
||||
}
|
||||
|
||||
func (f Function) String() string {
|
||||
return fmt.Sprintf("%s\n%s;\n\n", f.docs(), f.formatMain())
|
||||
}
|
@@ -6,4 +6,11 @@ func main() {
|
||||
if err := generateFeatureMatrix(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
funcs, err := generateFunctionTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := generateDTSFile(funcs); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user