create-wot-app
Version:
CLI tool to create a demo of @web-of-trust/ui Components Library.
412 lines (371 loc) • 10.9 kB
JavaScript
#!/usr/bin/env node
const fs = require('fs').promises;
const path = require('path');
const { execSync } = require('child_process');
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// Template content
const templates = {
packageJson: (projectName) => ({
name: projectName,
version: "1.0.0",
private: true,
scripts: {
"dev": "webpack serve",
"build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
dependencies: {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@web-of-trust/ui": "^1.0.5",
"@mui/material": "^5.0.0",
"@emotion/react": "^11.0.0",
"@emotion/styled": "^11.0.0"
},
devDependencies: {
"webpack": "^5.0.0",
"webpack-cli": "^4.0.0",
"webpack-dev-server": "^4.0.0",
"ts-loader": "^9.0.0",
"html-webpack-plugin": "^5.0.0",
"typescript": "^4.0.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0"
}
}),
tsconfig: {
compilerOptions: {
target: "ES6",
module: "ES6",
jsx: "react-jsx",
strict: true,
moduleResolution: "node",
esModuleInterop: true,
skipLibCheck: true
},
include: ["src/**/*"],
exclude: ["node_modules"]
},
webpackConfig: `const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
module: {
rules: [
{
test: /\\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
devServer: {
static: './dist',
port: 3000,
},
mode: 'development',
};`,
indexHtml: `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Of Trust App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>`,
indexTsx: `import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(
document.getElementById('root')!
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);`,
appTsx: `import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import { theme } from './lib/theme';
import CredentialView from './components/CredentialView';
function App() {
return (
<ThemeProvider theme={theme}>
<div>
<h1>Web Of Trust Application</h1>
<CredentialView />
</div>
</ThemeProvider>
);
}
export default App;`,
themeTsx: `import { createTheme } from '@mui/material/styles';
declare module '@mui/material/styles' {
interface Palette {
t3BodyText: string;
bgCredentialDetails: string;
}
interface PaletteOptions {
t3BodyText?: string;
bgCredentialDetails?: string;
}
}
export const theme = createTheme({
palette: {
primary: {
main: '#003FE0',
},
t3BodyText: '#202e5b',
bgCredentialDetails: '#C2F1BE',
},
});`,
constantsTs: `import { ClaimDetail } from '@web-of-trust/ui';
export const CLAIM_DATA: ClaimDetail = {
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json',
],
id: 'urn:uuid:123',
type: ['VerifiableCredential', 'OpenBadgeCredential'],
issuer: {
id: 'did:key:example',
type: ['Profile'],
},
issuanceDate: '2024-12-27T17:43:58.457Z',
expirationDate: '2025-12-27T17:43:58.455Z',
credentialSubject: {
type: ['AchievementSubject'],
name: 'John Doe',
duration: '3 months',
achievement: [
{
id: 'urn:uuid:leadership-achievement',
type: ['Achievement'],
name: 'Leadership Excellence',
description: 'Demonstrated exceptional leadership skills',
criteria: {
narrative: 'Led multiple successful team projects',
},
},
],
portfolio: [
{
'@type': 'schema:CreativeWork',
name: 'Project Documentation',
url: 'https://example.com/doc',
},
],
evidenceLink: '',
evidenceDescription:
'Describe how you earned this skill to test the text wrapping',
credentialType: '',
},
proof: {
type: 'Ed25519Signature2020',
created: '2024-12-27T17:43:58Z',
verificationMethod: 'did:key:example',
proofPurpose: 'assertionMethod',
proofValue: 'example-proof-value',
},
};
export const CLAIM_DATA_WITH_IMAGE: ClaimDetail = {
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json',
{
duration: 'https://schema.org/duration',
fullName: 'https://schema.org/name',
portfolio: 'https://schema.org/portfolio',
evidenceLink: 'https://schema.org/evidenceLink',
evidenceDescription: 'https://schema.org/evidenceDescription',
credentialType: 'https://schema.org/credentialType',
},
'https://w3id.org/security/suites/ed25519-2020/v1',
],
id: 'urn:uuid:456',
type: ['VerifiableCredential', 'OpenBadgeCredential'],
issuer: {
id: 'did:key:example2',
type: ['Profile'],
},
issuanceDate: '2025-01-08T18:09:45.621Z',
expirationDate: '2026-01-08T18:09:45.615Z',
credentialSubject: {
type: ['AchievementSubject'],
name: 'Jane Smith',
duration: '5 years',
achievement: [
{
id: 'urn:uuid:software-dev-achievement',
type: ['Achievement'],
name: 'Software Development',
description: 'Developed comprehensive software solutions',
criteria: {
narrative: 'Contributed to multiple software projects',
},
image: {
id: 'https://placehold.co/600x400',
type: 'Image',
},
},
],
portfolio: [
{
'@type': 'schema:CreativeWork',
name: 'Project Portfolio',
url: 'https://example.com/portfolio',
},
],
evidenceLink: 'https://placehold.co/600x400',
credentialType: 'Software Engineering',
},
proof: {
type: 'Ed25519Signature2020',
created: '2025-01-08T18:09:45Z',
verificationMethod: 'did:key:example2',
proofPurpose: 'assertionMethod',
proofValue: 'example-proof-value-2',
},
};
export const COMMENTS: ClaimDetail[] = [
{
'@context': [
'https://www.w3.org/2018/credentials/v1',
'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json',
],
id: 'comment1',
type: ['VerifiableCredential', 'OpenBadgeCredential'],
issuer: {
id: 'did:key:commenter',
type: ['Profile'],
},
issuanceDate: '2024-12-27T17:43:58.457Z',
expirationDate: '2025-12-27T17:43:58.455Z',
credentialSubject: {
name: 'Jane Smith',
howKnow: 'Worked together for 2 years',
recommendationText: 'Excellent team player and leader',
qualifications: 'Senior Manager',
explainAnswer: 'Witnessed leadership abilities firsthand',
},
proof: {
type: 'Ed25519Signature2020',
created: '2024-12-27T17:43:58Z',
verificationMethod: 'did:key:commenter',
proofPurpose: 'assertionMethod',
proofValue: 'example-comment-proof-value',
},
},
];
`,
credentialViewTsx: `import React, { useState } from 'react';
import { ClaimCard, ClaimDetail } from '@web-of-trust/ui';
import { CLAIM_DATA, CLAIM_DATA_WITH_IMAGE, COMMENTS } from '../constants';
const CredentialView: React.FC = () => {
const [claimDetail, setClaimDetail] = useState<ClaimDetail | null>(CLAIM_DATA);
const [comments, setComments] = useState<ClaimDetail[] | undefined>(COMMENTS); // Use COMMENTS
return (
<>
<div>with comments</div>
<ClaimCard
claimDetail={claimDetail}
status="authenticated"
comments={comments}
/>
<div>with image</div>
<ClaimCard
claimDetail={CLAIM_DATA_WITH_IMAGE}
status="authenticated"
comments={comments}
/>
<div>without comments</div>
<ClaimCard
claimDetail={claimDetail}
status="authenticated"
/>
</>
);
};
export default CredentialView;
`
};
async function createProjectStructure(projectPath) {
const directories = [
'',
'src',
'src/components',
'src/lib',
'public'
];
for (const dir of directories) {
await fs.mkdir(path.join(projectPath, dir), { recursive: true });
}
}
async function createFiles(projectPath, projectName) {
const files = {
'package.json': JSON.stringify(templates.packageJson(projectName), null, 2),
'tsconfig.json': JSON.stringify(templates.tsconfig, null, 2),
'webpack.config.js': templates.webpackConfig,
'public/index.html': templates.indexHtml,
'src/index.tsx': templates.indexTsx,
'src/App.tsx': templates.appTsx,
'src/lib/theme.ts': templates.themeTsx,
'src/constants.ts': templates.constantsTs,
'src/components/CredentialView.tsx': templates.credentialViewTsx
};
for (const [filePath, content] of Object.entries(files)) {
await fs.writeFile(path.join(projectPath, filePath), content);
}
}
async function installDependencies(projectPath) {
console.log('Installing dependencies... This might take a few minutes.');
execSync('npm install', { cwd: projectPath, stdio: 'inherit' });
}
async function main() {
rl.question('Project name: ', async (projectName) => {
try {
const projectPath = path.join(process.cwd(), projectName);
console.log(`Creating new Web of Trust app in ${projectPath}`);
await createProjectStructure(projectPath);
await createFiles(projectPath, projectName);
await installDependencies(projectPath);
console.log(`
Success! Created ${projectName} at ${projectPath}
Inside that directory, you can run several commands:
npm run dev
Starts the development server.
npm run build
Bundles the app into static files for production.
npm test
Runs the test suite.
We suggest that you begin by typing:
cd ${projectName}
npm run dev
Happy hacking!`);
} catch (error) {
console.error('Error creating project:', error);
} finally {
rl.close();
}
});
}
main().catch(console.error);