@webjskit/cli 0.4.1 → 0.4.4
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/lib/create.js +33 -10
- package/lib/saas-template.js +48 -0
- package/package.json +2 -2
- package/templates/.cursorrules +1 -1
- package/templates/.github/copilot-instructions.md +2 -2
- package/templates/.windsurfrules +3 -2
- package/templates/AGENTS.md +30 -3
package/lib/create.js
CHANGED
|
@@ -87,9 +87,9 @@ export async function scaffoldApp(name, cwd, opts = {}) {
|
|
|
87
87
|
'@web/test-runner': '^0.20.0',
|
|
88
88
|
'@web/test-runner-playwright': '^0.11.0',
|
|
89
89
|
'playwright': '^1.59.0',
|
|
90
|
-
// tsserver
|
|
91
|
-
//
|
|
92
|
-
|
|
90
|
+
// tsserver plugin for editor intelligence inside html`` templates.
|
|
91
|
+
// @webjskit/ts-plugin bundles ts-lit-plugin internally, so just one
|
|
92
|
+
// plugin entry is needed in tsconfig (see below).
|
|
93
93
|
'@webjskit/ts-plugin': 'latest',
|
|
94
94
|
},
|
|
95
95
|
}, null, 2) + '\n');
|
|
@@ -104,14 +104,16 @@ export async function scaffoldApp(name, cwd, opts = {}) {
|
|
|
104
104
|
noEmit: true,
|
|
105
105
|
allowImportingTsExtensions: true,
|
|
106
106
|
skipLibCheck: true,
|
|
107
|
-
// ts-
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
107
|
+
// @webjskit/ts-plugin gives the editor:
|
|
108
|
+
// • type-check + diagnostics inside html`` templates (via the
|
|
109
|
+
// ts-lit-plugin it bundles internally)
|
|
110
|
+
// • webjs-aware go-to-definition on custom-element tags
|
|
111
|
+
// • "Unknown tag/attribute" suppression for elements registered
|
|
112
|
+
// via Class.register('tag-name')
|
|
113
|
+
// • attribute auto-complete sourced from `static properties`
|
|
114
|
+
// • attribute-value type-check against `declare` annotations
|
|
115
|
+
// Editor-only — the framework runs without it.
|
|
113
116
|
plugins: [
|
|
114
|
-
{ name: 'ts-lit-plugin', strict: true },
|
|
115
117
|
{ name: '@webjskit/ts-plugin' },
|
|
116
118
|
],
|
|
117
119
|
},
|
|
@@ -266,6 +268,27 @@ export async function POST(req: Request) {
|
|
|
266
268
|
const body = await req.json();
|
|
267
269
|
return Response.json(await createUser(body));
|
|
268
270
|
}
|
|
271
|
+
`);
|
|
272
|
+
// Minimal test stub so the scaffold passes `webjs check` (tests-exist)
|
|
273
|
+
// and `webjs test` runs cleanly. Replace these with real assertions
|
|
274
|
+
// once you wire the action/query to a real data source.
|
|
275
|
+
await writeFile(join(appDir, 'test', 'unit', 'users.test.ts'), `import { test } from 'node:test';
|
|
276
|
+
import assert from 'node:assert/strict';
|
|
277
|
+
|
|
278
|
+
import { listUsers } from '../../modules/users/queries/list-users.server.ts';
|
|
279
|
+
import { createUser } from '../../modules/users/actions/create-user.server.ts';
|
|
280
|
+
|
|
281
|
+
test('listUsers returns an array', async () => {
|
|
282
|
+
const users = await listUsers();
|
|
283
|
+
assert.ok(Array.isArray(users));
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test('createUser returns a success envelope with the input echoed back', async () => {
|
|
287
|
+
const result = await createUser({ name: 'Test', email: 'test@example.com' });
|
|
288
|
+
assert.equal(result.success, true);
|
|
289
|
+
assert.equal(result.data.name, 'Test');
|
|
290
|
+
assert.equal(result.data.email, 'test@example.com');
|
|
291
|
+
});
|
|
269
292
|
`);
|
|
270
293
|
await writeFile(join(appDir, 'modules', 'users', 'types.ts'), `export interface User {
|
|
271
294
|
id: string;
|
package/lib/saas-template.js
CHANGED
|
@@ -133,6 +133,54 @@ export async function writeSaasFiles(appDir) {
|
|
|
133
133
|
"",
|
|
134
134
|
].join('\n'));
|
|
135
135
|
|
|
136
|
+
// test/unit/auth.test.ts — minimal stub so the scaffold passes
|
|
137
|
+
// `webjs check` (tests-exist) and `webjs test` runs cleanly out of the
|
|
138
|
+
// box. The signup/current-user functions import from lib/prisma.ts and
|
|
139
|
+
// lib/auth.ts, both of which need `prisma generate` to have run before
|
|
140
|
+
// they can be imported — so we deliberately test only the runtime-
|
|
141
|
+
// dependency-free types.ts here. Replace with real tests once Prisma
|
|
142
|
+
// is set up (run `npm install && npx prisma migrate dev --name init`).
|
|
143
|
+
await writeFile(join(appDir, 'test', 'unit', 'auth.test.ts'), [
|
|
144
|
+
"import { test } from 'node:test';",
|
|
145
|
+
"import assert from 'node:assert/strict';",
|
|
146
|
+
"",
|
|
147
|
+
"import type { User, ActionResult } from '../../modules/auth/types.ts';",
|
|
148
|
+
"",
|
|
149
|
+
"test('User shape: id is numeric, email is required', () => {",
|
|
150
|
+
" const u: User = { id: 1, name: 'Test', email: 'test@example.com' };",
|
|
151
|
+
" assert.equal(typeof u.id, 'number');",
|
|
152
|
+
" assert.equal(typeof u.email, 'string');",
|
|
153
|
+
"});",
|
|
154
|
+
"",
|
|
155
|
+
"test('ActionResult: success envelope carries data', () => {",
|
|
156
|
+
" const r: ActionResult<User> = {",
|
|
157
|
+
" success: true,",
|
|
158
|
+
" data: { id: 1, name: 'Test', email: 'test@example.com' },",
|
|
159
|
+
" };",
|
|
160
|
+
" assert.equal(r.success, true);",
|
|
161
|
+
" if (r.success) assert.equal(r.data.email, 'test@example.com');",
|
|
162
|
+
"});",
|
|
163
|
+
"",
|
|
164
|
+
"test('ActionResult: failure envelope carries error + status', () => {",
|
|
165
|
+
" const r: ActionResult<User> = {",
|
|
166
|
+
" success: false,",
|
|
167
|
+
" error: 'Email already registered',",
|
|
168
|
+
" status: 409,",
|
|
169
|
+
" };",
|
|
170
|
+
" assert.equal(r.success, false);",
|
|
171
|
+
" if (!r.success) {",
|
|
172
|
+
" assert.equal(r.status, 409);",
|
|
173
|
+
" assert.ok(r.error.length > 0);",
|
|
174
|
+
" }",
|
|
175
|
+
"});",
|
|
176
|
+
"",
|
|
177
|
+
"// TODO: once you've run `npm install && npx prisma migrate dev` you can",
|
|
178
|
+
"// import { signup } from '../../modules/auth/actions/signup.server.ts'",
|
|
179
|
+
"// and { currentUser } from '../../modules/auth/queries/current-user.server.ts'",
|
|
180
|
+
"// and write real integration tests against a test SQLite DB.",
|
|
181
|
+
"",
|
|
182
|
+
].join('\n'));
|
|
183
|
+
|
|
136
184
|
// app/api/auth/[...path]/route.ts
|
|
137
185
|
await mkdir(join(appDir, 'app', 'api', 'auth', '[...path]'), { recursive: true });
|
|
138
186
|
await writeFile(join(appDir, 'app', 'api', 'auth', '[...path]', 'route.ts'), [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webjskit/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "webjs CLI — dev, start, create, db",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@webjskit/server": "0.4.
|
|
16
|
+
"@webjskit/server": "0.4.2"
|
|
17
17
|
},
|
|
18
18
|
"publishConfig": {
|
|
19
19
|
"access": "public"
|
package/templates/.cursorrules
CHANGED
|
@@ -80,5 +80,5 @@ Quality bar stays the same — just no blocking on questions.
|
|
|
80
80
|
- One function per server action file (*.server.ts)
|
|
81
81
|
- Components must call customElements.define('tag', Class)
|
|
82
82
|
- Never import @prisma/client or node:* from client components
|
|
83
|
-
-
|
|
83
|
+
- Directives are deliberately minimal: only `unsafeHTML`, `live`, and `repeat` ship. Lit's `classMap` / `styleMap` / `ref` / `when` / `choose` / `guard` are NOT exported — use plain template-literal expressions (`class=${cond ? 'a' : 'b'}`, `${cond ? a : b}`) and lifecycle hooks (`this.query('#el')` in `firstUpdated`) instead.
|
|
84
84
|
- See AGENTS.md for the complete directive decision guide
|
|
@@ -64,9 +64,9 @@ Every code change must include:
|
|
|
64
64
|
## Code patterns
|
|
65
65
|
|
|
66
66
|
- Tagged template: html`<div>${value}</div>` with css`...` for styles
|
|
67
|
-
- Components: extend WebComponent,
|
|
67
|
+
- Components: extend WebComponent, declare `static properties` (and `static styles` for shadow-DOM components), call `Class.register('tag-name')` at the bottom of the file. The tag name is the argument to `.register()`, not a static field.
|
|
68
68
|
- Server actions: *.server.ts files with one exported async function each
|
|
69
|
-
- Directives:
|
|
69
|
+
- Directives: webjs ships only `unsafeHTML`, `live`, and `repeat`. Lit's `classMap` / `styleMap` / `ref` / `when` / `choose` / `guard` are NOT exported — use plain template-literal expressions and lifecycle hooks instead.
|
|
70
70
|
- Context: import { createContext, ContextProvider, ContextConsumer } from '@webjskit/core/context'
|
|
71
71
|
- Task: import { Task, TaskStatus } from '@webjskit/core/task'
|
|
72
72
|
- Routing: file-based under app/ (page.ts, layout.ts, route.ts, middleware.ts)
|
package/templates/.windsurfrules
CHANGED
|
@@ -67,8 +67,9 @@ The user should never have to ask for tests or documentation.
|
|
|
67
67
|
## Framework specifics
|
|
68
68
|
|
|
69
69
|
- No build step: ES modules served directly
|
|
70
|
-
- Web components
|
|
70
|
+
- Web components render into light DOM by default (so Tailwind / global CSS apply directly). Opt in to shadow DOM per component with `static shadow = true` when you need scoped styles, slot projection, or third-party-embed isolation.
|
|
71
|
+
- Custom-element tag names are passed to `.register('tag-name')` — they are NOT a static field on the class.
|
|
71
72
|
- One function per server action file (*.server.ts)
|
|
72
|
-
- Use
|
|
73
|
+
- Directives are deliberately minimal: only `unsafeHTML`, `live`, and `repeat` ship. Use plain template-literal expressions (`class=${active ? 'btn active' : 'btn'}`, `style=${'color:' + color}`, `${cond ? a : b}`) and lifecycle hooks (`this.query('#el')` in `firstUpdated`) instead of Lit's `classMap` / `styleMap` / `ref` / `when` / `choose` / `guard`.
|
|
73
74
|
- Use Context for cross-component data, Task for async data in components
|
|
74
75
|
- Full API reference in AGENTS.md
|
package/templates/AGENTS.md
CHANGED
|
@@ -84,6 +84,34 @@ node_modules/@webjskit/
|
|
|
84
84
|
Reaching straight for the source is the fastest way to resolve "why
|
|
85
85
|
doesn't X work?" — no documentation guesswork, no stale blog posts.
|
|
86
86
|
|
|
87
|
+
## Editor TS plugin — `@webjskit/ts-plugin`
|
|
88
|
+
|
|
89
|
+
This scaffold's `tsconfig.json` lists a single tsserver plugin. It is
|
|
90
|
+
editor-only — not required for the framework to run.
|
|
91
|
+
|
|
92
|
+
```jsonc
|
|
93
|
+
// tsconfig.json (already wired by the scaffold)
|
|
94
|
+
"plugins": [
|
|
95
|
+
{ "name": "@webjskit/ts-plugin" }
|
|
96
|
+
]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`@webjskit/ts-plugin` bundles `ts-lit-plugin` internally (it's a runtime
|
|
100
|
+
dependency of the plugin) and loads it programmatically — so users
|
|
101
|
+
list one entry, not two. You get the full stack of template-literal
|
|
102
|
+
intelligence (type-checking, diagnostics, go-to-def inside
|
|
103
|
+
`` html`…` `` and `` css`…` `` templates) **plus** webjs-aware behaviour
|
|
104
|
+
layered on top:
|
|
105
|
+
|
|
106
|
+
- "Unknown tag/attribute" diagnostics are silenced for elements
|
|
107
|
+
registered via `Class.register('tag-name')`.
|
|
108
|
+
- Attribute auto-complete sourced from each component's
|
|
109
|
+
`static properties`.
|
|
110
|
+
- Attribute-value type-check against `declare propName: T` annotations.
|
|
111
|
+
|
|
112
|
+
See [docs.webjs.com → Editor setup](https://docs.webjs.com/docs/editor-setup)
|
|
113
|
+
for the full walkthrough.
|
|
114
|
+
|
|
87
115
|
## File conventions
|
|
88
116
|
|
|
89
117
|
```
|
|
@@ -172,9 +200,8 @@ import { rateLimit, cache, createAuth, Credentials, Session } from '@webjskit/se
|
|
|
172
200
|
import { WebComponent, html, css } from '@webjskit/core';
|
|
173
201
|
|
|
174
202
|
export class Counter extends WebComponent {
|
|
175
|
-
static tag = 'my-counter'; // required, must contain a hyphen
|
|
176
203
|
static properties = { count: { type: Number } };
|
|
177
|
-
static styles = css`button { padding: 8px 12px; }`;
|
|
204
|
+
static styles = css`button { padding: 8px 12px; }`; // shadow-DOM only
|
|
178
205
|
// static shadow = true; // opt into shadow DOM (default: light DOM)
|
|
179
206
|
// static lazy = true; // download JS only when scrolled into view
|
|
180
207
|
|
|
@@ -223,7 +250,7 @@ absolute URLs from `ctx.url`).
|
|
|
223
250
|
|
|
224
251
|
## Invariants (do not violate)
|
|
225
252
|
|
|
226
|
-
1. Custom element tags must contain a hyphen.
|
|
253
|
+
1. Custom element tags must contain a hyphen. Pass the tag to `.register('tag-name')` at the bottom of the file. The tag is not a static field.
|
|
227
254
|
2. Never import `@prisma/client` or `node:*` from client-reachable files —
|
|
228
255
|
only from `.server.ts` modules or `lib/*.ts`.
|
|
229
256
|
3. Event / property / boolean holes in `` html`` `` are unquoted:
|