untested readme generator

main
Jackson Morgan 9 months ago
parent 8c50776013
commit 22c476d3ef
  1. 15
      package-lock.json
  2. 1
      packages/cli/.gitignore
  3. 8
      packages/cli/package.json
  4. 93
      packages/cli/src/build.ts
  5. 36
      packages/cli/src/create.ts
  6. 50
      packages/cli/src/generateReadme.ts
  7. 29
      packages/cli/src/index.ts
  8. 26
      packages/cli/src/init.ts
  9. 15
      packages/cli/src/templates/readme/main.ejs
  10. 27
      packages/cli/src/templates/readme/shape.ejs
  11. 26
      packages/cli/src/util/forAllShapes.ts
  12. 25
      packages/cli/src/util/modifyPackageJson.ts
  13. 2
      packages/cli/test/.shapes/foafProfile.shex
  14. 4
      packages/cli/test/package.json

15
package-lock.json generated

@ -28647,7 +28647,8 @@
"ejs": "^3.1.8",
"fs-extra": "^10.1.0",
"loading-cli": "^1.1.0",
"prettier": "^3.0.3"
"prettier": "^3.0.3",
"type-fest": "^4.31.0"
},
"bin": {
"ldo": "dist/index.js"
@ -28726,6 +28727,18 @@
}
}
},
"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==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"packages/cli/node_modules/typescript": {
"version": "4.9.5",
"dev": true,

@ -0,0 +1 @@
test/.ldo

