From a94aea110afa664a9f2a3d05408bea885d36c5bf Mon Sep 17 00:00:00 2001 From: nicwands Date: Tue, 24 Mar 2026 13:52:38 -0400 Subject: [PATCH] initial-commit --- .gitignore | 38 +++++++ .nvmrc | 1 + bin/create.js | 114 +++++++++++++++++++++ package-lock.json | 16 +++ package.json | 22 ++++ template/.gitignore | 38 +++++++ template/.nvmrc | 1 + template/README.md | 20 ++++ template/package.json | 27 +++++ template/rollup.config.js | 13 +++ template/src/${NAME_PASCAL_CASE}Adapter.js | 23 +++++ template/src/index.js | 14 +++ template/test/plugin.test.js | 8 ++ template/vitest.config.js | 8 ++ 14 files changed, 343 insertions(+) create mode 100644 .gitignore create mode 100644 .nvmrc create mode 100755 bin/create.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 template/.gitignore create mode 100644 template/.nvmrc create mode 100644 template/README.md create mode 100644 template/package.json create mode 100644 template/rollup.config.js create mode 100644 template/src/${NAME_PASCAL_CASE}Adapter.js create mode 100644 template/src/index.js create mode 100644 template/test/plugin.test.js create mode 100644 template/vitest.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88ffb0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +.env +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local +out + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +.eslintcache + +# Cypress +/cypress/videos/ +/cypress/screenshots/ + +# Vitest +__screenshots__/ diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/bin/create.js b/bin/create.js new file mode 100755 index 0000000..0d75802 --- /dev/null +++ b/bin/create.js @@ -0,0 +1,114 @@ +#!/usr/bin/env node + +import { + readFileSync, + writeFileSync, + mkdirSync, + readdirSync, + copyFileSync, + existsSync, +} from 'fs' +import { join, dirname } from 'path' +import { fileURLToPath } from 'url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const TEMPLATE_DIR = join(__dirname, '..', 'template') + +const toKebabCase = (str) => { + return str + .replace(/([a-z])([A-Z])/g, '$1-$2') + .replace(/[\s_]+/g, '-') + .toLowerCase() +} + +const toPascalCase = (str) => { + return str + .replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : '')) + .replace(/^./, (s) => s.toUpperCase()) +} + +const toTitleCase = (str) => { + return str + .replace(/[-_\s]+(.)?/g, (_, c) => (c ? ' ' + c.toUpperCase() : '')) + .replace(/^./, (s) => s.toUpperCase()) + .replace(/\b\w/g, (c) => c.toUpperCase()) +} + +const replaceValues = (content, values) => { + return content.replace(/\$\{(.*?)\}/g, (_, key) => { + return values[key] ?? `\${${key}}` + }) +} + +const copyDir = (src, dest, values) => { + mkdirSync(dest, { recursive: true }) + const entries = readdirSync(src, { withFileTypes: true }) + + for (const entry of entries) { + const srcPath = join(src, entry.name) + const destName = replaceValues(entry.name, values) + const destPath = join(dest, destName) + + if (entry.isDirectory()) { + copyDir(srcPath, destPath, values) + } else { + copyFileSync(srcPath, destPath) + const content = readFileSync(destPath, 'utf8') + writeFileSync(destPath, replaceValues(content, values)) + } + } +} + +const prompt = async (question) => { + const readline = await import('readline') + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }) + return new Promise((resolve) => { + rl.question(question, (answer) => { + rl.close() + resolve(answer) + }) + }) +} + +const main = async () => { + const name = await prompt('Plugin name: ') + const description = await prompt('Plugin description: ') + + const trimmedName = name?.trim() + + if (!trimmedName) { + console.error('Plugin name is required') + process.exit(1) + } + + const kebab = toKebabCase(trimmedName) + const pascal = toPascalCase(trimmedName) + const title = toTitleCase(trimmedName) + + const values = { + NAME_KEBAB_CASE: kebab, + NAME_PASCAL_CASE: pascal, + NAME_TITLE_CASE: title, + DESCRIPTION: description.trim(), + } + + const destDir = join(process.cwd(), `takerofnotes-plugin-${kebab}`) + + if (existsSync(destDir)) { + console.error(`Directory already exists: ${destDir}`) + process.exit(1) + } + + copyDir(TEMPLATE_DIR, destDir, values) + + console.log(`\nPlugin created: takerofnotes-plugin-${kebab}`) + console.log(`\nNext steps:`) + console.log(` cd takerofnotes-plugin-${kebab}`) + console.log(` npm install`) + console.log(` cat README.md`) +} + +main() diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6775b77 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,16 @@ +{ + "name": "create-takerofnotes-plugin", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "create-takerofnotes-plugin", + "version": "0.1.0", + "license": "MIT", + "bin": { + "create-takerofnotes-plugin": "bin/create.js" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..57d7b2b --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "create-takerofnotes-plugin", + "version": "0.1.0", + "description": "Scaffold a new Taker of Notes storage plugin", + "type": "module", + "bin": { + "create-takerofnotes-plugin": "./bin/create.js" + }, + "files": [ + "bin" + ], + "scripts": { + "test": "echo \"No tests yet\"" + }, + "keywords": [ + "takerofnotes", + "plugin", + "scaffold" + ], + "author": "nicwands", + "license": "MIT" +} diff --git a/template/.gitignore b/template/.gitignore new file mode 100644 index 0000000..2940187 --- /dev/null +++ b/template/.gitignore @@ -0,0 +1,38 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +.env +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local +.npmrc + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +.eslintcache + +# Cypress +/cypress/videos/ +/cypress/screenshots/ + +# Vitest +__screenshots__/ diff --git a/template/.nvmrc b/template/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/template/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/template/README.md b/template/README.md new file mode 100644 index 0000000..21c549d --- /dev/null +++ b/template/README.md @@ -0,0 +1,20 @@ +# Taker of Notes Plugin: ${NAME_TITLE_CASE} + +${DESCRIPTION} + +## Getting Started + +1. Install dependencies: `npm install` +2. Run tests: `npm test` +3. Build: `npm run build` + +## Project Structure + +- `src/` - Source code +- `test/` - Tests +- `dist/` - Built output +- `rollup.config.js` - Build configuration + +## Development + +Implement your storage adapter in `src/${NAME_PASCAL_CASE}.js`. diff --git a/template/package.json b/template/package.json new file mode 100644 index 0000000..569f70d --- /dev/null +++ b/template/package.json @@ -0,0 +1,27 @@ +{ + "name": "@takerofnotes/plugin-${NAME_KEBAB_CASE}", + "version": "0.1.0", + "repository": { + "type": "git" + }, + "type": "module", + "main": "dist/index.js", + "exports": "./dist/index.js", + "scripts": { + "build": "rollup -c", + "test": "vitest", + "prepublishOnly": "npm run build" + }, + "author": "nicwands", + "license": "MIT", + "description": "${DESCRIPTION}", + "dependencies": { + "@takerofnotes/plugin-sdk": "^0.2.0" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^29.0.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "rollup": "^4.59.0", + "vitest": "^4.0.18" + } +} diff --git a/template/rollup.config.js b/template/rollup.config.js new file mode 100644 index 0000000..d1a1015 --- /dev/null +++ b/template/rollup.config.js @@ -0,0 +1,13 @@ +// rollup.config.js +import resolve from '@rollup/plugin-node-resolve' +import commonjs from '@rollup/plugin-commonjs' + +export default { + input: 'src/index.js', + output: { + file: 'dist/index.js', + format: 'esm', + sourcemap: true, + }, + plugins: [resolve(), commonjs()], +} diff --git a/template/src/${NAME_PASCAL_CASE}Adapter.js b/template/src/${NAME_PASCAL_CASE}Adapter.js new file mode 100644 index 0000000..03f1690 --- /dev/null +++ b/template/src/${NAME_PASCAL_CASE}Adapter.js @@ -0,0 +1,23 @@ +import { BaseNotesAdapter } from '@takerofnotes/plugin-sdk' + +export default class ${NAME_PASCAL_CASE}Adapter extends BaseNotesAdapter { + constructor(config) { + super() + + for (const field in config) { + this[field] = config[field] + } + } + + async init() {} + + async getAll() {} + + async create(note) {} + + async update(note) {} + + async delete(id) {} + + async testConnection() {} +} diff --git a/template/src/index.js b/template/src/index.js new file mode 100644 index 0000000..21585af --- /dev/null +++ b/template/src/index.js @@ -0,0 +1,14 @@ +import { definePlugin } from '@takerofnotes/plugin-sdk' +import ${NAME_PASCAL_CASE}Adapter from './${NAME_PASCAL_CASE}Adapter' + +export default definePlugin({ + id: '${NAME_KEBAB_CASE}', + name: '${NAME_TITLE_CASE}', + description: '${DESCRIPTION}', + version: '0.1.0', + apiVersion: '0.3.1', + configSchema: [], + createAdapter(config) { + return new ${NAME_PASCAL_CASE}Adapter(config) + }, +}) diff --git a/template/test/plugin.test.js b/template/test/plugin.test.js new file mode 100644 index 0000000..267fc7a --- /dev/null +++ b/template/test/plugin.test.js @@ -0,0 +1,8 @@ +import { describePluginTests } from '@takerofnotes/plugin-sdk' +import plugin from '../src/index.js' + +const adapter = plugin.createAdapter({ + // Adapter config here +}) + +describePluginTests({ plugin, adapter }, describe, it, expect) diff --git a/template/vitest.config.js b/template/vitest.config.js new file mode 100644 index 0000000..ca4b6a6 --- /dev/null +++ b/template/vitest.config.js @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + globals: true, + testTimeout: 30000, + }, +})