diff options
Diffstat (limited to 'deno/tools')
| -rw-r--r-- | deno/tools/deno.json | 1 | ||||
| -rw-r--r-- | deno/tools/generate-geosite-rules.ts (renamed from deno/tools/gen-geosite-rules.ts) | 0 | ||||
| -rw-r--r-- | deno/tools/manage-service.ts | 39 | ||||
| -rw-r--r-- | deno/tools/template.ts | 122 | 
4 files changed, 162 insertions, 0 deletions
diff --git a/deno/tools/deno.json b/deno/tools/deno.json index 3597182..355046a 100644 --- a/deno/tools/deno.json +++ b/deno/tools/deno.json @@ -3,5 +3,6 @@    "tasks": {    },    "imports": { +    "mustache": "npm:mustache@^4.2.0"    }  } diff --git a/deno/tools/gen-geosite-rules.ts b/deno/tools/generate-geosite-rules.ts index c59d34f..c59d34f 100644 --- a/deno/tools/gen-geosite-rules.ts +++ b/deno/tools/generate-geosite-rules.ts diff --git a/deno/tools/manage-service.ts b/deno/tools/manage-service.ts new file mode 100644 index 0000000..45e09ec --- /dev/null +++ b/deno/tools/manage-service.ts @@ -0,0 +1,39 @@ +import { parseArgs } from "@std/cli"; +import { TemplateDir } from "./template.ts"; +import { join } from "@std/path"; + +if (import.meta.main) { +  const args = parseArgs(Deno.args, { +    string: ["project-dir"], +    boolean: ["no-dry-run"], +  }); + +  if (args._.length === 0) { +    throw new Error("You must specify a command."); +  } + +  const projectDir = args["project-dir"]; +  if (projectDir == null) { +    throw new Error("You must specify project-dir."); +  } + +  const command = String(args._[0]); + +  switch (command) { +    case "gen-tmpl": +      new TemplateDir( +        join(projectDir, "services/templates"), +      ).generateWithVariableFiles( +        [ +          join(projectDir, "data/config"), +          join(projectDir, "services/config.template"), +        ], +        args["no-dry-run"] === true +          ? join(projectDir, "services/generated") +          : undefined, +      ); +      break; +    default: +      throw new Error(command + " is not a valid command."); +  } +} diff --git a/deno/tools/template.ts b/deno/tools/template.ts new file mode 100644 index 0000000..0b043a1 --- /dev/null +++ b/deno/tools/template.ts @@ -0,0 +1,122 @@ +import { dirname, join, relative } from "@std/path"; +import { copySync, existsSync, walkSync } from "@std/fs"; +import { parse } from "@std/dotenv"; +import { distinct } from "@std/collections"; +// @ts-types="npm:@types/mustache" +import Mustache from "mustache"; + +Mustache.tags = ["@@", "@@"]; +Mustache.escape = (value) => String(value); + +function getVariableKeys(original: string): string[] { +  return distinct( +    Mustache.parse(original) +      .filter(function (v) { +        return v[0] === "name"; +      }) +      .map(function (v) { +        return v[1]; +      }), +  ); +} + +export function loadVariables(files: string[]): Record<string, string> { +  const vars: Record<string, string> = {}; +  for (const file of files) { +    const text = Deno.readTextFileSync(file); +    for (const [key, valueText] of Object.entries(parse(text))) { +      getVariableKeys(valueText).forEach((name) => { +        if (!(name in vars)) { +          throw new Error( +            `Variable ${name} is not defined yet, perhaps due to typos or wrong order.`, +          ); +        } +      }); +      vars[key] = Mustache.render(valueText, vars); +    } +  } +  return vars; +} + +const TEMPLATE_FILE_EXT = ".template"; + +export class TemplateDir { +  templates: { path: string; ext: string; text: string; vars: string[] }[] = []; +  plains: { path: string }[] = []; + +  constructor(public dir: string) { +    console.log("Scanning template dir:"); +    Array.from( +      walkSync(dir, { includeDirs: false, followSymlinks: true }), +    ).forEach(({ path }) => { +      path = relative(this.dir, path); +      if (path.endsWith(TEMPLATE_FILE_EXT)) { +        console.log(`  (template) ${path}`); +        const text = Deno.readTextFileSync(join(dir, path)); +        this.templates.push({ +          path, +          ext: TEMPLATE_FILE_EXT, +          text, +          vars: getVariableKeys(text), +        }); +      } else { +        console.log(`  (plain) ${path}`); +        this.plains.push({ path }); +      } +    }); +    console.log("Done scanning template dir."); +  } + +  allNeededVars() { +    return distinct(this.templates.flatMap((t) => t.vars)); +  } + +  generate(vars: Record<string, string>, generatedDir?: string) { +    console.log( +      `Generating, template dir: ${this.dir}, generated dir: ${generatedDir ?? "[dry-run]"}:`, +    ); + +    const undefinedVars = this.allNeededVars().filter((v) => !(v in vars)); +    if (undefinedVars.length !== 0) { +      throw new Error( +        `Needed variables are not defined: ${undefinedVars.join(", ")}`, +      ); +    } + +    if (generatedDir != null) { +      if (existsSync(generatedDir)) { +        console.log(` delete old generated dir ${generatedDir}`); +        Deno.removeSync(generatedDir, { recursive: true }); +      } + +      for (const file of this.plains) { +        const [source, destination] = [ +          join(this.dir, file.path), +          join(generatedDir, file.path), +        ]; +        console.log(`  copy ${source} to ${destination} ...`); +        Deno.mkdirSync(dirname(destination), { recursive: true }); +        copySync(source, destination); +      } +      for (const file of this.templates) { +        const [source, destination] = [ +          join(this.dir, file.path), +          join(generatedDir, file.path.slice(0, -file.ext.length)), +        ]; +        console.log(`  generate ${source} to ${destination} ...`); +        const rendered = Mustache.render(file.text, vars); +        Deno.mkdirSync(dirname(destination), { recursive: true }); +        Deno.writeTextFileSync(destination, rendered); +      } +    } +    console.log(`Done generating.`); +  } + +  generateWithVariableFiles(varFiles: string[], generatedDir?: string) { +    console.log("Scanning defined vars:"); +    const vars = loadVariables(varFiles); +    Object.keys(vars).forEach((name) => console.log(`  ${name}`)); +    console.log("Done scanning defined vars."); +    this.generate(vars, generatedDir); +  } +}  | 
