asjs-express 1.3.0 → 1.4.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/README.md +64 -0
- package/bin/create-asjs-app.js +223 -0
- package/package.json +11 -2
- package/templates/minimal/README.md.tpl +12 -0
- package/templates/minimal/app.js.tpl +29 -0
- package/templates/minimal/gitignore.tpl +4 -0
- package/templates/minimal/package.json.tpl +15 -0
- package/templates/minimal/views/home.asjs.tpl +5 -0
- package/templates/minimal/views/layouts/main.asjs.tpl +16 -0
- package/templates/starter/README.md.tpl +12 -0
- package/templates/starter/app.js.tpl +54 -0
- package/templates/starter/gitignore.tpl +4 -0
- package/templates/starter/package.json.tpl +15 -0
- package/templates/starter/views/about.asjs.tpl +10 -0
- package/templates/starter/views/home.asjs.tpl +10 -0
- package/templates/starter/views/layouts/main.asjs.tpl +16 -0
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ The intention is simple: this should read like something a careful team would ac
|
|
|
28
28
|
- Built-in client router with transitions, prefetch, and loading bar support
|
|
29
29
|
- Async form enhancement for marked forms
|
|
30
30
|
- Plugin and hook support for Express projects that need to grow over time
|
|
31
|
+
- npx project scaffolding so a new ASJS app can be created with starter files in one command
|
|
31
32
|
- Package-served assets, so you do not have to manually copy router files into your public folder
|
|
32
33
|
|
|
33
34
|
### Install
|
|
@@ -36,6 +37,37 @@ The intention is simple: this should read like something a careful team would ac
|
|
|
36
37
|
npm install asjs-express
|
|
37
38
|
```
|
|
38
39
|
|
|
40
|
+
### Create a new app with npx
|
|
41
|
+
|
|
42
|
+
ASJS now includes a small project generator.
|
|
43
|
+
If you want a React or Next.js-style bootstrap flow, you can create a fresh project folder with one command.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx asjs-express my-app
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
That command creates a ready-to-run ASJS project with `app.js`, `views/layouts/main.asjs`, `views/home.asjs`, `package.json`, `.gitignore`, and a small README.
|
|
50
|
+
|
|
51
|
+
You can also choose a slightly richer starter that already includes two routes and the newer helper API:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx asjs-express create my-app --template starter
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Useful flags:
|
|
58
|
+
|
|
59
|
+
- `--template minimal`
|
|
60
|
+
- `--template starter`
|
|
61
|
+
- `--skip-install`
|
|
62
|
+
- `--force`
|
|
63
|
+
|
|
64
|
+
After generation:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cd my-app
|
|
68
|
+
npm run dev
|
|
69
|
+
```
|
|
70
|
+
|
|
39
71
|
### Quick start
|
|
40
72
|
|
|
41
73
|
```js
|
|
@@ -690,6 +722,7 @@ WebAS yüzeyi, bu iki geliştiricinin birlikte şekillendirdiği düzenli, açı
|
|
|
690
722
|
- Geçiş, prefetch ve loading bar destekli istemci yönlendirmesi
|
|
691
723
|
- İşaretli formlar için dahili async form akışı
|
|
692
724
|
- Büyüyen Express projeleri için plugin ve hook desteği
|
|
725
|
+
- Tek komutla başlangıç dosyaları oluşturan npx proje oluşturucu
|
|
693
726
|
- Public klasöre dosya kopyalamadan paket içinden asset servisi
|
|
694
727
|
|
|
695
728
|
### Kurulum
|
|
@@ -698,6 +731,37 @@ WebAS yüzeyi, bu iki geliştiricinin birlikte şekillendirdiği düzenli, açı
|
|
|
698
731
|
npm install asjs-express
|
|
699
732
|
```
|
|
700
733
|
|
|
734
|
+
### npx ile yeni proje oluşturma
|
|
735
|
+
|
|
736
|
+
ASJS artık küçük bir proje oluşturucu ile geliyor.
|
|
737
|
+
React veya Next.js tarzı bir başlangıç akışı istiyorsan tek komutla yeni proje klasörü oluşturabilirsin.
|
|
738
|
+
|
|
739
|
+
```bash
|
|
740
|
+
npx asjs-express my-app
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
Bu komut çalışan bir ASJS projesi üretir. İçine `app.js`, `views/layouts/main.asjs`, `views/home.asjs`, `package.json`, `.gitignore` ve kısa bir README koyar.
|
|
744
|
+
|
|
745
|
+
Biraz daha dolu bir başlangıç istersen iki route ve yeni helper API ile gelen starter şablonunu seçebilirsin:
|
|
746
|
+
|
|
747
|
+
```bash
|
|
748
|
+
npx asjs-express create my-app --template starter
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
Kullanışlı bayraklar:
|
|
752
|
+
|
|
753
|
+
- `--template minimal`
|
|
754
|
+
- `--template starter`
|
|
755
|
+
- `--skip-install`
|
|
756
|
+
- `--force`
|
|
757
|
+
|
|
758
|
+
Üretimden sonra:
|
|
759
|
+
|
|
760
|
+
```bash
|
|
761
|
+
cd my-app
|
|
762
|
+
npm run dev
|
|
763
|
+
```
|
|
764
|
+
|
|
701
765
|
### Hızlı başlangıç
|
|
702
766
|
|
|
703
767
|
```js
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const packageJson = require('../package.json');
|
|
8
|
+
|
|
9
|
+
const TEMPLATE_REGISTRY = {
|
|
10
|
+
minimal: {
|
|
11
|
+
label: 'Minimal starter',
|
|
12
|
+
directory: path.join(__dirname, '..', 'templates', 'minimal')
|
|
13
|
+
},
|
|
14
|
+
starter: {
|
|
15
|
+
label: 'Starter app',
|
|
16
|
+
directory: path.join(__dirname, '..', 'templates', 'starter')
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function printHelp() {
|
|
21
|
+
console.log(`ASJS project creator\n\nUsage:\n npx asjs-express my-app\n npx asjs-express create my-app --template starter\n\nOptions:\n --template <name> Template to use: ${Object.keys(TEMPLATE_REGISTRY).join(', ')}\n --skip-install Do not run npm install automatically\n --force Allow writing into an existing directory\n --help Show this help message\n`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseArgs(argv) {
|
|
25
|
+
const args = [...argv];
|
|
26
|
+
const options = {
|
|
27
|
+
force: false,
|
|
28
|
+
skipInstall: false,
|
|
29
|
+
template: 'minimal'
|
|
30
|
+
};
|
|
31
|
+
let target = '';
|
|
32
|
+
|
|
33
|
+
if (args[0] === 'create') {
|
|
34
|
+
args.shift();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
while (args.length) {
|
|
38
|
+
const current = args.shift();
|
|
39
|
+
|
|
40
|
+
if (!current) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (current === '--help' || current === '-h') {
|
|
45
|
+
options.help = true;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (current === '--skip-install') {
|
|
50
|
+
options.skipInstall = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (current === '--force') {
|
|
55
|
+
options.force = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (current === '--template') {
|
|
60
|
+
options.template = String(args.shift() || '').trim() || options.template;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (current.startsWith('--template=')) {
|
|
65
|
+
options.template = current.split('=')[1] || options.template;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!target) {
|
|
70
|
+
target = current;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
options,
|
|
76
|
+
target
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function sanitizePackageName(value) {
|
|
81
|
+
return String(value || 'asjs-app')
|
|
82
|
+
.trim()
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
.replace(/[^a-z0-9-_]+/g, '-')
|
|
85
|
+
.replace(/^-+|-+$/g, '') || 'asjs-app';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function toDisplayName(value) {
|
|
89
|
+
return String(value || 'ASJS App')
|
|
90
|
+
.replace(/[-_]+/g, ' ')
|
|
91
|
+
.replace(/\s+/g, ' ')
|
|
92
|
+
.trim()
|
|
93
|
+
.split(' ')
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
96
|
+
.join(' ') || 'ASJS App';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function ensureTargetDirectory(targetDirectory, force) {
|
|
100
|
+
if (!fs.existsSync(targetDirectory)) {
|
|
101
|
+
fs.mkdirSync(targetDirectory, { recursive: true });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const stats = fs.statSync(targetDirectory);
|
|
106
|
+
if (!stats.isDirectory()) {
|
|
107
|
+
throw new Error(`Target exists but is not a directory: ${targetDirectory}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const existingEntries = fs.readdirSync(targetDirectory);
|
|
111
|
+
if (existingEntries.length > 0 && !force) {
|
|
112
|
+
throw new Error(`Target directory is not empty: ${targetDirectory}\nUse --force if you want to write files into it.`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function renderTemplate(content, variables) {
|
|
117
|
+
return content.replace(/__([A-Z0-9_]+)__/g, (match, token) => {
|
|
118
|
+
return Object.prototype.hasOwnProperty.call(variables, token) ? variables[token] : match;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function resolveOutputFileName(fileName) {
|
|
123
|
+
if (fileName === 'gitignore.tpl') {
|
|
124
|
+
return '.gitignore';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return fileName.endsWith('.tpl') ? fileName.slice(0, -4) : fileName;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function copyTemplateDirectory(sourceDirectory, targetDirectory, variables) {
|
|
131
|
+
const entries = fs.readdirSync(sourceDirectory, { withFileTypes: true });
|
|
132
|
+
|
|
133
|
+
entries.forEach((entry) => {
|
|
134
|
+
const sourcePath = path.join(sourceDirectory, entry.name);
|
|
135
|
+
const outputName = resolveOutputFileName(entry.name);
|
|
136
|
+
const targetPath = path.join(targetDirectory, outputName);
|
|
137
|
+
|
|
138
|
+
if (entry.isDirectory()) {
|
|
139
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
140
|
+
copyTemplateDirectory(sourcePath, targetPath, variables);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const content = fs.readFileSync(sourcePath, 'utf8');
|
|
145
|
+
const rendered = renderTemplate(content, variables);
|
|
146
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
147
|
+
fs.writeFileSync(targetPath, rendered, 'utf8');
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function runInstall(targetDirectory) {
|
|
152
|
+
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
153
|
+
const result = spawnSync(npmCommand, ['install'], {
|
|
154
|
+
cwd: targetDirectory,
|
|
155
|
+
stdio: 'inherit'
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return result.status === 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function main() {
|
|
162
|
+
const { options, target } = parseArgs(process.argv.slice(2));
|
|
163
|
+
|
|
164
|
+
if (options.help || !target) {
|
|
165
|
+
printHelp();
|
|
166
|
+
process.exit(options.help ? 0 : 1);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const template = TEMPLATE_REGISTRY[options.template];
|
|
170
|
+
if (!template) {
|
|
171
|
+
console.error(`Unknown template: ${options.template}`);
|
|
172
|
+
console.error(`Available templates: ${Object.keys(TEMPLATE_REGISTRY).join(', ')}`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const targetDirectory = path.resolve(process.cwd(), target);
|
|
177
|
+
const packageName = sanitizePackageName(path.basename(targetDirectory));
|
|
178
|
+
const displayName = toDisplayName(packageName);
|
|
179
|
+
const expressVersion = packageJson.dependencies && packageJson.dependencies.express
|
|
180
|
+
? packageJson.dependencies.express
|
|
181
|
+
: '^4.21.2';
|
|
182
|
+
|
|
183
|
+
const variables = {
|
|
184
|
+
APP_TITLE: displayName,
|
|
185
|
+
ASJS_VERSION: packageJson.version,
|
|
186
|
+
EXPRESS_VERSION: expressVersion,
|
|
187
|
+
PACKAGE_NAME: packageName,
|
|
188
|
+
PORT: '3000',
|
|
189
|
+
TEMPLATE_LABEL: template.label,
|
|
190
|
+
YEAR: String(new Date().getFullYear())
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
ensureTargetDirectory(targetDirectory, options.force);
|
|
195
|
+
copyTemplateDirectory(template.directory, targetDirectory, variables);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error(error.message);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log(`\nCreated ${template.label} in ${targetDirectory}`);
|
|
202
|
+
|
|
203
|
+
if (!options.skipInstall) {
|
|
204
|
+
console.log('\nInstalling dependencies...');
|
|
205
|
+
const installed = runInstall(targetDirectory);
|
|
206
|
+
|
|
207
|
+
if (!installed) {
|
|
208
|
+
console.log('\nDependency install did not complete successfully. You can run it manually:');
|
|
209
|
+
console.log(` cd ${path.basename(targetDirectory)}`);
|
|
210
|
+
console.log(' npm install');
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
console.log('\nNext steps:');
|
|
216
|
+
console.log(` cd ${path.basename(targetDirectory)}`);
|
|
217
|
+
if (options.skipInstall) {
|
|
218
|
+
console.log(' npm install');
|
|
219
|
+
}
|
|
220
|
+
console.log(' npm run dev');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "asjs-express",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Lightweight Express view engine with EJS-like templates, layouts, async page rendering, form enhancement, and a built-in client router.",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"asjs-express": "./bin/create-asjs-app.js",
|
|
8
|
+
"create-asjs-app": "./bin/create-asjs-app.js"
|
|
9
|
+
},
|
|
6
10
|
"author": {
|
|
7
11
|
"name": "Acarfx",
|
|
8
12
|
"url": "https://acarfx.com"
|
|
@@ -21,9 +25,11 @@
|
|
|
21
25
|
"./package.json": "./package.json"
|
|
22
26
|
},
|
|
23
27
|
"files": [
|
|
28
|
+
"bin",
|
|
24
29
|
"index.js",
|
|
25
30
|
"lib",
|
|
26
|
-
"README.md"
|
|
31
|
+
"README.md",
|
|
32
|
+
"templates"
|
|
27
33
|
],
|
|
28
34
|
"scripts": {
|
|
29
35
|
"start": "node example-express/app.js",
|
|
@@ -36,14 +42,17 @@
|
|
|
36
42
|
"keywords": [
|
|
37
43
|
"asjs",
|
|
38
44
|
"component",
|
|
45
|
+
"cli",
|
|
39
46
|
"ejs",
|
|
40
47
|
"express",
|
|
41
48
|
"express-view-engine",
|
|
42
49
|
"forms",
|
|
43
50
|
"layout",
|
|
44
51
|
"prefetch",
|
|
52
|
+
"scaffold",
|
|
45
53
|
"server-rendered",
|
|
46
54
|
"ssr",
|
|
55
|
+
"starter-template",
|
|
47
56
|
"template-engine",
|
|
48
57
|
"view-engine",
|
|
49
58
|
"spa-navigation"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const { setupAsjs } = require('asjs-express');
|
|
3
|
+
|
|
4
|
+
const app = express();
|
|
5
|
+
const port = process.env.PORT || __PORT__;
|
|
6
|
+
|
|
7
|
+
const asjs = setupAsjs(app, {
|
|
8
|
+
rootDir: __dirname,
|
|
9
|
+
defaultLayout: 'layouts/main',
|
|
10
|
+
navItems: [
|
|
11
|
+
{ href: '/', label: 'Home', activeMode: 'exact' }
|
|
12
|
+
],
|
|
13
|
+
transitions: 'fade',
|
|
14
|
+
prefetch: true,
|
|
15
|
+
loadingBar: true
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
app.get('/', asjs.page('home', {
|
|
19
|
+
title: '__APP_TITLE__',
|
|
20
|
+
headline: '__APP_TITLE__ is ready.',
|
|
21
|
+
description: 'Header, router, loading bar, and SPA-ready page transitions are already connected.',
|
|
22
|
+
nextStep: 'Add a second route whenever you need it. ASJS will keep the same internal navigation flow.'
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
app.use(asjs.errors());
|
|
26
|
+
|
|
27
|
+
app.listen(port, () => {
|
|
28
|
+
console.log(`__APP_TITLE__ running at http://localhost:${port}`);
|
|
29
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PACKAGE_NAME__",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "ASJS minimal starter generated by npx asjs-express",
|
|
6
|
+
"main": "app.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "node app.js",
|
|
9
|
+
"start": "node app.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"asjs-express": "^__ASJS_VERSION__",
|
|
13
|
+
"express": "__EXPRESS_VERSION__"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title><%= title %></title>
|
|
7
|
+
<%- asjs.clientTags({ preload: true, theme: true }) %>
|
|
8
|
+
</head>
|
|
9
|
+
<body<%- asjs.bodyAttrs() %>>
|
|
10
|
+
<%- asjs.progressMarkup() %>
|
|
11
|
+
<%- asjs.header() %>
|
|
12
|
+
<main class="view-frame"<%- asjs.viewAttrs() %>>
|
|
13
|
+
<%- body %>
|
|
14
|
+
</main>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const { setupAsjs } = require('asjs-express');
|
|
3
|
+
|
|
4
|
+
const app = express();
|
|
5
|
+
const port = process.env.PORT || __PORT__;
|
|
6
|
+
|
|
7
|
+
const asjs = setupAsjs(app, {
|
|
8
|
+
rootDir: __dirname,
|
|
9
|
+
defaultLayout: 'layouts/main',
|
|
10
|
+
navItems: [
|
|
11
|
+
{ href: '/', label: 'Home', activeMode: 'exact' },
|
|
12
|
+
{ href: '/about', label: 'About', activeMode: 'exact' }
|
|
13
|
+
],
|
|
14
|
+
transitions: 'fade',
|
|
15
|
+
prefetch: true,
|
|
16
|
+
loadingBar: true
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const buildPage = asjs.createPageModel({
|
|
20
|
+
pageDescription: '__APP_TITLE__ starter created by ASJS.',
|
|
21
|
+
renderSummary: []
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
app.get('/', asjs.createPageRoute('home', {
|
|
25
|
+
buildPage,
|
|
26
|
+
renderState: {
|
|
27
|
+
delay: 120,
|
|
28
|
+
label: 'Home page ready',
|
|
29
|
+
narrative: 'ASJS prepared the page model before the HTML response reached the browser.'
|
|
30
|
+
}
|
|
31
|
+
}, () => ({
|
|
32
|
+
title: '__APP_TITLE__',
|
|
33
|
+
heroTitle: 'Welcome to __APP_TITLE__.',
|
|
34
|
+
heroText: 'This starter already includes the built-in header, SPA navigation, and a render-state example.'
|
|
35
|
+
})));
|
|
36
|
+
|
|
37
|
+
app.get('/about', asjs.createPageRoute('about', {
|
|
38
|
+
buildPage,
|
|
39
|
+
renderState: {
|
|
40
|
+
delay: 140,
|
|
41
|
+
label: 'About page ready',
|
|
42
|
+
narrative: 'The second route uses the same layout and internal navigation flow.'
|
|
43
|
+
}
|
|
44
|
+
}, () => ({
|
|
45
|
+
title: 'About | __APP_TITLE__',
|
|
46
|
+
heroTitle: 'The starter is ready to grow.',
|
|
47
|
+
heroText: 'Add new routes, layouts, forms, and partials without leaving the ASJS server-rendered flow.'
|
|
48
|
+
})));
|
|
49
|
+
|
|
50
|
+
app.use(asjs.errors());
|
|
51
|
+
|
|
52
|
+
app.listen(port, () => {
|
|
53
|
+
console.log(`__APP_TITLE__ running at http://localhost:${port}`);
|
|
54
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PACKAGE_NAME__",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "ASJS starter app generated by npx asjs-express",
|
|
6
|
+
"main": "app.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "node app.js",
|
|
9
|
+
"start": "node app.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"asjs-express": "^__ASJS_VERSION__",
|
|
13
|
+
"express": "__EXPRESS_VERSION__"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<section style="max-width: 760px; margin: 40px auto; padding: 24px;">
|
|
2
|
+
<span>Starter about</span>
|
|
3
|
+
<h1><%= heroTitle %></h1>
|
|
4
|
+
<p><%= heroText %></p>
|
|
5
|
+
<div style="margin-top: 20px;">
|
|
6
|
+
<% renderSummary.forEach((item) => { %>
|
|
7
|
+
<div><strong><%= item.label %>:</strong> <%= item.value %></div>
|
|
8
|
+
<% }) %>
|
|
9
|
+
</div>
|
|
10
|
+
</section>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<section style="max-width: 760px; margin: 40px auto; padding: 24px;">
|
|
2
|
+
<span>Starter home</span>
|
|
3
|
+
<h1><%= heroTitle %></h1>
|
|
4
|
+
<p><%= heroText %></p>
|
|
5
|
+
<div style="margin-top: 20px;">
|
|
6
|
+
<% renderSummary.forEach((item) => { %>
|
|
7
|
+
<div><strong><%= item.label %>:</strong> <%= item.value %></div>
|
|
8
|
+
<% }) %>
|
|
9
|
+
</div>
|
|
10
|
+
</section>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title><%= title %></title>
|
|
7
|
+
<%- asjs.clientTags({ preload: true, theme: true }) %>
|
|
8
|
+
</head>
|
|
9
|
+
<body<%- asjs.bodyAttrs() %>>
|
|
10
|
+
<%- asjs.progressMarkup() %>
|
|
11
|
+
<%- asjs.header() %>
|
|
12
|
+
<main class="view-frame"<%- asjs.viewAttrs() %>>
|
|
13
|
+
<%- body %>
|
|
14
|
+
</main>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|