create-tinybase 0.1.0 ā 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +2 -358
- package/package.json +12 -5
- package/templates/base/.prettierrc.hbs +5 -0
- package/templates/base/README.md.hbs +15 -0
- package/templates/base/eslint.config.js.hbs +38 -0
- package/templates/base/index.html.hbs +17 -0
- package/templates/base/package.json.hbs +60 -0
- package/templates/base/tsconfig.json.hbs +23 -0
- package/templates/base/tsconfig.node.json.hbs +9 -0
- package/templates/base/vite.config.js.hbs +10 -0
- package/templates/src/App.tsx.hbs +24 -0
- package/templates/src/index.css.hbs +110 -0
- package/templates/src/index.tsx.hbs +48 -0
- package/README.md +0 -17
- package/cspell.json +0 -8
package/cli.js
CHANGED
|
@@ -1,359 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import { mkdir, writeFile } from 'fs/promises';
|
|
5
|
-
import { join } from 'path';
|
|
6
|
-
|
|
7
|
-
console.log('š Welcome to TinyBase!\n');
|
|
8
|
-
|
|
9
|
-
const questions = [
|
|
10
|
-
{
|
|
11
|
-
type: 'text',
|
|
12
|
-
name: 'projectName',
|
|
13
|
-
message: 'Project name:',
|
|
14
|
-
initial: 'my-tinybase-app',
|
|
15
|
-
validate: (value) => value.length > 0 ? true : 'Project name is required'
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
type: 'select',
|
|
19
|
-
name: 'language',
|
|
20
|
-
message: 'Language:',
|
|
21
|
-
choices: [
|
|
22
|
-
{ title: 'TypeScript', value: 'typescript' },
|
|
23
|
-
{ title: 'JavaScript', value: 'javascript' }
|
|
24
|
-
],
|
|
25
|
-
initial: 0
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
type: 'select',
|
|
29
|
-
name: 'framework',
|
|
30
|
-
message: 'Framework:',
|
|
31
|
-
choices: [
|
|
32
|
-
{ title: 'React', value: 'react' },
|
|
33
|
-
{ title: 'Vanilla', value: 'vanilla' }
|
|
34
|
-
],
|
|
35
|
-
initial: 0
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
type: 'select',
|
|
39
|
-
name: 'persister',
|
|
40
|
-
message: 'Persistence:',
|
|
41
|
-
choices: [
|
|
42
|
-
{ title: 'None (in-memory only)', value: 'none' },
|
|
43
|
-
{ title: 'Local Storage', value: 'local-storage' },
|
|
44
|
-
{ title: 'SQLite', value: 'sqlite' },
|
|
45
|
-
{ title: 'PostgreSQL', value: 'postgres' }
|
|
46
|
-
],
|
|
47
|
-
initial: 0
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
type: 'confirm',
|
|
51
|
-
name: 'sync',
|
|
52
|
-
message: 'Include sync capabilities?',
|
|
53
|
-
initial: false
|
|
54
|
-
}
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
async function main() {
|
|
58
|
-
const answers = await prompts(questions, {
|
|
59
|
-
onCancel: () => {
|
|
60
|
-
console.log('\nā Cancelled');
|
|
61
|
-
process.exit(0);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
console.log('\nš¦ Creating your TinyBase app...\n');
|
|
66
|
-
|
|
67
|
-
const projectPath = join(process.cwd(), answers.projectName);
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
await createProject(projectPath, answers);
|
|
71
|
-
console.log('ā
Done!\n');
|
|
72
|
-
console.log(`Next steps:`);
|
|
73
|
-
console.log(` cd ${answers.projectName}`);
|
|
74
|
-
console.log(` npm install`);
|
|
75
|
-
console.log(` npm run dev`);
|
|
76
|
-
} catch (error) {
|
|
77
|
-
console.error('ā Error creating project:', error.message);
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function createProject(projectPath, config) {
|
|
83
|
-
await mkdir(projectPath, { recursive: true });
|
|
84
|
-
await mkdir(join(projectPath, 'src'), { recursive: true });
|
|
85
|
-
await mkdir(join(projectPath, 'public'), { recursive: true });
|
|
86
|
-
|
|
87
|
-
// Generate files based on config
|
|
88
|
-
await generatePackageJson(projectPath, config);
|
|
89
|
-
await generateIndexHtml(projectPath, config);
|
|
90
|
-
await generateViteConfig(projectPath, config);
|
|
91
|
-
await generateTsConfig(projectPath, config);
|
|
92
|
-
await generateSourceFiles(projectPath, config);
|
|
93
|
-
await generateReadme(projectPath, config);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function generatePackageJson(projectPath, config) {
|
|
97
|
-
const isTS = config.language === 'typescript';
|
|
98
|
-
const isReact = config.framework === 'react';
|
|
99
|
-
|
|
100
|
-
const pkg = {
|
|
101
|
-
name: config.projectName,
|
|
102
|
-
version: '0.1.0',
|
|
103
|
-
type: 'module',
|
|
104
|
-
scripts: {
|
|
105
|
-
dev: 'vite',
|
|
106
|
-
build: isTS ? 'tsc && vite build' : 'vite build',
|
|
107
|
-
preview: 'vite preview'
|
|
108
|
-
},
|
|
109
|
-
dependencies: {
|
|
110
|
-
tinybase: '^5.4.3'
|
|
111
|
-
},
|
|
112
|
-
devDependencies: {
|
|
113
|
-
vite: '^5.4.11'
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
if (isReact) {
|
|
118
|
-
pkg.dependencies.react = '^18.3.1';
|
|
119
|
-
pkg.dependencies['react-dom'] = '^18.3.1';
|
|
120
|
-
pkg.devDependencies['@vitejs/plugin-react'] = '^4.3.4';
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (isTS) {
|
|
124
|
-
pkg.devDependencies.typescript = '^5.6.3';
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (config.persister === 'sqlite') {
|
|
128
|
-
pkg.dependencies['tinybase-sqlite-wasm'] = '^1.0.0';
|
|
129
|
-
} else if (config.persister === 'postgres') {
|
|
130
|
-
pkg.dependencies['pg'] = '^8.13.1';
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (config.sync) {
|
|
134
|
-
pkg.dependencies['tinybase-ws-server'] = '^1.0.0';
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
await writeFile(
|
|
138
|
-
join(projectPath, 'package.json'),
|
|
139
|
-
JSON.stringify(pkg, null, 2)
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async function generateIndexHtml(projectPath, config) {
|
|
144
|
-
const isTS = config.language === 'typescript';
|
|
145
|
-
const ext = isTS ? 'tsx' : 'jsx';
|
|
146
|
-
|
|
147
|
-
const html = `<!DOCTYPE html>
|
|
148
|
-
<html lang="en">
|
|
149
|
-
<head>
|
|
150
|
-
<meta charset="UTF-8" />
|
|
151
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
152
|
-
<title>${config.projectName}</title>
|
|
153
|
-
</head>
|
|
154
|
-
<body>
|
|
155
|
-
<div id="app"></div>
|
|
156
|
-
<script type="module" src="/src/index.${ext}"></script>
|
|
157
|
-
</body>
|
|
158
|
-
</html>
|
|
159
|
-
`;
|
|
160
|
-
|
|
161
|
-
await writeFile(join(projectPath, 'index.html'), html);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
async function generateViteConfig(projectPath, config) {
|
|
165
|
-
const isReact = config.framework === 'react';
|
|
166
|
-
const isTS = config.language === 'typescript';
|
|
167
|
-
const ext = isTS ? 'ts' : 'js';
|
|
168
|
-
|
|
169
|
-
let content = `import { defineConfig } from 'vite';\n`;
|
|
170
|
-
|
|
171
|
-
if (isReact) {
|
|
172
|
-
content += `import react from '@vitejs/plugin-react';\n`;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
content += `\nexport default defineConfig({\n`;
|
|
176
|
-
content += ` plugins: [${isReact ? 'react()' : ''}],\n`;
|
|
177
|
-
content += `});\n`;
|
|
178
|
-
|
|
179
|
-
await writeFile(join(projectPath, `vite.config.${ext}`), content);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async function generateTsConfig(projectPath, config) {
|
|
183
|
-
if (config.language !== 'typescript') return;
|
|
184
|
-
|
|
185
|
-
const tsconfig = {
|
|
186
|
-
compilerOptions: {
|
|
187
|
-
target: 'ES2020',
|
|
188
|
-
useDefineForClassFields: true,
|
|
189
|
-
lib: ['ES2020', 'DOM', 'DOM.Iterable'],
|
|
190
|
-
module: 'ESNext',
|
|
191
|
-
skipLibCheck: true,
|
|
192
|
-
moduleResolution: 'bundler',
|
|
193
|
-
allowImportingTsExtensions: true,
|
|
194
|
-
resolveJsonModule: true,
|
|
195
|
-
isolatedModules: true,
|
|
196
|
-
noEmit: true,
|
|
197
|
-
jsx: config.framework === 'react' ? 'react-jsx' : undefined,
|
|
198
|
-
strict: true,
|
|
199
|
-
noUnusedLocals: true,
|
|
200
|
-
noUnusedParameters: true,
|
|
201
|
-
noFallthroughCasesInSwitch: true
|
|
202
|
-
},
|
|
203
|
-
include: ['src']
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
await writeFile(
|
|
207
|
-
join(projectPath, 'tsconfig.json'),
|
|
208
|
-
JSON.stringify(tsconfig, null, 2)
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async function generateSourceFiles(projectPath, config) {
|
|
213
|
-
const isTS = config.language === 'typescript';
|
|
214
|
-
const isReact = config.framework === 'react';
|
|
215
|
-
const ext = isTS ? (isReact ? 'tsx' : 'ts') : (isReact ? 'jsx' : 'js');
|
|
216
|
-
|
|
217
|
-
if (isReact) {
|
|
218
|
-
await generateReactApp(projectPath, config, ext);
|
|
219
|
-
} else {
|
|
220
|
-
await generateVanillaApp(projectPath, config, ext);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
async function generateReactApp(projectPath, config, ext) {
|
|
225
|
-
const isTS = config.language === 'typescript';
|
|
226
|
-
|
|
227
|
-
// index file
|
|
228
|
-
const indexContent = `import React from 'react';
|
|
229
|
-
import ReactDOM from 'react-dom/client';
|
|
230
|
-
import App from './App';
|
|
231
|
-
import './index.css';
|
|
232
|
-
|
|
233
|
-
ReactDOM.createRoot(document.getElementById('app')${isTS ? '!' : ''}).render(
|
|
234
|
-
<React.StrictMode>
|
|
235
|
-
<App />
|
|
236
|
-
</React.StrictMode>
|
|
237
|
-
);
|
|
238
|
-
`;
|
|
239
|
-
|
|
240
|
-
await writeFile(join(projectPath, 'src', `index.${ext}`), indexContent);
|
|
241
|
-
|
|
242
|
-
// App component
|
|
243
|
-
const appContent = `import { createStore } from 'tinybase';
|
|
244
|
-
import { useCreateStore, useCell } from 'tinybase/ui-react';
|
|
245
|
-
|
|
246
|
-
function App() {
|
|
247
|
-
const store = useCreateStore(() => createStore().setCell('data', 'row1', 'count', 0));
|
|
248
|
-
|
|
249
|
-
return (
|
|
250
|
-
<div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
|
|
251
|
-
<h1>š TinyBase App</h1>
|
|
252
|
-
<Counter store={store} />
|
|
253
|
-
</div>
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function Counter({ store }${isTS ? ': { store: any }' : ''}) {
|
|
258
|
-
const count = useCell('data', 'row1', 'count', store) ${isTS ? 'as number' : ''};
|
|
259
|
-
|
|
260
|
-
return (
|
|
261
|
-
<div>
|
|
262
|
-
<p>Count: {count}</p>
|
|
263
|
-
<button onClick={() => store.setCell('data', 'row1', 'count', count + 1)}>
|
|
264
|
-
Increment
|
|
265
|
-
</button>
|
|
266
|
-
</div>
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export default App;
|
|
271
|
-
`;
|
|
272
|
-
|
|
273
|
-
await writeFile(join(projectPath, 'src', `App.${ext}`), appContent);
|
|
274
|
-
|
|
275
|
-
// CSS
|
|
276
|
-
const cssContent = `body {
|
|
277
|
-
margin: 0;
|
|
278
|
-
padding: 0;
|
|
279
|
-
font-family: system-ui, -apple-system, sans-serif;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
button {
|
|
283
|
-
padding: 0.5rem 1rem;
|
|
284
|
-
font-size: 1rem;
|
|
285
|
-
cursor: pointer;
|
|
286
|
-
background: #007bff;
|
|
287
|
-
color: white;
|
|
288
|
-
border: none;
|
|
289
|
-
border-radius: 4px;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
button:hover {
|
|
293
|
-
background: #0056b3;
|
|
294
|
-
}
|
|
295
|
-
`;
|
|
296
|
-
|
|
297
|
-
await writeFile(join(projectPath, 'src', 'index.css'), cssContent);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
async function generateVanillaApp(projectPath, config, ext) {
|
|
301
|
-
const content = `import { createStore } from 'tinybase';
|
|
302
|
-
|
|
303
|
-
const store = createStore();
|
|
304
|
-
store.setCell('data', 'row1', 'count', 0);
|
|
305
|
-
|
|
306
|
-
const app = document.getElementById('app');
|
|
307
|
-
app.innerHTML = \`
|
|
308
|
-
<div style="padding: 2rem; font-family: system-ui">
|
|
309
|
-
<h1>š TinyBase App</h1>
|
|
310
|
-
<p>Count: <span id="count">0</span></p>
|
|
311
|
-
<button id="increment">Increment</button>
|
|
312
|
-
</div>
|
|
313
|
-
\`;
|
|
314
|
-
|
|
315
|
-
const countEl = document.getElementById('count');
|
|
316
|
-
const button = document.getElementById('increment');
|
|
317
|
-
|
|
318
|
-
store.addCellListener('data', 'row1', 'count', () => {
|
|
319
|
-
countEl.textContent = store.getCell('data', 'row1', 'count');
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
button.addEventListener('click', () => {
|
|
323
|
-
const current = store.getCell('data', 'row1', 'count');
|
|
324
|
-
store.setCell('data', 'row1', 'count', current + 1);
|
|
325
|
-
});
|
|
326
|
-
`;
|
|
327
|
-
|
|
328
|
-
await writeFile(join(projectPath, 'src', `index.${ext}`), content);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
async function generateReadme(projectPath, config) {
|
|
332
|
-
const readme = `# ${config.projectName}
|
|
333
|
-
|
|
334
|
-
A TinyBase application scaffolded with \`tinybase-new\`.
|
|
335
|
-
|
|
336
|
-
## Configuration
|
|
337
|
-
|
|
338
|
-
- Language: ${config.language}
|
|
339
|
-
- Framework: ${config.framework}
|
|
340
|
-
- Persistence: ${config.persister}
|
|
341
|
-
- Sync: ${config.sync ? 'Yes' : 'No'}
|
|
342
|
-
|
|
343
|
-
## Getting Started
|
|
344
|
-
|
|
345
|
-
\`\`\`bash
|
|
346
|
-
npm install
|
|
347
|
-
npm run dev
|
|
348
|
-
\`\`\`
|
|
349
|
-
|
|
350
|
-
## Learn More
|
|
351
|
-
|
|
352
|
-
- [TinyBase Documentation](https://tinybase.org/)
|
|
353
|
-
- [TinyBase GitHub](https://github.com/tinyplex/tinybase)
|
|
354
|
-
`;
|
|
355
|
-
|
|
356
|
-
await writeFile(join(projectPath, 'README.md'), readme);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
main();
|
|
2
|
+
import{dirname as c,join as p}from"path";import{createCLI as l}from"tinycreate";import{fileURLToPath as u}from"url";const m=c(u(import.meta.url)),g={welcomeMessage:`\u{1F389} Welcome to TinyBase!
|
|
3
|
+
`,questions:[{type:"text",name:"projectName",message:"Project name:",initial:"my-tinybase-app",validate:e=>e.length>0?!0:"Project name is required"},{type:"select",name:"language",message:"Language:",choices:[{title:"TypeScript",value:"typescript"},{title:"JavaScript",value:"javascript"}],initial:0},{type:"select",name:"framework",message:"Framework:",choices:[{title:"React",value:"react"},{title:"Vanilla",value:"vanilla"}],initial:0},{type:"confirm",name:"prettier",message:"Include Prettier?",initial:!1},{type:"confirm",name:"eslint",message:"Include ESLint?",initial:!1}],createContext:e=>{const{projectName:r,language:s,framework:i,prettier:n,eslint:a}=e,t=s==="typescript",o=i==="react";return{projectName:r,language:s,framework:i,prettier:n,eslint:a,typescript:t,react:o,ext:t?o?"tsx":"ts":o?"jsx":"js"}},createDirectories:async e=>{const{mkdir:r}=await import("fs/promises"),{join:s}=await import("path");await r(s(e,"src"),{recursive:!0}),await r(s(e,"public"),{recursive:!0})},getFiles:e=>{const{typescript:r,react:s,ext:i,prettier:n,eslint:a}=e,t=[{template:"base/package.json.hbs",output:"package.json",prettier:!0},{template:"base/index.html.hbs",output:"index.html",prettier:!0},{template:"base/README.md.hbs",output:"README.md",prettier:!0},{template:"src/index.css.hbs",output:"src/index.css",prettier:!0},{template:"src/index.tsx.hbs",output:`src/index.${i}`,prettier:!0,transpile:!0}];return n&&t.push({template:"base/.prettierrc.hbs",output:".prettierrc",prettier:!0}),a&&t.push({template:"base/eslint.config.js.hbs",output:"eslint.config.js",prettier:!0}),s&&t.push({template:"src/App.tsx.hbs",output:`src/App.${i}`,prettier:!0,transpile:!0},{template:"base/vite.config.js.hbs",output:"vite.config.js",prettier:!0}),r&&t.push({template:"base/tsconfig.json.hbs",output:"tsconfig.json",prettier:!0},{template:"base/tsconfig.node.json.hbs",output:"tsconfig.node.json",prettier:!0}),t},templateRoot:p(m,"templates"),onSuccess:e=>{console.log("Next steps:"),console.log(` cd ${e}`),console.log(" npm install"),console.log(" npm run dev")}};l(g).catch(e=>{console.error(e),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-tinybase",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"author": "jamesgpearce",
|
|
5
|
-
"repository":
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/tinyplex/tinybase.git"
|
|
8
|
+
},
|
|
6
9
|
"license": "MIT",
|
|
7
10
|
"homepage": "https://tinybase.org",
|
|
8
11
|
"description": "The CLI to build a new app using TinyBase, a reactive data store and sync engine.",
|
|
@@ -17,12 +20,16 @@
|
|
|
17
20
|
],
|
|
18
21
|
"type": "module",
|
|
19
22
|
"bin": {
|
|
20
|
-
"create-tinybase": "
|
|
23
|
+
"create-tinybase": "cli.js"
|
|
21
24
|
},
|
|
22
25
|
"dependencies": {
|
|
23
|
-
"
|
|
26
|
+
"tinycreate": "file:../tinycreate"
|
|
24
27
|
},
|
|
25
28
|
"engines": {
|
|
26
29
|
"node": ">=18.0.0"
|
|
27
|
-
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"cli.js",
|
|
33
|
+
"templates"
|
|
34
|
+
]
|
|
28
35
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
A TinyBase app built with {{#if typescript}}TypeScript{{else}}JavaScript{{/if}} and {{#if react}}React{{else}}Vanilla JS{{/if}}.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npm run dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Learn More
|
|
13
|
+
|
|
14
|
+
- [TinyBase Documentation](https://tinybase.org)
|
|
15
|
+
- [TinyBase Examples](https://tinybase.org/demos/)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{{#if typescript}}
|
|
2
|
+
{{addImport "import tseslint from 'typescript-eslint';"}}
|
|
3
|
+
{{/if}}
|
|
4
|
+
{{#if react}}
|
|
5
|
+
{{addImport "import pluginReact from 'eslint-plugin-react';"}}
|
|
6
|
+
{{addImport "import pluginReactHooks from 'eslint-plugin-react-hooks';"}}
|
|
7
|
+
{{/if}}
|
|
8
|
+
|
|
9
|
+
export default [
|
|
10
|
+
{{#if typescript}}
|
|
11
|
+
...tseslint.configs.recommended,
|
|
12
|
+
{{else}}
|
|
13
|
+
{
|
|
14
|
+
rules: {
|
|
15
|
+
'no-unused-vars': 'warn',
|
|
16
|
+
'no-undef': 'error',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
{{/if}}
|
|
20
|
+
|
|
21
|
+
{{#if react}}
|
|
22
|
+
pluginReact.configs.flat.recommended,
|
|
23
|
+
{
|
|
24
|
+
plugins: {
|
|
25
|
+
'react-hooks': pluginReactHooks,
|
|
26
|
+
},
|
|
27
|
+
rules: {
|
|
28
|
+
...pluginReactHooks.configs.recommended.rules,
|
|
29
|
+
'react/react-in-jsx-scope': 'off',
|
|
30
|
+
},
|
|
31
|
+
settings: {
|
|
32
|
+
react: {
|
|
33
|
+
version: 'detect',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{{/if}}
|
|
38
|
+
];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>TinyBase</title>
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body>
|
|
11
|
+
{{#if react}}
|
|
12
|
+
<div id="app"></div>
|
|
13
|
+
{{/if}}
|
|
14
|
+
<script type="module" src="/src/index.{{ext}}"></script>
|
|
15
|
+
</body>
|
|
16
|
+
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
{{#list}}
|
|
6
|
+
"dev": "vite"
|
|
7
|
+
{{#if typescript}}
|
|
8
|
+
"build": "tsc && vite build"
|
|
9
|
+
{{else}}
|
|
10
|
+
"build": "vite build"
|
|
11
|
+
{{/if}}
|
|
12
|
+
"preview": "vite preview"
|
|
13
|
+
{{#if prettier}}
|
|
14
|
+
"format": "prettier --write ."
|
|
15
|
+
"format:check": "prettier --check ."
|
|
16
|
+
{{/if}}
|
|
17
|
+
{{#if eslint}}
|
|
18
|
+
"lint": "eslint ."
|
|
19
|
+
{{/if}}
|
|
20
|
+
{{/list}}
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
{{#list}}
|
|
24
|
+
"vite": "^7.1.3"
|
|
25
|
+
{{#if typescript}}
|
|
26
|
+
"typescript": "^5.9.3"
|
|
27
|
+
"@types/node": "^25.0.3"
|
|
28
|
+
{{#if react}}
|
|
29
|
+
"@types/react": "^18.3.18"
|
|
30
|
+
"@types/react-dom": "^18.3.5"
|
|
31
|
+
{{/if}}
|
|
32
|
+
{{/if}}
|
|
33
|
+
{{#if react}}
|
|
34
|
+
"@vitejs/plugin-react": "^4.3.4"
|
|
35
|
+
{{/if}}
|
|
36
|
+
{{#if prettier}}
|
|
37
|
+
"prettier": "^3.7.4"
|
|
38
|
+
{{/if}}
|
|
39
|
+
{{#if eslint}}
|
|
40
|
+
"eslint": "^9.39.2"
|
|
41
|
+
{{#if typescript}}
|
|
42
|
+
"typescript-eslint": "^8.51.0"
|
|
43
|
+
{{/if}}
|
|
44
|
+
{{#if react}}
|
|
45
|
+
"eslint-plugin-react": "^7.37.3"
|
|
46
|
+
"eslint-plugin-react-hooks": "^5.1.0"
|
|
47
|
+
{{/if}}
|
|
48
|
+
{{/if}}
|
|
49
|
+
{{/list}}
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
{{#list}}
|
|
53
|
+
"tinybase": "^6.6.0"
|
|
54
|
+
{{#if react}}
|
|
55
|
+
"react": "^18.3.1"
|
|
56
|
+
"react-dom": "^18.3.1"
|
|
57
|
+
{{/if}}
|
|
58
|
+
{{/list}}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"moduleDetection": "force",
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
{{#if react}}
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
{{/if}}
|
|
16
|
+
"strict": true,
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"noFallthroughCasesInSwitch": true,
|
|
20
|
+
"noUncheckedSideEffectImports": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["src"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{{addImport "import {createStore} from 'tinybase';"}}
|
|
2
|
+
{{addImport "import {Provider, useValue} from 'tinybase/ui-react';"}}
|
|
3
|
+
|
|
4
|
+
const store = createStore().setValue('count', 0);
|
|
5
|
+
|
|
6
|
+
export const App = () => (
|
|
7
|
+
<Provider store={store}>
|
|
8
|
+
<h1>TinyBase + React</h1>
|
|
9
|
+
<Counter />
|
|
10
|
+
</Provider>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const Counter = () => {
|
|
14
|
+
const count = useValue('count', store);
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div>
|
|
18
|
+
<p>Count: {count}</p>
|
|
19
|
+
<button onClick={() => store.setValue('count', (c: number) => c + 1)}>
|
|
20
|
+
Increment
|
|
21
|
+
</button>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
body {
|
|
2
|
+
color: #bbb;
|
|
3
|
+
background: #111;
|
|
4
|
+
font-family: 'Inter', sans-serif;
|
|
5
|
+
user-select: none;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
#app {
|
|
9
|
+
max-width: 40rem;
|
|
10
|
+
margin: 2rem auto;
|
|
11
|
+
display: grid;
|
|
12
|
+
grid-template-columns: 1fr;
|
|
13
|
+
column-gap: 2rem;
|
|
14
|
+
align-items: start;
|
|
15
|
+
row-gap: 1rem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
a {
|
|
19
|
+
color: #bbb;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
h1 {
|
|
23
|
+
font-weight: 600;
|
|
24
|
+
img {
|
|
25
|
+
margin-right: 1rem;
|
|
26
|
+
height: 2.5rem;
|
|
27
|
+
vertical-align: text-bottom;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
h2 {
|
|
32
|
+
text-align: center;
|
|
33
|
+
font-weight: 800;
|
|
34
|
+
font-size: 1.3rem;
|
|
35
|
+
margin: 1rem 0 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#buttons {
|
|
39
|
+
border: solid #666;
|
|
40
|
+
border-width: 1px 0;
|
|
41
|
+
text-align: center;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#buttons button {
|
|
45
|
+
border: 1px solid #666;
|
|
46
|
+
border-radius: 0.25rem;
|
|
47
|
+
padding: 0.5rem 1rem;
|
|
48
|
+
background: #222;
|
|
49
|
+
color: #fff;
|
|
50
|
+
font-family: inherit;
|
|
51
|
+
font-weight: 800;
|
|
52
|
+
font-size: 0.8rem;
|
|
53
|
+
width: 10rem;
|
|
54
|
+
margin: 1rem;
|
|
55
|
+
}
|
|
56
|
+
#buttons button:hover {
|
|
57
|
+
border-color: #d81b60;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
table {
|
|
61
|
+
border-collapse: collapse;
|
|
62
|
+
font-size: inherit;
|
|
63
|
+
line-height: inherit;
|
|
64
|
+
margin-top: 0.5rem;
|
|
65
|
+
table-layout: fixed;
|
|
66
|
+
width: 100%;
|
|
67
|
+
margin-bottom: 2rem;
|
|
68
|
+
caption-side: bottom;
|
|
69
|
+
}
|
|
70
|
+
table caption {
|
|
71
|
+
text-align: left;
|
|
72
|
+
button {
|
|
73
|
+
border: 0;
|
|
74
|
+
margin-right: 0.25rem;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
table caption button {
|
|
78
|
+
line-height: 0.7rem;
|
|
79
|
+
margin: 0 0.25rem 0 0;
|
|
80
|
+
vertical-align: middle;
|
|
81
|
+
}
|
|
82
|
+
th,
|
|
83
|
+
td {
|
|
84
|
+
padding: 0.25rem 0.5rem 0.25rem 0;
|
|
85
|
+
text-align: left;
|
|
86
|
+
}
|
|
87
|
+
thead th,
|
|
88
|
+
thead td {
|
|
89
|
+
border: solid #999;
|
|
90
|
+
border-width: 1px 0;
|
|
91
|
+
}
|
|
92
|
+
tbody th,
|
|
93
|
+
tbody td {
|
|
94
|
+
border-bottom: 1px solid #333;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
table.sortedTable thead th {
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@media (min-width: 40rem) {
|
|
102
|
+
#app {
|
|
103
|
+
grid-template-columns: 1fr 1fr;
|
|
104
|
+
}
|
|
105
|
+
header,
|
|
106
|
+
p,
|
|
107
|
+
#buttons {
|
|
108
|
+
grid-column: span 2;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{{addImport "import './index.css';"}}
|
|
2
|
+
|
|
3
|
+
{{#if react}}
|
|
4
|
+
{{addImport "import ReactDOM from 'react-dom/client';"}}
|
|
5
|
+
{{addImport "import {App} from './App';"}}
|
|
6
|
+
|
|
7
|
+
addEventListener('load', () =>
|
|
8
|
+
ReactDOM.createRoot(document.getElementById('app')!).render(<App />),
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
{{else}}
|
|
12
|
+
{{addImport "import {createStore} from 'tinybase';"}}
|
|
13
|
+
|
|
14
|
+
const onClick = (id: string, onClick: () => void) =>
|
|
15
|
+
document.getElementById(id)!.addEventListener('click', onClick);
|
|
16
|
+
|
|
17
|
+
const updateJson = (id: string, content: unknown) =>
|
|
18
|
+
(document.getElementById(id)!.innerText = JSON.stringify(content, null, 2));
|
|
19
|
+
|
|
20
|
+
const getRandom = (max = 100) => Math.floor(Math.random() * max);
|
|
21
|
+
|
|
22
|
+
addEventListener('load', () => {
|
|
23
|
+
const store = createStore();
|
|
24
|
+
|
|
25
|
+
onClick('countButton', () => store.setValue('counter', (value) => value + 1));
|
|
26
|
+
onClick('randomButton', () => store.setValue('random', getRandom()));
|
|
27
|
+
onClick('addPetButton', () =>
|
|
28
|
+
store.addRow('pets', {
|
|
29
|
+
name: ['fido', 'felix', 'bubbles', 'lowly', 'polly'][getRandom(5)],
|
|
30
|
+
species: store.getRowIds('species')[getRandom(5)],
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
store.addValuesListener(() => updateJson('valuesJson', store.getValues()));
|
|
35
|
+
store.addTablesListener(() => updateJson('tablesJson', store.getTables()));
|
|
36
|
+
|
|
37
|
+
store
|
|
38
|
+
.setValue('counter', 0)
|
|
39
|
+
.setRow('pets', '0', {name: 'fido', species: 'dog'})
|
|
40
|
+
.setTable('species', {
|
|
41
|
+
dog: {price: 5},
|
|
42
|
+
cat: {price: 4},
|
|
43
|
+
fish: {price: 2},
|
|
44
|
+
worm: {price: 1},
|
|
45
|
+
parrot: {price: 3},
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
{{/if}}
|
package/README.md
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# create-tinybase
|
|
2
|
-
|
|
3
|
-
CLI tool to scaffold a new TinyBase application.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm init tinybase
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
This will prompt you with questions to configure your new TinyBase app:
|
|
12
|
-
|
|
13
|
-
- Project name
|
|
14
|
-
- Language (TypeScript or JavaScript)
|
|
15
|
-
- Framework (React or Vanilla)
|
|
16
|
-
- Persistence option (None, Local Storage, SQLite, PostgreSQL)
|
|
17
|
-
- Sync capabilities
|