face-up 0.0.0 → 0.0.1
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/.gitmodules +3 -0
- package/.kiro/steering/project-context.md +17 -0
- package/.vscode/settings.json +3 -0
- package/FaceUp.js +305 -0
- package/README.md +165 -2
- package/imports.html +8 -0
- package/package.json +30 -20
- package/tests/test1.html +130 -0
- package/types/.kiro/specs/conversion-template/README.md +128 -0
- package/types/.kiro/specs/conversion-template/design.md +360 -0
- package/types/.kiro/specs/conversion-template/requirements.md +191 -0
- package/types/.kiro/specs/conversion-template/tasks.md +174 -0
- package/types/.kiro/steering/coding-standards.md +17 -0
- package/types/.kiro/steering/conversion-guide.md +103 -0
- package/types/.kiro/steering/declarative-configuration.md +108 -0
- package/types/.kiro/steering/emc-json-serializability.md +306 -0
- package/types/EnhancementConversionInstructions.md +1626 -0
- package/types/LICENSE +21 -0
- package/types/NewCustomElementFeature.md +672 -0
- package/types/NewEnhancementInstructions.md +395 -0
- package/types/README.md +2 -0
- package/types/agrace/types.d.ts +11 -0
- package/types/assign-gingerly/types.d.ts +328 -0
- package/types/be-a-beacon/types.d.ts +17 -0
- package/types/be-bound/types.d.ts +61 -0
- package/types/be-buttoned-up/types.d.ts +19 -0
- package/types/be-clonable/types.d.ts +36 -0
- package/types/be-committed/types.d.ts +22 -0
- package/types/be-decked-with/types.d.ts +26 -0
- package/types/be-delible/types.d.ts +25 -0
- package/types/be-reflective/types.d.ts +80 -0
- package/types/be-render-neutral/types.d.ts +29 -0
- package/types/be-typed/types.d.ts +31 -0
- package/types/do-inc/types.d.ts +56 -0
- package/types/do-invoke/types.d.ts +38 -0
- package/types/do-merge/types.d.ts +28 -0
- package/types/do-toggle/types.d.ts +31 -0
- package/types/face-up/types.d.ts +104 -0
- package/types/global.d.ts +29 -0
- package/types/id-generation/types.d.ts +26 -0
- package/types/inferencer/types.d.ts +46 -0
- package/types/mount-observer/types.d.ts +363 -0
- package/types/nested-regex-groups/types.d.ts +101 -0
- package/types/roundabout/types.d.ts +255 -0
- package/types/time-ticker/types.d.ts +66 -0
- package/types/truth-sourcer/types.d.ts +46 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# New Enhancement Instructions
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
This document provides step-by-step instructions for creating a **brand new** "be-*" or "do-*" enhancement project using the modern architecture. Unlike the [Enhancement Conversion Instructions](./EnhancementConversionInstructions.md) (which covers migrating legacy projects), this guide starts from scratch.
|
|
6
|
+
|
|
7
|
+
**Note:** This guide is specifically for **enhancements** (declarative behaviors added to existing HTML elements via attributes). It does NOT apply to custom elements. Enhancements use `be-hive` and `mount-observer` to attach behavior to elements without requiring custom element registration.
|
|
8
|
+
|
|
9
|
+
## Reference Implementations
|
|
10
|
+
|
|
11
|
+
- **[do-invoke](https://github.com/bahrus/do-invoke)** — Custom parser with nested paths and default values
|
|
12
|
+
- **[do-toggle](https://github.com/bahrus/do-toggle)** — Parser with infer pattern
|
|
13
|
+
- **[do-inc](https://github.com/bahrus/do-inc)** — Increment enhancement with parser
|
|
14
|
+
- **[be-clonable](https://github.com/bahrus/be-clonable)** — Most up-to-date conversion example (useful for architecture patterns)
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
- Node.js installed
|
|
19
|
+
- npm installed
|
|
20
|
+
- `ncu` (npm-check-updates) installed globally: `npm install -g npm-check-updates`
|
|
21
|
+
|
|
22
|
+
## Step 1: Initialize the Project
|
|
23
|
+
|
|
24
|
+
1. Create a new repository (e.g., `do-merge` or `be-fancy`)
|
|
25
|
+
2. Run `npm init` or create a `package.json` manually
|
|
26
|
+
3. Add the `types` submodule:
|
|
27
|
+
```bash
|
|
28
|
+
git submodule add https://github.com/bahrus/types.git types
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Step 2: Configure package.json
|
|
32
|
+
|
|
33
|
+
Set up the standard scripts and dependencies:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"name": "do-my-enhancement",
|
|
38
|
+
"version": "0.0.0",
|
|
39
|
+
"description": "Description of what the enhancement does",
|
|
40
|
+
"type": "module",
|
|
41
|
+
"main": "do-my-enhancement.js",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "node emc.mjs > emc.json && node [emoji].mjs > [emoji].json",
|
|
44
|
+
"serve": "node ./node_modules/spa-ssi/serve.js",
|
|
45
|
+
"test": "playwright test",
|
|
46
|
+
"safari": "npx playwright wk http://localhost:8000",
|
|
47
|
+
"update": "ncu -u && npm install"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"assign-gingerly": "0.0.5",
|
|
51
|
+
"be-hive": "0.1.9",
|
|
52
|
+
"inferencer": "0.0.1",
|
|
53
|
+
"mount-observer": "0.0.16",
|
|
54
|
+
"roundabout-lib": "0.0.2"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"spa-ssi": "0.0.27",
|
|
58
|
+
"@playwright/test": "1.59.1"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Notes:**
|
|
64
|
+
- Replace `[emoji]` with your emoji shorthand (or remove that part of the build script if no emoji)
|
|
65
|
+
- Use exact versions, not ranges (no `^` or `~`)
|
|
66
|
+
- Add `nested-regex-groups` if you need custom attribute parsing
|
|
67
|
+
- Run `npm run update` after creating package.json to install dependencies
|
|
68
|
+
|
|
69
|
+
## Step 3: Create Type Definitions
|
|
70
|
+
|
|
71
|
+
Create `types/[project-name]/types.d.ts` with the standard structure:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
import { ElementEnhancementGateway, SpawnContext } from "../assign-gingerly/types";
|
|
75
|
+
|
|
76
|
+
export interface EndUserProps {
|
|
77
|
+
// Properties that end users configure via attributes
|
|
78
|
+
myProp: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface AllProps extends EndUserProps {
|
|
82
|
+
enhancedElement: Element & ElementEnhancementGateway;
|
|
83
|
+
resolved: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type AP = AllProps;
|
|
87
|
+
export type PAP = Partial<AP>;
|
|
88
|
+
export type ProPAP = Promise<PAP>;
|
|
89
|
+
|
|
90
|
+
export interface Actions {
|
|
91
|
+
init(self: AP, enhancedElement: Element, ctx: SpawnContext, initVals: PAP): Promise<void>;
|
|
92
|
+
hydrate(self: AP): ProPAP;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Key points:**
|
|
97
|
+
- `EndUserProps` — what the user configures via HTML attributes
|
|
98
|
+
- `AllProps` — includes `enhancedElement` and any internal state like `resolved`
|
|
99
|
+
- `Actions` — methods the enhancement exposes; `init` always has the 4-parameter signature
|
|
100
|
+
- If using a custom parser, import `StatementsResult` from `../nested-regex-groups/types`
|
|
101
|
+
|
|
102
|
+
## Step 4: Create emc.mjs (Build Configuration)
|
|
103
|
+
|
|
104
|
+
Create `emc.mjs` in the project root:
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
//@ts-check
|
|
108
|
+
|
|
109
|
+
/** @import {EMC} from './types/mount-observer/types' */;
|
|
110
|
+
/** @import {AllProps, Actions} from './types/[project-name]/types' */
|
|
111
|
+
/** @import {RAConfig} from './types/roundabout/types' */
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @type {EMC<any, AllProps, Element, RAConfig<AllProps, Actions> >}
|
|
115
|
+
*/
|
|
116
|
+
export const emc = {
|
|
117
|
+
enhConfig: {
|
|
118
|
+
enhKey: 'DoMyEnhancement',
|
|
119
|
+
spawn: '[project-name]/[project-name].js',
|
|
120
|
+
withAttrs: {
|
|
121
|
+
base: '[project-name]',
|
|
122
|
+
// For simple string/number properties:
|
|
123
|
+
// myProp: '${base}-my-prop',
|
|
124
|
+
// For the base attribute itself (JSON object):
|
|
125
|
+
// _base: { mapsTo: 'myProp', instanceOf: 'Object' }
|
|
126
|
+
// For boolean properties:
|
|
127
|
+
// _nudge: { instanceOf: 'Boolean' }
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
customData: {
|
|
131
|
+
weakRef: {
|
|
132
|
+
properties: ['enhancedElement']
|
|
133
|
+
},
|
|
134
|
+
actions: {
|
|
135
|
+
hydrate: {
|
|
136
|
+
ifAllOf: ['myProp', 'enhancedElement']
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export function render(){
|
|
143
|
+
return JSON.stringify(emc, null, 4);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.log(render());
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Attribute Parsing Options
|
|
150
|
+
|
|
151
|
+
**JSON attribute (parsed by browser):**
|
|
152
|
+
```javascript
|
|
153
|
+
_base: { mapsTo: 'mergeParamSets', instanceOf: 'Object' }
|
|
154
|
+
```
|
|
155
|
+
Use when the attribute value is valid JSON. The browser's JSON parser handles it.
|
|
156
|
+
|
|
157
|
+
**Custom parser (for string DSL syntax):**
|
|
158
|
+
```javascript
|
|
159
|
+
_base: {
|
|
160
|
+
mapsTo: 'invokeParamSet',
|
|
161
|
+
parser: 'parse-pattern-statements',
|
|
162
|
+
instanceOf: 'Array',
|
|
163
|
+
parserConfig: parsePatterns
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
Use when you need to parse a custom string syntax. See [Enhancement Conversion Instructions](./EnhancementConversionInstructions.md) Step 7a for details.
|
|
167
|
+
|
|
168
|
+
## Step 5: Create Emoji Shorthand (Optional)
|
|
169
|
+
|
|
170
|
+
If your enhancement has an emoji shorthand, create `[emoji].mjs`:
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
import myJSON from './emc.json' with {type: 'json'};
|
|
174
|
+
|
|
175
|
+
/** @import {EMC} from './types/mount-observer/types' */;
|
|
176
|
+
/** @import {AllProps} from './types/[project-name]/types' */
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @type {EMC<any, AllProps> }
|
|
180
|
+
*/
|
|
181
|
+
const emc = {
|
|
182
|
+
...myJSON,
|
|
183
|
+
enhConfig: {
|
|
184
|
+
...myJSON.enhConfig,
|
|
185
|
+
enhKey: '[emoji]',
|
|
186
|
+
withAttrs: {
|
|
187
|
+
...myJSON.enhConfig.withAttrs,
|
|
188
|
+
base: '[emoji]'
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export function render(){
|
|
194
|
+
return JSON.stringify(emc, null, 4);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
console.log(render());
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Critical:** The `...myJSON` spread at the top level ensures `customData` is carried over.
|
|
201
|
+
|
|
202
|
+
## Step 6: Create the Enhancement Class
|
|
203
|
+
|
|
204
|
+
Create `[project-name].js`:
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
// @ts-check
|
|
208
|
+
/** @import {Actions, PAP, AllProps, AP} from './types/[project-name]/types' */;
|
|
209
|
+
/** @import {RoundaboutOptions} from './types/roundabout/types' */;
|
|
210
|
+
/** @import {ElementEnhancementGateway, SpawnContext} from './types/assign-gingerly/types' */;
|
|
211
|
+
/** @import {EMC} from './types/mount-observer/types' */;
|
|
212
|
+
/** @import {RAConfig} from './types/roundabout/types' */;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @implements {Actions}
|
|
216
|
+
*/
|
|
217
|
+
class DoMyEnhancement {
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @this {AllProps & Actions}
|
|
221
|
+
* @param {Element & ElementEnhancementGateway} enhancedElement
|
|
222
|
+
* @param {SpawnContext} ctx
|
|
223
|
+
* @param {PAP} initVals
|
|
224
|
+
*/
|
|
225
|
+
constructor(enhancedElement, ctx, initVals){
|
|
226
|
+
this.init(this, enhancedElement, ctx, initVals);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* @param {AllProps} self
|
|
231
|
+
* @param {Element & ElementEnhancementGateway} enhancedElement
|
|
232
|
+
* @param {SpawnContext} ctx
|
|
233
|
+
* @param {PAP} initVals
|
|
234
|
+
*/
|
|
235
|
+
async init(self, enhancedElement, ctx, initVals){
|
|
236
|
+
const {customData} = /** @type {EMC<any, AllProps, Element, RAConfig<AllProps, Actions>>} */ (ctx.emc);
|
|
237
|
+
/**
|
|
238
|
+
* @type {RoundaboutOptions}
|
|
239
|
+
*/
|
|
240
|
+
const raOptions = {
|
|
241
|
+
...customData,
|
|
242
|
+
vm: self,
|
|
243
|
+
initialPropVals: {
|
|
244
|
+
enhancedElement,
|
|
245
|
+
...customData?.defaultPropVals,
|
|
246
|
+
...initVals
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
(await import('roundabout-lib/roundabout.js')).roundabout(raOptions);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @param {AP} self
|
|
254
|
+
*/
|
|
255
|
+
async hydrate(self){
|
|
256
|
+
// Your enhancement logic here
|
|
257
|
+
return /** @type {PAP} */ ({resolved: true});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export {DoMyEnhancement};
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Key patterns:**
|
|
265
|
+
- No base class — plain JavaScript class
|
|
266
|
+
- Constructor delegates to `init` with 4 parameters
|
|
267
|
+
- `init` extracts `customData` from `ctx.emc` (no JSON import needed)
|
|
268
|
+
- Action methods receive `self` (the reactive proxy) as first parameter
|
|
269
|
+
- Return partial props from actions to trigger reactive updates
|
|
270
|
+
|
|
271
|
+
## Step 7: Create imports.html
|
|
272
|
+
|
|
273
|
+
```html
|
|
274
|
+
<script type=importmap >
|
|
275
|
+
{
|
|
276
|
+
"imports": {
|
|
277
|
+
"assign-gingerly/": "/node_modules/assign-gingerly/",
|
|
278
|
+
"[project-name]/": "/",
|
|
279
|
+
"be-hive/": "/node_modules/be-hive/",
|
|
280
|
+
"inferencer/": "/node_modules/inferencer/",
|
|
281
|
+
"mount-observer/": "/node_modules/mount-observer/",
|
|
282
|
+
"roundabout-lib/": "/node_modules/roundabout-lib/",
|
|
283
|
+
"id-generation/": "/node_modules/id-generation/"
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
</script>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Step 8: Configure VS Code
|
|
290
|
+
|
|
291
|
+
Create `.vscode/settings.json`:
|
|
292
|
+
|
|
293
|
+
```json
|
|
294
|
+
{
|
|
295
|
+
"explorer.fileNesting.patterns": {
|
|
296
|
+
"*.mjs": "${capture}.json"
|
|
297
|
+
},
|
|
298
|
+
"explorer.fileNesting.enabled": true
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Step 9: Set Up Auto-Build Hook
|
|
303
|
+
|
|
304
|
+
Create `.kiro/hooks/auto-build-config.kiro.hook`:
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"name": "Auto-build Configuration",
|
|
309
|
+
"version": "1.0.0",
|
|
310
|
+
"description": "Automatically runs npm run build when emc.mjs or emoji .mjs files are saved",
|
|
311
|
+
"when": {
|
|
312
|
+
"type": "fileEdited",
|
|
313
|
+
"patterns": ["emc.mjs", "[emoji].mjs"]
|
|
314
|
+
},
|
|
315
|
+
"then": {
|
|
316
|
+
"type": "runCommand",
|
|
317
|
+
"command": "npm run build",
|
|
318
|
+
"timeout": 10000
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Step 10: Build and Verify
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
npm run update # Install/update dependencies
|
|
327
|
+
npm run build # Generate emc.json and emoji.json
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Verify that `emc.json` (and `[emoji].json`) are generated with the expected structure.
|
|
331
|
+
|
|
332
|
+
## Step 11: Create Test HTML
|
|
333
|
+
|
|
334
|
+
Create a test file (e.g., `tests/test1.html`):
|
|
335
|
+
|
|
336
|
+
```html
|
|
337
|
+
<!DOCTYPE html>
|
|
338
|
+
<html lang="en">
|
|
339
|
+
<head>
|
|
340
|
+
<meta charset="UTF-8">
|
|
341
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
342
|
+
<title>Test</title>
|
|
343
|
+
<!-- #include virtual="/imports.html" -->
|
|
344
|
+
<be-hive>
|
|
345
|
+
<script type=emc src="[project-name]/emc.json"></script>
|
|
346
|
+
</be-hive>
|
|
347
|
+
<script type=module>
|
|
348
|
+
import 'be-hive/be-hive.js';
|
|
349
|
+
</script>
|
|
350
|
+
</head>
|
|
351
|
+
<body>
|
|
352
|
+
<!-- Your test markup here -->
|
|
353
|
+
</body>
|
|
354
|
+
</html>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Architecture Overview
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
[project-name]/
|
|
361
|
+
├── .kiro/
|
|
362
|
+
│ └── hooks/
|
|
363
|
+
│ └── auto-build-config.kiro.hook
|
|
364
|
+
├── .vscode/
|
|
365
|
+
│ └── settings.json
|
|
366
|
+
├── types/ (git submodule)
|
|
367
|
+
│ └── [project-name]/
|
|
368
|
+
│ └── types.d.ts
|
|
369
|
+
├── [project-name].js (enhancement class - browser code)
|
|
370
|
+
├── emc.mjs (build script → emc.json)
|
|
371
|
+
├── emc.json (generated - runtime config)
|
|
372
|
+
├── [emoji].mjs (build script → [emoji].json)
|
|
373
|
+
├── [emoji].json (generated - emoji variant config)
|
|
374
|
+
├── imports.html (import map for browser)
|
|
375
|
+
├── package.json
|
|
376
|
+
└── README.md
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Key Differences from Conversion
|
|
380
|
+
|
|
381
|
+
| Aspect | Conversion | New Enhancement |
|
|
382
|
+
|--------|-----------|-----------------|
|
|
383
|
+
| Legacy code | Move to `legacy/` folder | N/A — no legacy code |
|
|
384
|
+
| ts-refs | Remove and migrate to `types/` | Start directly in `types/` |
|
|
385
|
+
| Type cleanup | Remove IEnhancement extends, BAP | Write clean types from scratch |
|
|
386
|
+
| Static config | Migrate to emc.mjs customData | Write customData directly |
|
|
387
|
+
| Class refactor | Remove BE extends, bootUp | Write plain class from start |
|
|
388
|
+
|
|
389
|
+
## Tips
|
|
390
|
+
|
|
391
|
+
- **Start simple**: Get the basic case working before adding complexity
|
|
392
|
+
- **Test incrementally**: Build one example at a time
|
|
393
|
+
- **Check generated JSON**: Run `node emc.mjs` to see the output
|
|
394
|
+
- **Use `@ts-check`**: Catches type errors early in `.js` files
|
|
395
|
+
- **Don't import emc.json in the class**: The config comes through `ctx.emc`
|
package/types/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {RAConfig} from '../roundabout/types';
|
|
2
|
+
import {AttrPatterns} from '../assign-gingerly/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Assign Gingerly Roundabout Config
|
|
6
|
+
*/
|
|
7
|
+
export interface AgraceConfig<TProps = unknown, TActions = TProps, ETProps = TProps, TCustomData = unknown> {
|
|
8
|
+
raConfig: RAConfig<TProps, TActions, ETProps, TCustomData>,
|
|
9
|
+
withAttrs?: AttrPatterns<TProps>,
|
|
10
|
+
template?: string | HTMLTemplateElement,
|
|
11
|
+
}
|