@govela/create 0.0.0-stage → 0.7.0
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/LICENSE +21 -0
- package/README.md +17 -2
- package/bin/create.js +146 -0
- package/package.json +24 -4
- package/templates/app/base/AGENTS.md +44 -0
- package/templates/app/base/README.md +19 -0
- package/templates/app/base/_github/workflows/ci.yml +23 -0
- package/templates/app/base/_gitignore +7 -0
- package/templates/app/base/cmd/server/main.go +53 -0
- package/templates/app/base/go.mod +8 -0
- package/templates/app/base/server/routes/load.go +30 -0
- package/templates/app/base/vela.config.json +6 -0
- package/templates/app/base/vite.config.js +11 -0
- package/templates/app/base/vitest.setup.__EXT__ +5 -0
- package/templates/app/js/eslint.config.js +12 -0
- package/templates/app/js/package.json +32 -0
- package/templates/app/js/src/routes/Layout.vela +24 -0
- package/templates/app/js/src/routes/Page.test.js +13 -0
- package/templates/app/js/src/routes/Page.vela +10 -0
- package/templates/app/ts/eslint.config.js +14 -0
- package/templates/app/ts/package.json +34 -0
- package/templates/app/ts/src/routes/Layout.vela +26 -0
- package/templates/app/ts/src/routes/Page.test.ts +13 -0
- package/templates/app/ts/src/routes/Page.vela +12 -0
- package/templates/app/ts/tsconfig.json +4 -0
- package/templates/app/ts/vela.app.json +1 -0
- package/templates/default/base/AGENTS.md +28 -0
- package/templates/default/base/README.md +17 -0
- package/templates/default/base/_github/workflows/ci.yml +20 -0
- package/templates/default/base/_gitignore +2 -0
- package/templates/default/base/index.html +12 -0
- package/templates/default/base/vite.config.js +9 -0
- package/templates/default/base/vitest.setup.__EXT__ +5 -0
- package/templates/default/js/eslint.config.js +12 -0
- package/templates/default/js/package.json +28 -0
- package/templates/default/js/src/App.vela +25 -0
- package/templates/default/js/src/Counter.test.js +20 -0
- package/templates/default/js/src/Counter.vela +23 -0
- package/templates/default/js/src/main.js +4 -0
- package/templates/default/ts/eslint.config.js +14 -0
- package/templates/default/ts/package.json +30 -0
- package/templates/default/ts/src/App.vela +25 -0
- package/templates/default/ts/src/Counter.test.ts +20 -0
- package/templates/default/ts/src/Counter.vela +27 -0
- package/templates/default/ts/src/main.ts +4 -0
- package/templates/default/ts/tsconfig.json +18 -0
- package/templates/ssr/base/AGENTS.md +34 -0
- package/templates/ssr/base/README.md +30 -0
- package/templates/ssr/base/_github/workflows/ci.yml +23 -0
- package/templates/ssr/base/_gitignore +4 -0
- package/templates/ssr/base/cmd/server/main.go +57 -0
- package/templates/ssr/base/go.mod +5 -0
- package/templates/ssr/base/vela.config.json +6 -0
- package/templates/ssr/base/vite.config.js +11 -0
- package/templates/ssr/base/vitest.setup.__EXT__ +5 -0
- package/templates/ssr/js/eslint.config.js +12 -0
- package/templates/ssr/js/package.json +30 -0
- package/templates/ssr/js/src/App.vela +27 -0
- package/templates/ssr/js/src/Counter.test.js +20 -0
- package/templates/ssr/js/src/Counter.vela +23 -0
- package/templates/ssr/js/src/client.js +5 -0
- package/templates/ssr/ts/eslint.config.js +14 -0
- package/templates/ssr/ts/package.json +32 -0
- package/templates/ssr/ts/src/App.vela +27 -0
- package/templates/ssr/ts/src/Counter.test.ts +20 -0
- package/templates/ssr/ts/src/Counter.vela +27 -0
- package/templates/ssr/ts/src/client.ts +5 -0
- package/templates/ssr/ts/tsconfig.json +18 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 abullo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @govela/create
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Creates a Vela application.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm create @govela my-app # Vite, Vitest with @govela/testing
|
|
7
|
+
npm create @govela my-app -- --ssr # a Go server renders, Vite builds the client
|
|
8
|
+
npm create @govela my-app -- --app # a Vela App: routes with Go loaders
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The projects are TypeScript (`vela check --types`, typed tests); `--js`
|
|
12
|
+
writes JavaScript. `--module example.com/my-app` sets the Go module path
|
|
13
|
+
of `--ssr` and `--app`. Every project has a component test, ESLint with
|
|
14
|
+
`@govela/eslint-plugin`, `npm run check` (`vela check`), a GitHub Actions
|
|
15
|
+
workflow running the check, the linter, the tests and the build, and an
|
|
16
|
+
`AGENTS.md` pointing to the Vela authoring guide.
|
|
17
|
+
|
|
18
|
+
License: MIT.
|
package/bin/create.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// npm create @govela [directory] -- [--ssr | --app] [--js] [--module path]
|
|
3
|
+
//
|
|
4
|
+
// Creates a Vela application from one of the templates of this package
|
|
5
|
+
// (tasks 066, 088): default (Vite), ssr (a Go server renders, Vite builds
|
|
6
|
+
// the client) or app (a Vela App with routes and Go loaders), in TypeScript
|
|
7
|
+
// or, with --js, JavaScript. A template is templates/<name>/base, the files
|
|
8
|
+
// of both languages, and templates/<name>/ts or /js on top of it.
|
|
9
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
|
|
13
|
+
const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
14
|
+
const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
15
|
+
|
|
16
|
+
const usage = `usage: npm create @govela [directory] -- [options]
|
|
17
|
+
|
|
18
|
+
options:
|
|
19
|
+
--ssr server rendering: a Go server renders, Vite builds the client
|
|
20
|
+
--app a Vela App: file-based routes with Go loaders
|
|
21
|
+
--js JavaScript instead of TypeScript
|
|
22
|
+
--ts TypeScript (the default)
|
|
23
|
+
--module path the Go module path of --ssr and --app (example.com/<name>)
|
|
24
|
+
--help this text
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
function fail(message) {
|
|
28
|
+
process.stderr.write(`create-vela: ${message}\n`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function parseArgs(argv) {
|
|
33
|
+
const options = { template: 'default', directory: null, module: null, language: 'ts' };
|
|
34
|
+
for (let i = 0; i < argv.length; i++) {
|
|
35
|
+
const arg = argv[i];
|
|
36
|
+
switch (arg) {
|
|
37
|
+
case '--ssr':
|
|
38
|
+
case '--app': {
|
|
39
|
+
const template = arg.slice(2);
|
|
40
|
+
if (options.template !== 'default' && options.template !== template) throw new Error('--ssr and --app exclude each other');
|
|
41
|
+
options.template = template;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case '--js':
|
|
45
|
+
case '--ts':
|
|
46
|
+
options.language = arg.slice(2);
|
|
47
|
+
break;
|
|
48
|
+
case '--module':
|
|
49
|
+
if (!argv[i + 1]) throw new Error('--module needs a Go module path');
|
|
50
|
+
options.module = argv[++i];
|
|
51
|
+
break;
|
|
52
|
+
case '--help':
|
|
53
|
+
case '-h':
|
|
54
|
+
options.help = true;
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
if (arg.startsWith('-')) throw new Error(`unknown option ${arg}`);
|
|
58
|
+
if (options.directory) throw new Error(`a second directory ${arg}`);
|
|
59
|
+
options.directory = arg;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return options;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// packageName turns a directory name into a valid npm package name.
|
|
66
|
+
function packageName(dir) {
|
|
67
|
+
const name = basename(dir).toLowerCase().replace(/[^a-z0-9._~-]+/g, '-').replace(/^[._-]+|-+$/g, '');
|
|
68
|
+
return name || 'vela-app';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Files that npm would rename or drop when publishing are stored with a
|
|
72
|
+
// leading underscore.
|
|
73
|
+
function outputName(name) {
|
|
74
|
+
if (name === '_gitignore') return '.gitignore';
|
|
75
|
+
if (name === '_github') return '.github';
|
|
76
|
+
return name;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function substitute(text, values) {
|
|
80
|
+
for (const [key, value] of Object.entries(values)) text = text.split(key).join(value);
|
|
81
|
+
return text;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function copyTemplate(from, to, values) {
|
|
85
|
+
mkdirSync(to, { recursive: true });
|
|
86
|
+
for (const name of readdirSync(from)) {
|
|
87
|
+
const source = join(from, name);
|
|
88
|
+
const target = join(to, substitute(outputName(name), values));
|
|
89
|
+
if (statSync(source).isDirectory()) {
|
|
90
|
+
copyTemplate(source, target, values);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
writeFileSync(target, substitute(readFileSync(source, 'utf8'), values));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function create(options) {
|
|
98
|
+
const directory = resolve(options.directory || 'vela-app');
|
|
99
|
+
if (existsSync(directory) && readdirSync(directory).length > 0) {
|
|
100
|
+
throw new Error(`${directory} is not empty`);
|
|
101
|
+
}
|
|
102
|
+
const name = packageName(directory);
|
|
103
|
+
const module = options.module || `example.com/${name}`;
|
|
104
|
+
if (!/^[A-Za-z0-9._~\/-]+$/.test(module)) throw new Error(`${module} is not a Go module path`);
|
|
105
|
+
const values = {
|
|
106
|
+
__NAME__: name,
|
|
107
|
+
__MODULE__: module,
|
|
108
|
+
__VERSION__: manifest.version,
|
|
109
|
+
__APP_VERSION__: manifest.govela.app,
|
|
110
|
+
__EXT__: options.language,
|
|
111
|
+
};
|
|
112
|
+
const template = join(root, 'templates', options.template);
|
|
113
|
+
copyTemplate(join(template, 'base'), directory, values);
|
|
114
|
+
copyTemplate(join(template, options.language), directory, values);
|
|
115
|
+
return { directory, name, template: options.template, language: options.language };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function main() {
|
|
119
|
+
let options;
|
|
120
|
+
try {
|
|
121
|
+
options = parseArgs(process.argv.slice(2));
|
|
122
|
+
} catch (err) {
|
|
123
|
+
process.stderr.write(usage);
|
|
124
|
+
fail(err.message);
|
|
125
|
+
}
|
|
126
|
+
if (options.help) {
|
|
127
|
+
process.stdout.write(usage);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
let result;
|
|
131
|
+
try {
|
|
132
|
+
result = create(options);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
fail(err.message);
|
|
135
|
+
}
|
|
136
|
+
const dir = relative(process.cwd(), result.directory) || '.';
|
|
137
|
+
const steps = [`cd ${dir}`, 'npm install'];
|
|
138
|
+
if (result.template === 'ssr') steps.push('npx vela', 'go mod tidy', 'npm run dev:vela & npm run dev & npm run dev:server');
|
|
139
|
+
else if (result.template === 'app') steps.push('npx vela-app gen && npx vela', 'go mod tidy', 'npm run dev');
|
|
140
|
+
else steps.push('npm run dev');
|
|
141
|
+
const language = result.language === 'ts' ? 'TypeScript' : 'JavaScript';
|
|
142
|
+
process.stdout.write(`Created ${result.name} (${result.template}, ${language}) in ${result.directory}\n\nNext:\n${steps.map((s) => ` ${s}\n`).join('')}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// npm runs the command through a link in node_modules/.bin.
|
|
146
|
+
if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@govela/create",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"description": "Creates a Vela application: npm create @govela. Manifest generated by the compiler repository; do not edit.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/elauven/vela.git",
|
|
9
|
+
"directory": "packages/create"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"bin": {
|
|
13
|
+
"create-govela": "bin/create.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin",
|
|
17
|
+
"templates",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
22
|
+
},
|
|
23
|
+
"govela": {
|
|
24
|
+
"app": "0.3.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Agent instructions
|
|
2
|
+
|
|
3
|
+
This is a Vela App: file-based routes in `src/routes`, rendered on the
|
|
4
|
+
server by Go and hydrated in the browser. Before you create or edit a
|
|
5
|
+
`.vela` component, read the Vela authoring guide of the compiler version
|
|
6
|
+
this project uses:
|
|
7
|
+
|
|
8
|
+
https://github.com/elauven/vela/blob/v__VERSION__/AGENTS.md
|
|
9
|
+
|
|
10
|
+
and, for routes, loaders and actions:
|
|
11
|
+
|
|
12
|
+
https://github.com/elauven/vela/blob/v__VERSION__/docs/APP.md
|
|
13
|
+
|
|
14
|
+
When a compiler diagnostic disagrees with an example, trust the diagnostic.
|
|
15
|
+
|
|
16
|
+
## This project
|
|
17
|
+
|
|
18
|
+
- `src/routes/**/Page.vela` are pages, `Layout.vela` wraps them. The Go
|
|
19
|
+
loaders in `server/routes` (the same tree) return each component's
|
|
20
|
+
`data` prop; `npx vela-app scaffold` adds the missing ones.
|
|
21
|
+
- `src/_app`, `internal/appgen` and `out/` are generated; never edit them.
|
|
22
|
+
- A component at any depth reads the route, the form and the data of the
|
|
23
|
+
page with `getPage()` from `#app/page.js` (call it in the script; keep it
|
|
24
|
+
as `$state.deep(getPage())` to follow navigation) instead of passing
|
|
25
|
+
`route` and `data` down as props.
|
|
26
|
+
- Permission checks go in `guard.go` (`func Guard(ctx, r) error`, `npx
|
|
27
|
+
vela-app scaffold -guard /route`), never in a layout loader: a guard runs
|
|
28
|
+
before every loader and action below its directory. Read the user from
|
|
29
|
+
an `app.Local` set by middleware.
|
|
30
|
+
- Code that runs during the first render is translated to Go: keep it free
|
|
31
|
+
of browser APIs. Treat every `VELA-SSR-*` diagnostic as an error to fix.
|
|
32
|
+
- Tests are `*.test.__EXT__` files next to the components, run by Vitest with
|
|
33
|
+
`@govela/testing` (`render`, `fireEvent`).
|
|
34
|
+
|
|
35
|
+
## Before handing off
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npm run check # vela-app gen and vela check --ssr (and --types)
|
|
39
|
+
npm run lint # eslint: the code of the components and modules
|
|
40
|
+
npm test # vitest run
|
|
41
|
+
npm run build # vela-app build: the server and the client into build/
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
All of them must pass.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# __NAME__
|
|
2
|
+
|
|
3
|
+
A [Vela App](https://github.com/elauven/vela/blob/main/docs/VELA_APP.md):
|
|
4
|
+
routes in `src/routes`, Go loaders in `server/routes`, the server in
|
|
5
|
+
`cmd/server`.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install
|
|
9
|
+
npx vela-app gen && npx vela # once, so that go mod tidy sees the generated packages
|
|
10
|
+
go mod tidy
|
|
11
|
+
npm run dev # http://127.0.0.1:8080
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm test # component tests
|
|
16
|
+
npm run check # compiler and server-rendering diagnostics
|
|
17
|
+
npm run lint # eslint
|
|
18
|
+
npm run build # build/: the server binary and the client
|
|
19
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v5
|
|
12
|
+
- uses: actions/setup-go@v6
|
|
13
|
+
with:
|
|
14
|
+
go-version-file: go.mod
|
|
15
|
+
- uses: actions/setup-node@v5
|
|
16
|
+
with:
|
|
17
|
+
node-version: '24'
|
|
18
|
+
cache: npm
|
|
19
|
+
- run: npm ci
|
|
20
|
+
- run: npm run check
|
|
21
|
+
- run: npm run lint
|
|
22
|
+
- run: npm test
|
|
23
|
+
- run: npm run build
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"flag"
|
|
5
|
+
"fmt"
|
|
6
|
+
"log"
|
|
7
|
+
"net"
|
|
8
|
+
"net/http"
|
|
9
|
+
"os"
|
|
10
|
+
"path/filepath"
|
|
11
|
+
|
|
12
|
+
"__MODULE__/internal/appgen"
|
|
13
|
+
"github.com/elauven/vela/app"
|
|
14
|
+
"github.com/elauven/vela/ssr_runtime/vite"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
func main() {
|
|
18
|
+
addr := flag.String("addr", "127.0.0.1:8080", "listen address")
|
|
19
|
+
dev := flag.String("dev", "", "URL of the Vite dev server; serve dist/ when empty")
|
|
20
|
+
dist := flag.String("dist", "dist", `directory of vite build; "none" serves pages without client`)
|
|
21
|
+
flag.Parse()
|
|
22
|
+
|
|
23
|
+
mux := http.NewServeMux()
|
|
24
|
+
var assets *vite.Assets
|
|
25
|
+
switch {
|
|
26
|
+
case *dev != "":
|
|
27
|
+
assets = vite.Dev(*dev)
|
|
28
|
+
case *dist != "none":
|
|
29
|
+
var err error
|
|
30
|
+
if assets, err = vite.Load(filepath.Join(*dist, ".vite", "manifest.json"), "/"); err != nil {
|
|
31
|
+
log.Fatal(err)
|
|
32
|
+
}
|
|
33
|
+
mux.Handle("/assets/", http.FileServer(http.Dir(*dist)))
|
|
34
|
+
}
|
|
35
|
+
handler, err := app.New(app.Config{
|
|
36
|
+
Routes: appgen.Routes(),
|
|
37
|
+
Assets: assets,
|
|
38
|
+
Manifest: filepath.Join("out", "client", "vela.manifest.json"),
|
|
39
|
+
Head: app.Head{Title: "__NAME__"},
|
|
40
|
+
Dev: *dev != "",
|
|
41
|
+
})
|
|
42
|
+
if err != nil {
|
|
43
|
+
log.Fatal(err)
|
|
44
|
+
}
|
|
45
|
+
mux.Handle("/", handler)
|
|
46
|
+
|
|
47
|
+
listener, err := net.Listen("tcp", *addr)
|
|
48
|
+
if err != nil {
|
|
49
|
+
log.Fatal(err)
|
|
50
|
+
}
|
|
51
|
+
fmt.Fprintf(os.Stdout, "http://%s\n", listener.Addr())
|
|
52
|
+
log.Fatal(http.Serve(listener, mux))
|
|
53
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Package routes holds the loaders of src/routes: the function of a
|
|
2
|
+
// component returns its data prop.
|
|
3
|
+
package routes
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"context"
|
|
7
|
+
|
|
8
|
+
"github.com/elauven/vela/app"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// Site is the data of Layout.
|
|
12
|
+
type Site struct {
|
|
13
|
+
Site string `json:"site"`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Greeting is the data of Page.
|
|
17
|
+
type Greeting struct {
|
|
18
|
+
Greeting string `json:"greeting"`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Layout loads the data of every page.
|
|
22
|
+
func Layout(ctx context.Context, r *app.Request) (Site, error) {
|
|
23
|
+
return Site{Site: "__NAME__"}, nil
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Page loads the data of /.
|
|
27
|
+
func Page(ctx context.Context, r *app.Request) (Greeting, error) {
|
|
28
|
+
r.Head().Title = "Home"
|
|
29
|
+
return Greeting{Greeting: "Hello from Go"}, nil
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import vela from '@govela/unplugin/vite';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
plugins: [vela()],
|
|
5
|
+
server: { origin: 'http://localhost:5173' },
|
|
6
|
+
build: { manifest: true, rollupOptions: { input: 'src/_app/client.js' } },
|
|
7
|
+
test: {
|
|
8
|
+
environment: 'happy-dom',
|
|
9
|
+
setupFiles: ['./vitest.setup.__EXT__'],
|
|
10
|
+
},
|
|
11
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from 'eslint/config';
|
|
2
|
+
import js from '@eslint/js';
|
|
3
|
+
import globals from 'globals';
|
|
4
|
+
import vela from '@govela/eslint-plugin';
|
|
5
|
+
|
|
6
|
+
export default defineConfig(
|
|
7
|
+
{ ignores: ['dist/', 'out/', 'build/', 'src/_app/'] },
|
|
8
|
+
js.configs.recommended,
|
|
9
|
+
vela.configs.recommended,
|
|
10
|
+
{ files: ['src/**/*.js', '**/*.vela/*.js'], languageOptions: { globals: globals.browser } },
|
|
11
|
+
{ files: ['*.config.js'], languageOptions: { globals: globals.node } },
|
|
12
|
+
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__NAME__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"imports": {
|
|
6
|
+
"#app/*": "./src/_app/*"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"gen": "vela-app gen",
|
|
10
|
+
"dev": "vela-app dev",
|
|
11
|
+
"check": "vela-app gen && vela check --project src --ssr",
|
|
12
|
+
"lint": "eslint .",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"build": "vela-app build"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@govela/app": "__APP_VERSION__",
|
|
18
|
+
"@govela/runtime": "__VERSION__"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@eslint/js": "^10.0.1",
|
|
22
|
+
"@govela/compiler": "__VERSION__",
|
|
23
|
+
"@govela/eslint-plugin": "__VERSION__",
|
|
24
|
+
"@govela/testing": "__VERSION__",
|
|
25
|
+
"@govela/unplugin": "__VERSION__",
|
|
26
|
+
"eslint": "^10.14.0",
|
|
27
|
+
"globals": "^17.0.0",
|
|
28
|
+
"happy-dom": "^21.0.4",
|
|
29
|
+
"vite": "^8.3.5",
|
|
30
|
+
"vitest": "^5.1.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export let data = null;
|
|
3
|
+
export let route = null;
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<div class="site">
|
|
8
|
+
<nav>
|
|
9
|
+
<a href="/" class:active={route.path === '/'}>{data.site}</a>
|
|
10
|
+
</nav>
|
|
11
|
+
<main><slot /></main>
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<style>
|
|
16
|
+
.site {
|
|
17
|
+
font-family: system-ui, sans-serif;
|
|
18
|
+
max-width: 40rem;
|
|
19
|
+
margin: 2rem auto;
|
|
20
|
+
}
|
|
21
|
+
.active {
|
|
22
|
+
font-weight: bold;
|
|
23
|
+
}
|
|
24
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { fireEvent, render } from '@govela/testing';
|
|
3
|
+
import Page from './Page.vela';
|
|
4
|
+
|
|
5
|
+
test('greets and counts clicks', async () => {
|
|
6
|
+
const { container } = render(Page, { props: { data: { greeting: 'Hello' } } });
|
|
7
|
+
expect(container.querySelector('h1').textContent).toBe('Hello');
|
|
8
|
+
|
|
9
|
+
const button = container.querySelector('button');
|
|
10
|
+
await fireEvent.click(button);
|
|
11
|
+
|
|
12
|
+
expect(button.textContent).toBe('Clicked 1 times');
|
|
13
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineConfig } from 'eslint/config';
|
|
2
|
+
import js from '@eslint/js';
|
|
3
|
+
import globals from 'globals';
|
|
4
|
+
import tseslint from 'typescript-eslint';
|
|
5
|
+
import vela from '@govela/eslint-plugin';
|
|
6
|
+
|
|
7
|
+
export default defineConfig(
|
|
8
|
+
{ ignores: ['dist/', 'out/', 'build/', 'src/_app/'] },
|
|
9
|
+
js.configs.recommended,
|
|
10
|
+
tseslint.configs.recommended,
|
|
11
|
+
vela.configs.recommended,
|
|
12
|
+
{ files: ['src/**/*.ts', '**/*.vela/*.ts'], languageOptions: { globals: globals.browser } },
|
|
13
|
+
{ files: ['*.config.js'], languageOptions: { globals: globals.node } },
|
|
14
|
+
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__NAME__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"imports": {
|
|
6
|
+
"#app/*": "./src/_app/*"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"gen": "vela-app gen",
|
|
10
|
+
"dev": "vela-app dev",
|
|
11
|
+
"check": "vela-app gen && vela check --project src --types --ssr",
|
|
12
|
+
"lint": "eslint .",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"build": "vela-app build"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@govela/app": "__APP_VERSION__",
|
|
18
|
+
"@govela/runtime": "__VERSION__"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@eslint/js": "^10.0.1",
|
|
22
|
+
"@govela/compiler": "__VERSION__",
|
|
23
|
+
"@govela/eslint-plugin": "__VERSION__",
|
|
24
|
+
"@govela/testing": "__VERSION__",
|
|
25
|
+
"@govela/unplugin": "__VERSION__",
|
|
26
|
+
"eslint": "^10.14.0",
|
|
27
|
+
"globals": "^17.0.0",
|
|
28
|
+
"happy-dom": "^21.0.4",
|
|
29
|
+
"typescript": "^6.0.3",
|
|
30
|
+
"typescript-eslint": "^8.72.1",
|
|
31
|
+
"vite": "^8.3.5",
|
|
32
|
+
"vitest": "^5.1.0"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { LayoutProps } from './$types';
|
|
3
|
+
|
|
4
|
+
export let data: LayoutProps['data'];
|
|
5
|
+
export let route: LayoutProps['route'];
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<div class="site">
|
|
10
|
+
<nav>
|
|
11
|
+
<a href="/" class:active={route.path === '/'}>{data.site}</a>
|
|
12
|
+
</nav>
|
|
13
|
+
<main><slot /></main>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
.site {
|
|
19
|
+
font-family: system-ui, sans-serif;
|
|
20
|
+
max-width: 40rem;
|
|
21
|
+
margin: 2rem auto;
|
|
22
|
+
}
|
|
23
|
+
.active {
|
|
24
|
+
font-weight: bold;
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { fireEvent, render } from '@govela/testing';
|
|
3
|
+
import Page from './Page.vela';
|
|
4
|
+
|
|
5
|
+
test('greets and counts clicks', async () => {
|
|
6
|
+
const { container } = render(Page, { props: { data: { greeting: 'Hello' } } });
|
|
7
|
+
expect(container.querySelector('h1')!.textContent).toBe('Hello');
|
|
8
|
+
|
|
9
|
+
const button = container.querySelector('button')!;
|
|
10
|
+
await fireEvent.click(button);
|
|
11
|
+
|
|
12
|
+
expect(button.textContent).toBe('Clicked 1 times');
|
|
13
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { PageProps } from './$types';
|
|
3
|
+
|
|
4
|
+
export let data: PageProps['data'];
|
|
5
|
+
|
|
6
|
+
let count = $state(0);
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<h1>{data.greeting}</h1>
|
|
11
|
+
<button @click={() => count++}>Clicked {count} times</button>
|
|
12
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "typescript": true }
|