diff --git a/README.md b/README.md index 33df2cf..62c0f30 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,21 @@ TypeType is designed to generate complex typescript type with ease. > npm i -D @mistlog/typetype ``` +### CLI + +> example: [typetype-examples/package.json](https://github.com/mistlog/typetype-examples/blob/main/package.json) + +```bash +typetype build : build all *.type files in +typetype build -w : watch all *.type files in +typetype clean : remove all generated *.ts files in +typetype debug : build in debug mode(backtrace will be available) +``` + +### API + +> example: [typetype-examples/index.ts](https://github.com/mistlog/typetype-examples/blob/main/index.ts) + ```ts import { transform } from "@mistlog/typetype"; @@ -27,6 +42,32 @@ console.log(output); // output: type TypeName = T extends string ? "string" : "number"; ``` +Debug mode: + +```ts +const output = transform(input, { debug: true }).code; +``` + +when `debug` is true, backtrace will be available: + +```log +Expected end of input but ";" found. +x 1:11-1:11 MultiLineComment +| type a = 1; +| ^ +o 1:11-1:11 _ +| type a = 1; +| ^ +x 1:11-1:11 TypeFunctionDeclaration +| type a = 1; +... +|/ / +| | +|/ +o 1:1-1:11 TypeFile + type a = 1; +``` + ## Examples - all examples: https://github.com/mistlog/typetype-examples diff --git a/cli/cli.ts b/cli/cli.ts new file mode 100644 index 0000000..d2a26eb --- /dev/null +++ b/cli/cli.ts @@ -0,0 +1,54 @@ +import { default as traverse } from "filewalker"; +import { default as watch } from "node-watch"; +import { readFileSync } from "fs"; +import { transform } from "../src" +import { outputFileSync, removeSync } from "fs-extra"; + +function TraverseDirectory(path: string, callback: (relative: string, absolute: string) => void) { + const action = (relative: string, stats, absolute: string) => callback(relative, absolute); + traverse(path) + .on("file", action) + .on("error", error => console.log(error)) + .walk(); +} + +function transformFile(absolute: string, config = { debug: false }) { + const input = readFileSync(absolute, "utf8"); + const output = transform(input, config).code; + outputFileSync(absolute.replace(".type", ".ts"), output, "utf8"); +} + +export function build(dir: string, options: { watch: boolean }) { + if (options.watch) { + watch(dir, { recursive: true }, (event, absolute: string) => { + if (absolute.endsWith(".type")) { + console.log(event, absolute); + debug(absolute); + } + }); + } else { + TraverseDirectory(dir, (relative, absolute) => { + if (absolute.endsWith(".type")) { + transformFile(absolute); + } + }) + } +} + +export function clean(dir: string) { + TraverseDirectory(dir, (relative, absolute) => { + if (absolute.endsWith(".type")) { + removeSync(absolute.replace(".type", ".ts")); + } + }) +} + +export function debug(absolute: string) { + try { + transformFile(absolute, { debug: true }); + } catch (error) { + console.log(`build type failed: ${absolute}`); + console.log(error.message); + console.log(error.backtrace); + } +} \ No newline at end of file diff --git a/cli/index.ts b/cli/index.ts new file mode 100644 index 0000000..39e30d0 --- /dev/null +++ b/cli/index.ts @@ -0,0 +1,44 @@ +#!/usr/bin/env node +import { default as program } from "commander"; +import { readJSONSync, lstatSync } from "fs-extra"; +import { resolve } from "path"; +import { build, clean, debug } from "./cli"; + +const packageJSON = readJSONSync(resolve(__dirname, "../../package.json")); +program.version(packageJSON.version); + +program + .option('-w, --watch', "watch mode") + +program + .command("build ") + .description("build ts type") + .action((dir: string) => { + const path = resolve(process.cwd(), dir); + if (lstatSync(path).isDirectory()) { + const options = program.opts(); + build(path, { watch: options.watch }); + } + }); + +program + .command("clean ") + .description("clean ts type") + .action((dir: string) => { + const path = resolve(process.cwd(), dir); + if (lstatSync(path).isDirectory()) { + clean(path); + } + }); + +program + .command("debug ") + .description("debug ts type") + .action((file: string) => { + const path = resolve(process.cwd(), file); + if (!lstatSync(path).isDirectory()) { + debug(path); + } + }); + +program.parse(process.argv); diff --git a/package-lock.json b/package-lock.json index 4effc03..738d4ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -864,6 +864,15 @@ "@babel/types": "^7.3.0" } }, + "@types/fs-extra": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.10.tgz", + "integrity": "sha512-O9T2LLkRDiTlalOBdjEkcnT0MRdT2+wglCl7pJUJ3mkWkR8hX4K+5bg2raQNJcLv4V8zGuTXe7Ud3wSqkTyuyQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -1078,6 +1087,11 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -1450,6 +1464,11 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -1964,6 +1983,14 @@ "dev": true, "optional": true }, + "filewalker": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/filewalker/-/filewalker-0.1.3.tgz", + "integrity": "sha1-1jv52BO6NTRLgYJ0eCT2/3VL9MU=", + "requires": { + "fqueue": "0.0.x" + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2028,6 +2055,11 @@ "mime-types": "^2.1.12" } }, + "fqueue": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/fqueue/-/fqueue-0.0.0.tgz", + "integrity": "sha1-UzIFpPmtIbuqOPxhzvOpMML1SDY=" + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -2037,6 +2069,17 @@ "map-cache": "^0.2.2" } }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2128,8 +2171,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "growly": { "version": "1.3.0", @@ -3105,6 +3147,15 @@ "minimist": "^1.2.5" } }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -3402,6 +3453,11 @@ "which": "^1.3.0" } }, + "node-watch": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.1.tgz", + "integrity": "sha512-UWblPYuZYrkCQCW5PxAwYSxaELNBLUckrTBBk8xr1/bUgyOkYYTsUcV4e3ytcazFEOyiRyiUrsG37pu6I0I05g==" + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -4582,6 +4638,11 @@ "set-value": "^2.0.1" } }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", diff --git a/package.json b/package.json index d5878fb..b03c837 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,12 @@ "files": [ "build" ], + "bin": { + "typetype": "build/cli/index.js" + }, "license": "MIT", "devDependencies": { + "@types/fs-extra": "^9.0.10", "@types/jest": "^24.0.17", "@types/node": "^14.14.22", "jest": "^24.8.0", @@ -33,6 +37,10 @@ }, "dependencies": { "@babel/core": "^7.12.10", + "commander": "^7.2.0", + "filewalker": "^0.1.3", + "fs-extra": "^9.1.0", + "node-watch": "^0.7.1", "pegjs-backtrace": "^0.2.0", "react-peg": "^0.1.7" } diff --git a/src/core/core.tsx b/src/core/core.tsx index 5c4fbb1..deed4fb 100644 --- a/src/core/core.tsx +++ b/src/core/core.tsx @@ -2,7 +2,7 @@ import { ReactPeg } from "react-peg"; import { TypeFile } from "../parser"; import { TSFile } from "../generator"; import generate from "@babel/generator"; -import * as Tracer from "pegjs-backtrace"; +import { default as Tracer } from "pegjs-backtrace"; import { IRenderConfig } from "react-peg/build/renderer/renderer"; export interface ITypeTypeConfig { diff --git a/tsconfig.json b/tsconfig.json index a18c868..5b058b2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,9 +6,11 @@ "sourceMap": false, "outDir": "./build", "declaration": true, - "module": "commonjs" + "module": "commonjs", + "esModuleInterop": true }, "include": [ - "src/**/*" + "src/**/*", + "cli/**/*" ] } \ No newline at end of file