diff --git a/package-lock.json b/package-lock.json index 7775f6a..3a76126 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8093,6 +8093,32 @@ "@ts-jison/lexer": "^0.4.1-alpha.1" } }, + "node_modules/@ts-morph/common": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.25.0.tgz", + "integrity": "sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg==", + "license": "MIT", + "dependencies": { + "minimatch": "^9.0.4", + "path-browserify": "^1.0.1", + "tinyglobby": "^0.2.9" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "devOptional": true, @@ -8569,6 +8595,17 @@ "version": "2.7.3", "license": "MIT" }, + "node_modules/@types/prompts": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/prompts/-/prompts-2.4.9.tgz", + "integrity": "sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "kleur": "^3.0.3" + } + }, "node_modules/@types/prop-types": { "version": "15.7.11", "dev": true, @@ -11031,6 +11068,12 @@ "node": ">=4" } }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "license": "MIT" + }, "node_modules/codepage": { "version": "1.15.0", "dev": true, @@ -21261,6 +21304,12 @@ "tslib": "^2.0.3" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "license": "MIT", @@ -22759,6 +22808,8 @@ }, "node_modules/prompts": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "license": "MIT", "dependencies": { "kleur": "^3.0.3", @@ -26605,6 +26656,45 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmp": { "version": "0.2.1", "dev": true, @@ -26713,6 +26803,16 @@ "version": "0.1.13", "license": "Apache-2.0" }, + "node_modules/ts-morph": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-24.0.0.tgz", + "integrity": "sha512-2OAOg/Ob5yx9Et7ZX4CvTCc0UFoZHwLEJ+dpDPSUi5TgwwlTlX47w+iFRrEwzUZwYACjq83cgjS/Da50Ga37uw==", + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.25.0", + "code-block-writer": "^13.0.3" + } + }, "node_modules/ts-node": { "version": "10.9.2", "devOptional": true, @@ -28648,7 +28748,9 @@ "fs-extra": "^10.1.0", "loading-cli": "^1.1.0", "prettier": "^3.0.3", - "type-fest": "^4.31.0" + "prompts": "^2.4.2", + "ts-morph": "^24.0.0", + "type-fest": "^2.19.0" }, "bin": { "ldo": "dist/index.js" @@ -28658,6 +28760,7 @@ "@types/ejs": "^3.1.1", "@types/fs-extra": "^9.0.13", "@types/jest": "^27.0.3", + "@types/prompts": "^2.4.9", "@types/shexj": "2.1.4", "copyfiles": "^2.4.1", "jest": "^27.4.2", @@ -28728,12 +28831,12 @@ } }, "packages/cli/node_modules/type-fest": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.31.0.tgz", - "integrity": "sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=16" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/packages/cli/package.json b/packages/cli/package.json index 1c7d33f..4f59999 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -21,7 +21,7 @@ "prepublishOnly": "npm run test && npm run build", "lint": "eslint src/** --fix --no-error-on-unmatched-pattern", "build:ldo": "./dist/index.js build --input test/.shapes --output test/.ldo", - "generate-readme": "./dist/index.js generate-readme --project ./test --shapes ./test/.shapes/ --ldo ./test/.ldo/" + "generate-readme": "./dist/index.js generate-readme --project ./test --shapes ./test/.shapes/ --ldo ./test/.ldo/" }, "repository": { "type": "git", @@ -38,6 +38,7 @@ "@types/ejs": "^3.1.1", "@types/fs-extra": "^9.0.13", "@types/jest": "^27.0.3", + "@types/prompts": "^2.4.9", "@types/shexj": "2.1.4", "copyfiles": "^2.4.1", "jest": "^27.4.2", @@ -53,7 +54,9 @@ "fs-extra": "^10.1.0", "loading-cli": "^1.1.0", "prettier": "^3.0.3", - "type-fest": "^4.31.0" + "prompts": "^2.4.2", + "ts-morph": "^24.0.0", + "type-fest": "^2.19.0" }, "files": [ "dist", diff --git a/packages/cli/src/create.ts b/packages/cli/src/create.ts index b37ec60..4f3d1ff 100644 --- a/packages/cli/src/create.ts +++ b/packages/cli/src/create.ts @@ -1,23 +1,84 @@ -import { exec } from "child-process-promise"; import { init } from "./init"; -import { modifyPackageJson } from "./util/modifyPackageJson"; +import { modifyPackageJson, savePackageJson } from "./util/modifyPackageJson"; import { generateReadme } from "./generateReadme"; import path from "path"; +import prompts from "prompts"; +import type { PackageJson } from "type-fest"; -interface CreateOptions { - directory: string; - name: string; -} - -export async function create(options: CreateOptions) { +export async function create(directory: string) { // Init the NPM Package - await exec(`npm init ${options.directory}`); + const responses = await prompts([ + { + type: "text", + name: "name", + message: "Package name:", + initial: path.basename(process.cwd()), + }, + { + type: "text", + name: "version", + message: "Version:", + initial: "1.0.0", + }, + { + type: "text", + name: "description", + message: "Description:", + }, + { + type: "list", + name: "keywords", + message: "Keywords (comma separated):", + separator: ",", + }, + { + type: "text", + name: "author", + message: "Author:", + }, + { + type: "text", + name: "license", + message: "License:", + initial: "MIT", + }, + { + type: "text", + name: "repository", + message: "Git repository (optional):", + }, + ]); + + const packageJson: PackageJson = { + name: responses.name, + version: responses.version, + description: responses.description, + keywords: responses.keywords, + author: responses.author, + license: responses.license, + }; + + if (responses.repository) { + packageJson.repository = { + type: "git", + url: responses.repository, + }; + packageJson.bugs = { + url: `${responses.repository.replace(/\.git$/, "")}/issues`, + }; + packageJson.homepage = `${responses.repository.replace( + /\.git$/, + "", + )}#readme`; + } + + await savePackageJson(directory, packageJson); // Init LDO - await init({ directory: options.directory }); + await init({ directory }); // Add prepublish script - await modifyPackageJson(async (packageJson) => { + await modifyPackageJson(directory, async (packageJson) => { if (!packageJson.scripts) packageJson.scripts = {}; packageJson.scripts.prepublish = "npm run build:ldo & npm run generate-readme"; @@ -29,8 +90,8 @@ export async function create(options: CreateOptions) { // Generate ReadMe await generateReadme({ - readmePath: path.join(options.directory, "README.md"), - shapesPath: path.join(options.directory, ".shapes"), - ldoPath: path.join(options.directory, ".ldo"), + project: directory, + shapes: path.join(directory, ".shapes"), + ldo: path.join(directory, ".ldo"), }); } diff --git a/packages/cli/src/generateReadme.ts b/packages/cli/src/generateReadme.ts index 0b4f885..bbf9670 100644 --- a/packages/cli/src/generateReadme.ts +++ b/packages/cli/src/generateReadme.ts @@ -2,11 +2,13 @@ import { getPackageJson } from "./util/modifyPackageJson"; import { forAllShapes } from "./util/forAllShapes"; import { promises as fs } from "fs"; import path from "path"; +import { Project } from "ts-morph"; +import { renderFile } from "ejs"; interface GenerateReadmeOptions { - projectFolder: string; - shapesPath: string; - ldoPath: string; + project: string; + shapes: string; + ldo: string; } interface ReadmeEjsOptions { @@ -24,27 +26,54 @@ interface ReadmeEjsOptions { } export async function generateReadme(options: GenerateReadmeOptions) { - const packageJson = await getPackageJson(options.projectFolder); - const projectName = packageJson.name; - const projectDescription = packageJson.description; + const packageJson = await getPackageJson(options.project); + const projectName = packageJson.name!; + const projectDescription = packageJson.description!; const shapes: ReadmeEjsOptions["shapes"] = []; - await forAllShapes(options.shapesPath, async (fileName, shexC) => { - const typesRaw = await fs.readFile( - path.join(options.shapesPath, `${fileName}.typings.ts`), - "utf8", - ); - const shapeTypesRaw = await fs.readFile( - path.join(options.shapesPath, `${fileName}.shapeTypes.ts`), - "utf8", - ); - - console.log(typesRaw); - console.log(shapeTypesRaw); - - // shapes.push({ - // name: fileName, - // shex: shexC, - // }); + await forAllShapes(options.shapes, async (fileName, shexC) => { + const typeFilePath = path.join(options.shapes, `${fileName}.typings.ts`); + + const typesRaw = await fs.readFile(typeFilePath, "utf8"); + + const shape: ReadmeEjsOptions["shapes"][0] = { + name: fileName, + shex: shexC, + typescript: typesRaw, + types: [], + }; + + listInterfaces(typeFilePath).forEach((interfaceName) => { + shape.types.push({ + typeName: interfaceName, + shapeTypeName: `${interfaceName}ShapeType`, + }); + }); }); + + const readmeEjsOptions: ReadmeEjsOptions = { + projectName, + projectDescription, + shapes, + }; + + // Save Readme + const finalContent = await renderFile( + path.join(__dirname, "./templates/readme/", "main.ejs"), + readmeEjsOptions, + ); + // Save readme to document + await fs.writeFile(path.join(options.project, "README.md"), finalContent); +} + +/** + * Helper Function that lists all the interfaces in a typescript file + */ +function listInterfaces(filePath: string): string[] { + const project = new Project(); + const sourceFile = project.addSourceFileAtPath(filePath); + + // Get all interfaces in the file + const interfaces = sourceFile.getInterfaces().map((iface) => iface.getName()); + return interfaces; } diff --git a/packages/cli/src/init.ts b/packages/cli/src/init.ts index ddafd50..c031381 100644 --- a/packages/cli/src/init.ts +++ b/packages/cli/src/init.ts @@ -58,7 +58,7 @@ export async function init(initOptions: InitOptions) { ); // Add build script - await modifyPackageJson("./", async (packageJson) => { + await modifyPackageJson(parentDirectory, async (packageJson) => { if (!packageJson.scripts) { packageJson.scripts = {}; } diff --git a/packages/cli/src/templates/readme/main.ejs b/packages/cli/src/templates/readme/main.ejs index 8c63de6..1ce6681 100644 --- a/packages/cli/src/templates/readme/main.ejs +++ b/packages/cli/src/templates/readme/main.ejs @@ -1,15 +1,15 @@ -# <%= projectname %> +# <%= projectName %> -<%= projectDescrition %> +<%- projectDescription %> This project includes shapes and generated files for [LDO](https://ldo.js.org). ## Installation ```bash -npm i <%= projectname %> +npm i <%= projectName %> ``` <% shapes.forEach(function(shape) { %> - <%- include('shape', { shape: shape, projectname: projectname }) %> +<%- include('shape', { shape: shape, projectName: projectName }) %> <% }); %> \ No newline at end of file diff --git a/packages/cli/src/templates/readme/shape.ejs b/packages/cli/src/templates/readme/shape.ejs index 38b694d..58f5f0a 100644 --- a/packages/cli/src/templates/readme/shape.ejs +++ b/packages/cli/src/templates/readme/shape.ejs @@ -4,8 +4,8 @@ ```typescript import { createLdoDataset } from "@ldo/ldo"; -import { <%= shape.types.map((type) => type.shapeTypeName).join(", ") %> } from "<%= projectName =>"; -import type { <%= shape.types.map((type) => type.typeName).join(", ") %> } from "<%= projectName =>"; +import { <%= shape.types.map((type) => type.shapeTypeName).join(", ") %> } from "<%= projectName %>"; +import type { <%= shape.types.map((type) => type.typeName).join(", ") %> } from "<%= projectName %>"; const ldoDataset = createLdoDataset(); <% shape.types.forEach(function(type, index) { %> const example<%= index %>: <%= type.typeName %> = ldoDataset diff --git a/packages/cli/src/util/modifyPackageJson.ts b/packages/cli/src/util/modifyPackageJson.ts index 54adab4..9854a7e 100644 --- a/packages/cli/src/util/modifyPackageJson.ts +++ b/packages/cli/src/util/modifyPackageJson.ts @@ -12,14 +12,21 @@ export async function getPackageJson( ); } +export async function savePackageJson( + projectFolder: string, + packageJson: PackageJson, +): Promise { + await fs.promises.writeFile( + path.join(projectFolder, "./package.json"), + JSON.stringify(packageJson, null, 2), + ); +} + export async function modifyPackageJson( projectFolder: string, modifyCallback: (packageJson: PackageJson) => Promise, ): Promise { const packageJson: PackageJson = await getPackageJson(projectFolder); const newPackageJson = await modifyCallback(packageJson); - await fs.promises.writeFile( - path.join(projectFolder, "./package.json"), - JSON.stringify(newPackageJson, null, 2), - ); + await savePackageJson(projectFolder, newPackageJson); }