parent
1db769103f
commit
e0520c0d08
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@ |
|||||||
|
{ |
||||||
|
"extends": ["../../eslintrc"] |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
# LDO-CLI |
||||||
|
|
||||||
|
A command line interface for Linked Data Objects. LDO-CLI builds `.shex` shapes into LDO types. |
||||||
|
|
||||||
|
## Setup |
||||||
|
Install the CLI |
||||||
|
|
||||||
|
```bash |
||||||
|
npm i ldo-cli --save-dev |
||||||
|
``` |
||||||
|
|
||||||
|
Set up a shapes folder |
||||||
|
```bash |
||||||
|
mkdir shapes |
||||||
|
``` |
||||||
|
|
||||||
|
Place ShexC shapes inside `.shex` files |
||||||
|
|
||||||
|
```bash |
||||||
|
touch ./shapes/example.shex |
||||||
|
``` |
||||||
|
|
||||||
|
Build the shpaes |
||||||
|
```bash |
||||||
|
ldo build --input ./shapes --output ./ldo |
||||||
|
``` |
@ -0,0 +1,5 @@ |
|||||||
|
const sharedConfig = require('../../jest.config.js'); |
||||||
|
module.exports = { |
||||||
|
...sharedConfig, |
||||||
|
'rootDir': './', |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
{ |
||||||
|
"name": "@ldo/cli", |
||||||
|
"version": "0.0.0", |
||||||
|
"description": "A Command Line Interface for Linked Data Objects", |
||||||
|
"main": "./dist/index.js", |
||||||
|
"bin": { |
||||||
|
"ldo": "./dist/index.js" |
||||||
|
}, |
||||||
|
"scripts": { |
||||||
|
"start": "node dist/index.js build", |
||||||
|
"start:init": "node dist/index.js init", |
||||||
|
"dev": "npm run build && npm run start:init", |
||||||
|
"build": "npm run clean && npm run build:ts && npm run copy-files && npm run update-permission", |
||||||
|
"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", |
||||||
|
"watch": "tsc --watch", |
||||||
|
"test": "jest --coverage", |
||||||
|
"test:watch": "jest --watch", |
||||||
|
"prepublishOnly": "npm run test && npm run build", |
||||||
|
"postinstall": "npm run build" |
||||||
|
}, |
||||||
|
"repository": { |
||||||
|
"type": "git", |
||||||
|
"url": "git+https://github.com/o-development/ldo-cli.git" |
||||||
|
}, |
||||||
|
"author": "Jackson Morgan", |
||||||
|
"license": "MIT", |
||||||
|
"bugs": { |
||||||
|
"url": "https://github.com/o-development/ldo-cli/issues" |
||||||
|
}, |
||||||
|
"homepage": "https://github.com/o-development/ldo-cli#readme", |
||||||
|
"devDependencies": { |
||||||
|
"@types/child-process-promise": "^2.2.2", |
||||||
|
"@types/ejs": "^3.1.1", |
||||||
|
"@types/fs-extra": "^9.0.13", |
||||||
|
"@types/jest": "^27.0.3", |
||||||
|
"@types/shexj": "^2.1.4", |
||||||
|
"copyfiles": "^2.4.1", |
||||||
|
"jest": "^27.4.2", |
||||||
|
"rimraf": "^3.0.2", |
||||||
|
"ts-jest": "^27.0.7" |
||||||
|
}, |
||||||
|
"dependencies": { |
||||||
|
"@shexjs/parser": "^1.0.0-alpha.24", |
||||||
|
"child-process-promise": "^2.2.1", |
||||||
|
"commander": "^9.3.0", |
||||||
|
"ejs": "^3.1.8", |
||||||
|
"fs-extra": "^10.1.0", |
||||||
|
"loading-cli": "^1.1.0", |
||||||
|
"shexj2typeandcontext": "^2.0.0" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
import fs from "fs-extra"; |
||||||
|
import path from "path"; |
||||||
|
import { Schema } from "shexj"; |
||||||
|
import parser from "@shexjs/parser"; |
||||||
|
import shexjToTypeAndContext from "shexj2typeandcontext"; |
||||||
|
import { renderFile } from "ejs"; |
||||||
|
import prettier from "prettier"; |
||||||
|
import loading from "loading-cli"; |
||||||
|
|
||||||
|
interface BuildOptions { |
||||||
|
input: string; |
||||||
|
output: string; |
||||||
|
} |
||||||
|
|
||||||
|
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 }); |
||||||
|
} |
||||||
|
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
|
||||||
|
const schema: Schema = parser |
||||||
|
.construct("https://ldo.js.org/") |
||||||
|
.parse(shexC); |
||||||
|
// Convert the content to types
|
||||||
|
const [typings, context] = await shexjToTypeAndContext(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,25 @@ |
|||||||
|
#!/usr/bin/env node |
||||||
|
|
||||||
|
import { program } from "commander"; |
||||||
|
import { build } from "./build"; |
||||||
|
import { init } from "./init"; |
||||||
|
|
||||||
|
program |
||||||
|
.name("LDO-CLI") |
||||||
|
.description("CLI to some JavaScript string utilities") |
||||||
|
.version("3.0.1"); |
||||||
|
|
||||||
|
program |
||||||
|
.command("build") |
||||||
|
.description("Build a shex folder into Shape Types") |
||||||
|
.option("-i, --input <inputPath>", "Provide the input path", "./shapes") |
||||||
|
.option("-o, --output <outputPath>", "Provide the output path", "./ldo") |
||||||
|
.action(build); |
||||||
|
|
||||||
|
program |
||||||
|
.command("init") |
||||||
|
.option("-d --directory>", "A parent directory for ldo files") |
||||||
|
.description("Initializes a project for ldo") |
||||||
|
.action(init); |
||||||
|
|
||||||
|
program.parse(); |
@ -0,0 +1,78 @@ |
|||||||
|
import { exec } from "child-process-promise"; |
||||||
|
import fs from "fs-extra"; |
||||||
|
import path from "path"; |
||||||
|
import { renderFile } from "ejs"; |
||||||
|
|
||||||
|
const DEFAULT_SHAPES_FOLDER = "./shapes"; |
||||||
|
const DEFAULT_LDO_FOLDER = "./ldo"; |
||||||
|
const POTENTIAL_PARENT_DIRECTORIES = ["src", "lib", "bin"]; |
||||||
|
|
||||||
|
export interface InitOptions { |
||||||
|
directory?: string; |
||||||
|
} |
||||||
|
|
||||||
|
export async function init(initOptions: InitOptions) { |
||||||
|
// Install dependencies
|
||||||
|
await exec("npm install ldo --save"); |
||||||
|
await exec("npm install ldo-cli @types/shexj @types/jsonld --save-dev"); |
||||||
|
|
||||||
|
// Find folder to save to
|
||||||
|
let parentDirectory = initOptions.directory; |
||||||
|
if (!parentDirectory) { |
||||||
|
parentDirectory = "./"; |
||||||
|
const allDirectories = ( |
||||||
|
await fs.promises.readdir("./", { |
||||||
|
withFileTypes: true, |
||||||
|
}) |
||||||
|
).filter((file) => file.isDirectory()); |
||||||
|
for (let i = 0; i < POTENTIAL_PARENT_DIRECTORIES.length; i++) { |
||||||
|
if ( |
||||||
|
allDirectories.some( |
||||||
|
(dir) => dir.name === POTENTIAL_PARENT_DIRECTORIES[i] |
||||||
|
) |
||||||
|
) { |
||||||
|
parentDirectory = POTENTIAL_PARENT_DIRECTORIES[i]; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Create "shapes" folder
|
||||||
|
const shapesFolderPath = path.join(parentDirectory, DEFAULT_SHAPES_FOLDER); |
||||||
|
await fs.promises.mkdir(shapesFolderPath); |
||||||
|
const defaultShapePaths = await fs.promises.readdir( |
||||||
|
path.join(__dirname, "./templates/defaultShapes") |
||||||
|
); |
||||||
|
await Promise.all( |
||||||
|
defaultShapePaths.map(async (shapePath) => { |
||||||
|
const shapeContent = await renderFile( |
||||||
|
path.join(__dirname, "./templates/defaultShapes", shapePath), |
||||||
|
{} |
||||||
|
); |
||||||
|
await fs.promises.writeFile( |
||||||
|
path.join(shapesFolderPath, `${path.parse(shapePath).name}.shex`), |
||||||
|
shapeContent |
||||||
|
); |
||||||
|
}) |
||||||
|
); |
||||||
|
|
||||||
|
// 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) |
||||||
|
); |
||||||
|
|
||||||
|
// Build LDO
|
||||||
|
await exec("npm run build:ldo"); |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
import { ContextDefinition } from "jsonld"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* <%- fileName %>Context: JSONLD Context for <%- fileName %> |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
export const <%- fileName %>Context: ContextDefinition = <%- context %>; |
@ -0,0 +1,19 @@ |
|||||||
|
# This shape is provided by default as an example |
||||||
|
# You can create your own shape to fit your needs using ShEx (https://shex.io) |
||||||
|
# Also check out https://shaperepo.com for examples of more shapes. |
||||||
|
|
||||||
|
PREFIX ex: <https://example.com/> |
||||||
|
PREFIX foaf: <http://xmlns.com/foaf/0.1/> |
||||||
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> |
||||||
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> |
||||||
|
|
||||||
|
ex:FoafProfile EXTRA a { |
||||||
|
a [ foaf:Person ] |
||||||
|
// rdfs:comment "Defines the node as a Person (from foaf)" ; |
||||||
|
foaf:name xsd:string ? |
||||||
|
// rdfs:comment "Define a person's name." ; |
||||||
|
foaf:img xsd:string ? |
||||||
|
// 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,8 @@ |
|||||||
|
import { Schema } from "shexj"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* <%- fileName %>Schema: ShexJ Schema for <%- fileName %> |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
export const <%- fileName %>Schema: Schema = <%- schema %>; |
@ -0,0 +1,24 @@ |
|||||||
|
import { ShapeType } from "ldo"; |
||||||
|
import { <%- fileName %>Schema } from "./<%- fileName %>.schema"; |
||||||
|
import { <%- fileName %>Context } from "./<%- fileName %>.context"; |
||||||
|
import { |
||||||
|
<% typings.forEach((typing) => { -%> |
||||||
|
<%- typing.dts.name %>, |
||||||
|
<% }); -%>} from "./<%- fileName %>.typings"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* LDO ShapeTypes <%- fileName %> |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
<% typings.forEach((typing) => { -%> |
||||||
|
|
||||||
|
/** |
||||||
|
* <%- typing.dts.name %> ShapeType |
||||||
|
*/ |
||||||
|
export const <%- typing.dts.name %>ShapeType: ShapeType<<%- typing.dts.name %>> = { |
||||||
|
schema: <%- fileName %>Schema, |
||||||
|
shape: "<%- typing.dts.shapeId %>", |
||||||
|
context: <%- fileName %>Context, |
||||||
|
}; |
||||||
|
<% }); -%> |
@ -0,0 +1,14 @@ |
|||||||
|
import { ContextDefinition } from "jsonld"; |
||||||
|
|
||||||
|
/** |
||||||
|
* ============================================================================= |
||||||
|
* Typescript Typings for <%- fileName %> |
||||||
|
* ============================================================================= |
||||||
|
*/ |
||||||
|
|
||||||
|
<% typings.forEach((typing) => { -%> |
||||||
|
/** |
||||||
|
* <%- typing.dts.name %> Type |
||||||
|
*/ |
||||||
|
export <%- typing.typingString -%> |
||||||
|
<% }); -%> |
@ -0,0 +1,3 @@ |
|||||||
|
it("placeholder", () => { |
||||||
|
expect(true).toBe(true); |
||||||
|
}); |
@ -0,0 +1,7 @@ |
|||||||
|
{ |
||||||
|
"extends": "../../tsconfig.base.json", |
||||||
|
"compilerOptions": { |
||||||
|
"outDir": "./dist" |
||||||
|
}, |
||||||
|
"include": ["./src"] |
||||||
|
} |
Loading…
Reference in new issue