@luckystack/devkit 0.1.7 → 0.1.9
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/CHANGELOG.md +14 -14
- package/dist/index.js.map +1 -1
- package/dist/templates/page_dashboard.template.tsx +35 -35
- package/dist/templates/page_plain.template.tsx +32 -32
- package/docs/cli.md +245 -245
- package/docs/hot-reload.md +365 -365
- package/docs/loader-pipeline.md +324 -324
- package/docs/runtime-type-resolver.md +258 -258
- package/docs/template-customization.md +136 -136
- package/docs/ts-program-cache.md +199 -199
- package/docs/type-map-generation.md +392 -392
- package/package.json +2 -2
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
# Template customization
|
|
2
|
-
|
|
3
|
-
`@luckystack/devkit` injects starter content into newly-created **empty** files
|
|
4
|
-
under `src/` (a new `_api/*.ts`, `_sync/*.ts`, or `page.tsx`). Two things are
|
|
5
|
-
customizable, both from the consumer's `.luckystack/templates/` folder:
|
|
6
|
-
|
|
7
|
-
1. **Which** template a file gets — the *selection rules*.
|
|
8
|
-
2. **What** that template contains — the *content*.
|
|
9
|
-
|
|
10
|
-
Everything here is **dev-only**: devkit reads it from the hot-reload path during
|
|
11
|
-
`npm run server`. Nothing ships to end users.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## How a file gets a template
|
|
16
|
-
|
|
17
|
-
`injectTemplate(filePath)` (called by the file watcher on empty-file creation):
|
|
18
|
-
|
|
19
|
-
1. **Classify** the file structurally into a `fileKind`:
|
|
20
|
-
`'api' | 'sync_server' | 'sync_client' | 'page'` (derived from the route
|
|
21
|
-
conventions — override the markers via `registerRoutingRules`). For pages,
|
|
22
|
-
placement is validated first; an un-routable placement gets a commented
|
|
23
|
-
diagnostic instead of a real template.
|
|
24
|
-
2. **Select a kind** via `resolveTemplateKind(ctx)` — the registered rules are
|
|
25
|
-
evaluated by descending `priority` (ties: newest registration first), and the
|
|
26
|
-
first matching rule's `kind` wins.
|
|
27
|
-
3. **Resolve content** for that kind, first hit wins:
|
|
28
|
-
- `.luckystack/templates/<kind>.template.ts(x)` — consumer file
|
|
29
|
-
- a `registerTemplate('<kind>', '...')` string override
|
|
30
|
-
- the bundled default shipped inside devkit (`dist/templates/`)
|
|
31
|
-
4. **Substitute placeholders** (`{{REL_PATH}}`, `{{PAGE_PATH}}`, `{{SYNC_NAME}}`)
|
|
32
|
-
and write the file.
|
|
33
|
-
|
|
34
|
-
The match context is:
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
interface TemplateMatchContext {
|
|
38
|
-
filePath: string; // absolute path of the new file
|
|
39
|
-
fileKind: 'api' | 'sync_server' | 'sync_client' | 'page';
|
|
40
|
-
hasPairedServer: boolean; // sync_client: does *_server_v<N>.ts exist?
|
|
41
|
-
srcRelativePath: string | null; // path relative to src/, or null
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## The consumer overlay: `.luckystack/templates/`
|
|
48
|
-
|
|
49
|
-
`create-luckystack-app` scaffolds this folder. devkit auto-loads
|
|
50
|
-
`.luckystack/templates/templateRules.ts` **once, in dev, before the first
|
|
51
|
-
injection** (a plain dynamic import — no wiring needed). Absent file ⇒ devkit's
|
|
52
|
-
built-in defaults apply.
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
.luckystack/templates/
|
|
56
|
-
templateRules.ts # the selection logic — edit/remove/add rules
|
|
57
|
-
api.template.ts # editable copies of the built-in template bodies
|
|
58
|
-
sync_server.template.ts
|
|
59
|
-
sync_client_paired.template.ts
|
|
60
|
-
sync_client_standalone.template.ts
|
|
61
|
-
page_plain.template.tsx
|
|
62
|
-
page_dashboard.template.tsx
|
|
63
|
-
README.md
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
A `*.template.*` file in this folder overrides that kind's content. **Delete** a
|
|
67
|
-
file to fall back to devkit's (upgradeable) bundled default. The shipped copies
|
|
68
|
-
are a snapshot of the defaults at scaffold time — to refresh, copy from
|
|
69
|
-
`node_modules/@luckystack/devkit/dist/templates/`.
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## API
|
|
74
|
-
|
|
75
|
-
| Export | Purpose |
|
|
76
|
-
|---|---|
|
|
77
|
-
| `registerTemplateRule({ kind, match, priority? })` | Add a selection rule. `priority` default for direct calls is the caller's; defaults use 10 (specific) / 0 (page catch-all). |
|
|
78
|
-
| `registerTemplateKind(kind, { match, content?, priority? })` | Register a brand-new kind (rule + optional inline content) in one call. Default priority 100 so custom kinds beat the built-ins. |
|
|
79
|
-
| `registerTemplate(kind, content)` | Override just the content body of a kind (string). |
|
|
80
|
-
| `resolveTemplateKind(ctx)` | Evaluate the active rules → the chosen kind (or null). |
|
|
81
|
-
| `getTemplateRules()` | The active rules in evaluation order. |
|
|
82
|
-
| `clearTemplateRules()` | Drop ALL rules — including the built-in defaults. The scaffolded `templateRules.ts` calls this first so it is the single source of truth. |
|
|
83
|
-
| `registerDefaultTemplateRules()` | (Re)arm the framework defaults. Armed automatically on module load; idempotent. |
|
|
84
|
-
| `BUILT_IN_TEMPLATE_KINDS` / `BUILT_IN_TEMPLATE_FILENAMES` / `DEFAULT_DASHBOARD_PATH_PATTERN` | The 6 kinds, their bundled filenames, and the page-dashboard heuristic regex. |
|
|
85
|
-
|
|
86
|
-
Built-in kinds: `api`, `sync_server`, `sync_client_paired`,
|
|
87
|
-
`sync_client_standalone`, `page_plain`, `page_dashboard`.
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## Recipes
|
|
92
|
-
|
|
93
|
-
### Change when a page gets the dashboard layout
|
|
94
|
-
|
|
95
|
-
Edit the `page_dashboard` rule in `.luckystack/templates/templateRules.ts`:
|
|
96
|
-
|
|
97
|
-
```ts
|
|
98
|
-
registerTemplateRule({
|
|
99
|
-
kind: 'page_dashboard',
|
|
100
|
-
priority: 10,
|
|
101
|
-
match: (ctx) => ctx.fileKind === 'page' && ctx.filePath.replaceAll('\\', '/').includes('/app/'),
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Remove a rule entirely
|
|
106
|
-
|
|
107
|
-
The scaffolded `templateRules.ts` starts with `clearTemplateRules()` and then
|
|
108
|
-
re-declares each default. Delete the rule you don't want — that mapping no
|
|
109
|
-
longer applies (a page with no matching rule simply isn't injected).
|
|
110
|
-
|
|
111
|
-
### Add a brand-new template kind
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
// .luckystack/templates/templateRules.ts
|
|
115
|
-
import { registerTemplateKind } from '@luckystack/devkit';
|
|
116
|
-
|
|
117
|
-
registerTemplateKind('page_marketing', {
|
|
118
|
-
priority: 20, // beats page_dashboard / page_plain
|
|
119
|
-
match: (ctx) => ctx.fileKind === 'page' && ctx.filePath.replaceAll('\\', '/').includes('/marketing/'),
|
|
120
|
-
});
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Then create `.luckystack/templates/page_marketing.template.tsx` with the body.
|
|
124
|
-
Custom kinds resolve content from `<kind>.template.tsx` then `<kind>.template.ts`
|
|
125
|
-
in the overlay folder (or a `registerTemplate('page_marketing', '...')` string).
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
## Packaging note (framework maintainers)
|
|
130
|
-
|
|
131
|
-
The bundled templates live at `packages/devkit/src/templates/`. They are read at
|
|
132
|
-
runtime via `fs.readFileSync(dist/templates/...)`, so `tsup.config.ts` copies
|
|
133
|
-
`src/templates → dist/templates` in its `onSuccess` hook and `files: ["dist"]`
|
|
134
|
-
ships them in the tarball. The scaffolded consumer copies live separately under
|
|
135
|
-
`create-luckystack-app/template/_dot_luckystack/templates/` (the `_dot_` prefix
|
|
136
|
-
is rewritten to `.` by the scaffold so npm doesn't drop the dot-folder).
|
|
1
|
+
# Template customization
|
|
2
|
+
|
|
3
|
+
`@luckystack/devkit` injects starter content into newly-created **empty** files
|
|
4
|
+
under `src/` (a new `_api/*.ts`, `_sync/*.ts`, or `page.tsx`). Two things are
|
|
5
|
+
customizable, both from the consumer's `.luckystack/templates/` folder:
|
|
6
|
+
|
|
7
|
+
1. **Which** template a file gets — the *selection rules*.
|
|
8
|
+
2. **What** that template contains — the *content*.
|
|
9
|
+
|
|
10
|
+
Everything here is **dev-only**: devkit reads it from the hot-reload path during
|
|
11
|
+
`npm run server`. Nothing ships to end users.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## How a file gets a template
|
|
16
|
+
|
|
17
|
+
`injectTemplate(filePath)` (called by the file watcher on empty-file creation):
|
|
18
|
+
|
|
19
|
+
1. **Classify** the file structurally into a `fileKind`:
|
|
20
|
+
`'api' | 'sync_server' | 'sync_client' | 'page'` (derived from the route
|
|
21
|
+
conventions — override the markers via `registerRoutingRules`). For pages,
|
|
22
|
+
placement is validated first; an un-routable placement gets a commented
|
|
23
|
+
diagnostic instead of a real template.
|
|
24
|
+
2. **Select a kind** via `resolveTemplateKind(ctx)` — the registered rules are
|
|
25
|
+
evaluated by descending `priority` (ties: newest registration first), and the
|
|
26
|
+
first matching rule's `kind` wins.
|
|
27
|
+
3. **Resolve content** for that kind, first hit wins:
|
|
28
|
+
- `.luckystack/templates/<kind>.template.ts(x)` — consumer file
|
|
29
|
+
- a `registerTemplate('<kind>', '...')` string override
|
|
30
|
+
- the bundled default shipped inside devkit (`dist/templates/`)
|
|
31
|
+
4. **Substitute placeholders** (`{{REL_PATH}}`, `{{PAGE_PATH}}`, `{{SYNC_NAME}}`)
|
|
32
|
+
and write the file.
|
|
33
|
+
|
|
34
|
+
The match context is:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
interface TemplateMatchContext {
|
|
38
|
+
filePath: string; // absolute path of the new file
|
|
39
|
+
fileKind: 'api' | 'sync_server' | 'sync_client' | 'page';
|
|
40
|
+
hasPairedServer: boolean; // sync_client: does *_server_v<N>.ts exist?
|
|
41
|
+
srcRelativePath: string | null; // path relative to src/, or null
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## The consumer overlay: `.luckystack/templates/`
|
|
48
|
+
|
|
49
|
+
`create-luckystack-app` scaffolds this folder. devkit auto-loads
|
|
50
|
+
`.luckystack/templates/templateRules.ts` **once, in dev, before the first
|
|
51
|
+
injection** (a plain dynamic import — no wiring needed). Absent file ⇒ devkit's
|
|
52
|
+
built-in defaults apply.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
.luckystack/templates/
|
|
56
|
+
templateRules.ts # the selection logic — edit/remove/add rules
|
|
57
|
+
api.template.ts # editable copies of the built-in template bodies
|
|
58
|
+
sync_server.template.ts
|
|
59
|
+
sync_client_paired.template.ts
|
|
60
|
+
sync_client_standalone.template.ts
|
|
61
|
+
page_plain.template.tsx
|
|
62
|
+
page_dashboard.template.tsx
|
|
63
|
+
README.md
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
A `*.template.*` file in this folder overrides that kind's content. **Delete** a
|
|
67
|
+
file to fall back to devkit's (upgradeable) bundled default. The shipped copies
|
|
68
|
+
are a snapshot of the defaults at scaffold time — to refresh, copy from
|
|
69
|
+
`node_modules/@luckystack/devkit/dist/templates/`.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## API
|
|
74
|
+
|
|
75
|
+
| Export | Purpose |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `registerTemplateRule({ kind, match, priority? })` | Add a selection rule. `priority` default for direct calls is the caller's; defaults use 10 (specific) / 0 (page catch-all). |
|
|
78
|
+
| `registerTemplateKind(kind, { match, content?, priority? })` | Register a brand-new kind (rule + optional inline content) in one call. Default priority 100 so custom kinds beat the built-ins. |
|
|
79
|
+
| `registerTemplate(kind, content)` | Override just the content body of a kind (string). |
|
|
80
|
+
| `resolveTemplateKind(ctx)` | Evaluate the active rules → the chosen kind (or null). |
|
|
81
|
+
| `getTemplateRules()` | The active rules in evaluation order. |
|
|
82
|
+
| `clearTemplateRules()` | Drop ALL rules — including the built-in defaults. The scaffolded `templateRules.ts` calls this first so it is the single source of truth. |
|
|
83
|
+
| `registerDefaultTemplateRules()` | (Re)arm the framework defaults. Armed automatically on module load; idempotent. |
|
|
84
|
+
| `BUILT_IN_TEMPLATE_KINDS` / `BUILT_IN_TEMPLATE_FILENAMES` / `DEFAULT_DASHBOARD_PATH_PATTERN` | The 6 kinds, their bundled filenames, and the page-dashboard heuristic regex. |
|
|
85
|
+
|
|
86
|
+
Built-in kinds: `api`, `sync_server`, `sync_client_paired`,
|
|
87
|
+
`sync_client_standalone`, `page_plain`, `page_dashboard`.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Recipes
|
|
92
|
+
|
|
93
|
+
### Change when a page gets the dashboard layout
|
|
94
|
+
|
|
95
|
+
Edit the `page_dashboard` rule in `.luckystack/templates/templateRules.ts`:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
registerTemplateRule({
|
|
99
|
+
kind: 'page_dashboard',
|
|
100
|
+
priority: 10,
|
|
101
|
+
match: (ctx) => ctx.fileKind === 'page' && ctx.filePath.replaceAll('\\', '/').includes('/app/'),
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Remove a rule entirely
|
|
106
|
+
|
|
107
|
+
The scaffolded `templateRules.ts` starts with `clearTemplateRules()` and then
|
|
108
|
+
re-declares each default. Delete the rule you don't want — that mapping no
|
|
109
|
+
longer applies (a page with no matching rule simply isn't injected).
|
|
110
|
+
|
|
111
|
+
### Add a brand-new template kind
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// .luckystack/templates/templateRules.ts
|
|
115
|
+
import { registerTemplateKind } from '@luckystack/devkit';
|
|
116
|
+
|
|
117
|
+
registerTemplateKind('page_marketing', {
|
|
118
|
+
priority: 20, // beats page_dashboard / page_plain
|
|
119
|
+
match: (ctx) => ctx.fileKind === 'page' && ctx.filePath.replaceAll('\\', '/').includes('/marketing/'),
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Then create `.luckystack/templates/page_marketing.template.tsx` with the body.
|
|
124
|
+
Custom kinds resolve content from `<kind>.template.tsx` then `<kind>.template.ts`
|
|
125
|
+
in the overlay folder (or a `registerTemplate('page_marketing', '...')` string).
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Packaging note (framework maintainers)
|
|
130
|
+
|
|
131
|
+
The bundled templates live at `packages/devkit/src/templates/`. They are read at
|
|
132
|
+
runtime via `fs.readFileSync(dist/templates/...)`, so `tsup.config.ts` copies
|
|
133
|
+
`src/templates → dist/templates` in its `onSuccess` hook and `files: ["dist"]`
|
|
134
|
+
ships them in the tarball. The scaffolded consumer copies live separately under
|
|
135
|
+
`create-luckystack-app/template/_dot_luckystack/templates/` (the `_dot_` prefix
|
|
136
|
+
is rewritten to `.` by the scaffold so npm doesn't drop the dot-folder).
|