scaffold/src/normalize.ts

232 lines
5.2 KiB
TypeScript
Executable file

import { Sys } from "../deps.ts";
export class ProjectNormalizer {
constructor(readonly sys = Sys) {
}
async isDirectory(path: string): Promise<boolean> {
try {
return (await this.sys.stat(path)).isDirectory;
} catch {
return false;
}
}
async isFile(path: string): Promise<boolean> {
try {
return (await this.sys.stat(path)).isFile;
} catch {
return false;
}
}
async readContent(path: string): Promise<string> {
try {
return await this.sys.readTextFile(path);
} catch {
return "";
}
}
async ensureDirectory(path: string) {
if (!await this.isDirectory(path)) {
await this.sys.mkdir(path, { recursive: true });
}
}
async writeJsonFile(path: string, content: any) {
await this.sys.writeTextFile(
path,
JSON.stringify(content),
);
await this.formatPath(path);
}
async formatPath(path: string) {
await this.sys.run({
cmd: ["./run", "fmt", "-q", path],
}).status();
}
async updateRunScript() {
await this.sys.writeTextFile(
"run",
`#!/bin/sh
# Simplified run tool for deno commands
if test $# -eq 0
then
echo "Usage: $0 [file or command]"
exit 1
elif echo $1 | grep -q '.*\.ts'
then
denocmd=run
denoargs=$1
shift
else
denocmd=$1
shift
fi
denoargs="$(cat config/$denocmd.flags 2> /dev/null) $denoargs $@"
exec deno $denocmd $denoargs
`,
);
await this.sys.chmod("run", 0o755);
}
async updateDenoDefs() {
const process = this.sys.run({
cmd: ["./run", "types"],
stdout: "piped",
});
const defs = new TextDecoder("utf-8").decode(await process.output());
await this.sys.writeTextFile("deno.d.ts", defs);
}
async updateEditorConfig() {
await this.sys.writeTextFile(
".editorconfig",
`root = true
[*.{ts,json}]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
`,
);
}
async updateTsConfig() {
await this.writeJsonFile("tsconfig.json", {
compilerOptions: {
module: "esnext",
target: "ESNext",
strict: true,
noImplicitReturns: true,
noFallthroughCasesInSwitch: true,
preserveConstEnums: true,
},
});
}
async updateVscodeConf() {
await this.ensureDirectory(".vscode");
const path = ".vscode/settings.json";
const json_config = await this.readContent(path);
const config = JSON.parse(json_config || "{}");
if (!config["deno.enable"]) {
config["deno.enable"] = true;
await this.writeJsonFile(path, config);
}
await this.writeJsonFile(".vscode/tasks.json", {
version: "2.0.0",
tasks: [
{
label: "test",
type: "shell",
group: {
kind: "test",
isDefault: true,
},
command: "./run test",
},
],
});
if (await this.isFile("cli.ts")) {
await this.writeJsonFile(".vscode/launch.json", {
version: "0.2.0",
configurations: [
{
name: "Deno",
type: "node",
request: "launch",
cwd: "\${workspaceFolder}",
program: "cli.ts",
console: "externalTerminal",
attachSimplePort: 9229,
runtimeExecutable: "deno",
runtimeArgs: [
"run",
"--inspect",
].concat(
(await this.readContent("config/run.flags"))
.split(" ")
.filter((part) => !!part),
),
},
],
});
}
}
async updateGitIgnore() {
await this.sys.writeTextFile(
".gitignore",
`deno.d.ts
.vscode
.local
`,
);
}
async updateGitHooks() {
await this.sys.writeTextFile(
".git/hooks/pre-commit",
`#!/bin/sh
set -e
./run fmt --check
./run test
`,
);
await this.sys.chmod(".git/hooks/pre-commit", 0o755);
}
async updateReadme() {
const project = this.sys.cwd().split("/").pop();
let sections = "";
if (await this.isDirectory("doc")) {
const index = await this.readContent("doc/index");
for (let section of index.split("\n")) {
if (section?.trim()) {
sections += "\n" +
(await this.readContent(`doc/${section.trim()}.md`));
}
}
}
await this.sys.writeTextFile(
"README.md",
`# typescript/${project}
[![Build Status](https://thunderk.visualstudio.com/typescript/_apis/build/status/${project}?branchName=master)](https://dev.azure.com/thunderk/typescript/_build?pipelineNameFilter=${project})
${sections}`,
);
await this.formatPath("README.md");
}
async normalize() {
if (!await this.isDirectory(".git")) {
throw new Error("Not in a git repository");
}
await this.updateRunScript();
await this.updateDenoDefs();
await this.updateVscodeConf();
await this.updateTsConfig();
await this.updateEditorConfig();
await this.updateGitIgnore();
await this.updateGitHooks();
await this.updateReadme();
}
}
export async function normalize(sys = Sys) {
const normalizer = new ProjectNormalizer(sys);
await normalizer.normalize();
}