@ -14,13 +14,14 @@
"build:ts": "tsc --project tsconfig.build.json",
"clean": "rimraf dist/",
"copy-files": "copyfiles -u 1 \"./src/**/*.ejs\" dist/",
"update-permission": "chmod +x ../../node_modules/.bin/ldo",
"update-permission": "chmod +x ./dist/index.js",
"watch": "tsc --watch",
"test": "jest --coverage",
"test:watch": "jest --watch",
"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"
"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/"
},
"repository": {
"type": "git",
@ -51,7 +52,8 @@
"ejs": "^3.1.8",
"fs-extra": "^10.1.0",
"loading-cli": "^1.1.0",
"prettier": "^3.0.3"
"prettier": "^3.0.3",
"type-fest": "^4.31.0"
},
"files": [
"dist",

@ -6,6 +6,7 @@ import schemaConverterShex from "@ldo/schema-converter-shex";
import { renderFile } from "ejs";
import prettier from "prettier";
import loading from "loading-cli";
import { forAllShapes } from "./util/forAllShapes";
interface BuildOptions {
input: string;
@ -15,13 +16,6 @@ interface BuildOptions {
export async function build(options: BuildOptions) {
const load = loading("Peparing Environment");
load.start();
const shapeDir = await fs.promises.readdir(options.input, {
withFileTypes: true,
});
// Filter out non-shex documents
const shexFiles = shapeDir.filter(
(file) => file.isFile() && file.name.endsWith(".shex"),
);
// Prepare new folder by clearing/and/or creating it
if (fs.existsSync(options.output)) {
await fs.promises.rm(options.output, { recursive: true });
@ -29,51 +23,44 @@ export async function build(options: BuildOptions) {
await fs.promises.mkdir(options.output);
load.text = "Generating LDO Documents";
await Promise.all(
shexFiles.map(async (file) => {
const fileName = path.parse(file.name).name;
// Get the content of each document
const shexC = await fs.promises.readFile(
path.join(options.input, file.name),
"utf8",
);
// Convert to ShexJ
let schema: Schema;
try {
schema = parser.construct("https://ldo.js.org/").parse(shexC);
} catch (err) {
const errMessage =
err instanceof Error
? err.message
: typeof err === "string"
? err
: "Unknown Error";
console.error(`Error processing ${file.name}: ${errMessage}`);
return;
}
// Convert the content to types
const [typings, context] = await schemaConverterShex(schema);
await Promise.all(
["context", "schema", "shapeTypes", "typings"].map(
async (templateName) => {
const finalContent = await renderFile(
path.join(__dirname, "./templates", `${templateName}.ejs`),
{
typings: typings.typings,
fileName,
schema: JSON.stringify(schema, null, 2),
context: JSON.stringify(context, null, 2),
},
);
// Save conversion to document
await fs.promises.writeFile(
path.join(options.output, `${fileName}.${templateName}.ts`),
await prettier.format(finalContent, { parser: "typescript" }),
);
},
),
);
}),
);
await forAllShapes(options.input, async (fileName, shexC) => {
// Convert to ShexJ
let schema: Schema;
try {
schema = parser.construct("https://ldo.js.org/").parse(shexC);
} catch (err) {
const errMessage =
err instanceof Error
? err.message
: typeof err === "string"
? err
: "Unknown Error";
console.error(`Error processing ${fileName}: ${errMessage}`);
return;
}
// Convert the content to types
const [typings, context] = await schemaConverterShex(schema);
await Promise.all(
["context", "schema", "shapeTypes", "typings"].map(
async (templateName) => {
const finalContent = await renderFile(
path.join(__dirname, "./templates", `${templateName}.ejs`),
{
typings: typings.typings,
fileName,
schema: JSON.stringify(schema, null, 2),
context: JSON.stringify(context, null, 2),
},
);
// Save conversion to document
await fs.promises.writeFile(
path.join(options.output, `${fileName}.${templateName}.ts`),
await prettier.format(finalContent, { parser: "typescript" }),
);
},
),
);
});
load.stop();
}

@ -0,0 +1,36 @@
import { exec } from "child-process-promise";
import { init } from "./init";
import { modifyPackageJson } from "./util/modifyPackageJson";
import { generateReadme } from "./generateReadme";
import path from "path";
interface CreateOptions {
directory: string;
name: string;
}
export async function create(options: CreateOptions) {
// Init the NPM Package
await exec(`npm init ${options.directory}`);
// Init LDO
await init({ directory: options.directory });
// Add prepublish script
await modifyPackageJson(async (packageJson) => {
if (!packageJson.scripts) packageJson.scripts = {};
packageJson.scripts.prepublish =
"npm run build:ldo & npm run generate-readme";
packageJson.scripts[
"genenerate-readme"
] = `ldo generate-readme --readme ./README.md --shapes ./.shapes --ldo ./ldo`;
return packageJson;
});
// Generate ReadMe
await generateReadme({
readmePath: path.join(options.directory, "README.md"),
shapesPath: path.join(options.directory, ".shapes"),
ldoPath: path.join(options.directory, ".ldo"),
});
}

@ -0,0 +1,50 @@
import { getPackageJson } from "./util/modifyPackageJson";
import { forAllShapes } from "./util/forAllShapes";
import { promises as fs } from "fs";
import path from "path";
interface GenerateReadmeOptions {
projectFolder: string;
shapesPath: string;
ldoPath: string;
}
interface ReadmeEjsOptions {
projectName: string;
projectDescription: string;
shapes: {
name: string;
types: {
typeName: string;
shapeTypeName: string;
}[];
shex: string;
typescript: string;
}[];
}
export async function generateReadme(options: GenerateReadmeOptions) {
const packageJson = await getPackageJson(options.projectFolder);
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,
// });
});
}

@ -3,6 +3,7 @@
import { program } from "commander";
import { build } from "./build";
import { init } from "./init";
import { create } from "./create";
program
.name("LDO-CLI")
@ -18,8 +19,34 @@ program
program
.command("init")
.option("-d --directory>", "A parent directory for ldo files")
.argument("[directory]", "A parent directory for ldo files")
.description("Initializes a project for LDO.")
.action(init);
program
.command("create")
.argument("<directory>", "The package's directory", "./")
.description("Creates a standalone package for shapes to publish to NPM.")
.action(create);
program
.command("generate-readme")
.description("Create a ReadMe from the shapes and generated code.")
.requiredOption(
"-r, --readme <readmePath>",
"Provide the path to the readme",
"./.shapes",
)
.requiredOption(
"-s, --shapes <shapesPath>",
"Provide the path to the shapes folder",
"./.shapes",
)
.requiredOption(
"-s, --ldo <ldoPath>",
"Provide the path to the ldo folder",
"./.ldo",
)
.action(build);
program.parse();

@ -2,6 +2,7 @@ import { exec } from "child-process-promise";
import fs from "fs-extra";
import path from "path";
import { renderFile } from "ejs";
import { modifyPackageJson } from "./util/modifyPackageJson";
const DEFAULT_SHAPES_FOLDER = "./.shapes";
const DEFAULT_LDO_FOLDER = "./.ldo";
@ -57,21 +58,16 @@ export async function init(initOptions: InitOptions) {
);
// Add build script
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const packageJson: any = JSON.parse(
(await fs.promises.readFile("./package.json")).toString(),
);
if (!packageJson.scripts) {
packageJson.scripts = {};
}
const ldoFolder = path.join(parentDirectory, DEFAULT_LDO_FOLDER);
packageJson.scripts[
"build:ldo"
] = `ldo build --input ${shapesFolderPath} --output ${ldoFolder}`;
await fs.promises.writeFile(
"./package.json",
JSON.stringify(packageJson, null, 2),
);
await modifyPackageJson("./", async (packageJson) => {
if (!packageJson.scripts) {
packageJson.scripts = {};
}
const ldoFolder = path.join(parentDirectory, DEFAULT_LDO_FOLDER);
packageJson.scripts[
"build:ldo"
] = `ldo build --input ${shapesFolderPath} --output ${ldoFolder}`;
return packageJson;
});
// Build LDO
await exec("npm run build:ldo");

@ -0,0 +1,15 @@
# <%= projectname %>
<%= projectDescrition %>
This project includes shapes and generated files for [LDO](https://ldo.js.org).
## Installation
```bash
npm i <%= projectname %>
```
<% shapes.forEach(function(shape) { %>
<%- include('shape', { shape: shape, projectname: projectname }) %>
<% }); %>

@ -0,0 +1,27 @@
## <%= shape.name %>
### Usage with LDO
```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 =>";
const ldoDataset = createLdoDataset();
<% shape.types.forEach(function(type, index) { %>
const example<%= index %>: <%= type.typeName %> = ldoDataset
.usingType(<%= type.shapeTypeName %>)
.fromSubject("http://example.com/example<%= index %>");
<%> }); <%>
```
### ShEx Typings
```shex
<%= shape.shex %>
```
### TypeScript Typings
```typescript
<%= shape.typescript %>
```

@ -0,0 +1,26 @@
import fs from "fs";
import path from "path";
export async function forAllShapes(
shapePath: string,
callback: (filename: string, shape: string) => Promise<void>,
): Promise<void> {
const shapeDir = await fs.promises.readdir(shapePath, {
withFileTypes: true,
});
// Filter out non-shex documents
const shexFiles = shapeDir.filter(
(file) => file.isFile() && file.name.endsWith(".shex"),
);
await Promise.all(
shexFiles.map(async (file) => {
const fileName = path.parse(file.name).name;
// Get the content of each document
const shexC = await fs.promises.readFile(
path.join(shapePath, file.name),
"utf8",
);
await callback(fileName, shexC);
}),
);
}

@ -0,0 +1,25 @@
import type { PackageJson } from "type-fest";
import fs from "fs-extra";
import path from "path";
export async function getPackageJson(
projectFolder: string,
): Promise<PackageJson> {
return JSON.parse(
(
await fs.promises.readFile(path.join(projectFolder, "./package.json"))
).toString(),
);
}
export async function modifyPackageJson(
projectFolder: string,
modifyCallback: (packageJson: PackageJson) => Promise<PackageJson>,
): Promise<void> {
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),
);
}

@ -13,7 +13,7 @@ ex:FoafProfile EXTRA a {
foaf:name xsd:string ?
// rdfs:comment "Define a person's name." ;
foaf:img xsd:string ?
// rdfs:comment "Photo link but in string form"
// rdfs:comment "Photo link but in string form" ;
foaf:knows @ex:FoafProfile *
// rdfs:comment "A list of WebIds for all the people this user knows." ;
}

@ -0,0 +1,4 @@
{
"name": "foaf-profile",
"description": "A profile using FOAF."
}
Loading…
Cancel
Save