@travetto/scaffold 3.0.0-rc.9 → 3.0.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 +7 -8
- package/bin/trv-scaffold.js +16 -2
- package/package.json +6 -4
- package/{support/resources → resources}/templates/todo/listing.json +0 -6
- package/{support/resources → resources}/templates/todo/package.json.txt +11 -4
- package/{support/resources → resources}/templates/todo/resources/application.yml +1 -1
- package/{support/resources → resources}/templates/todo/src/model/todo.ts +2 -2
- package/{support/resources → resources}/templates/todo/src/rest/auth.config.ts +7 -0
- package/{support/resources → resources}/templates/todo/src/rest/todo.ts +23 -22
- package/{support/resources → resources}/templates/todo/test/model/todo.ts +4 -0
- package/resources/templates/todo/tsconfig.json.txt +3 -0
- package/support/bin/context.ts +109 -36
- package/support/bin/features.ts +57 -21
- package/support/cli.scaffold.ts +26 -10
- package/support/.eslintrc.json +0 -14
- package/support/resources/templates/todo/eslintrc.js.txt +0 -1
- package/support/resources/templates/todo/tsconfig.json.txt +0 -3
- /package/{support/resources → resources}/templates/todo/README.md +0 -0
- /package/{support/resources → resources}/templates/todo/gitignore.txt +0 -0
- /package/{support/resources → resources}/templates/todo/src/rest/auth.ts +0 -0
- /package/{support/resources → resources}/templates/todo/src/rest/primary.ts +0 -0
package/README.md
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
# App Scaffold
|
|
4
4
|
## App Scaffold for the Travetto framework
|
|
5
5
|
|
|
6
|
-
**Install: @travetto/scaffold**
|
|
7
|
-
```bash
|
|
8
|
-
npm install @travetto/scaffold
|
|
9
|
-
```
|
|
10
|
-
|
|
11
6
|
A simple tool for scaffolding a reference project. To get started, you need to make sure:
|
|
12
7
|
|
|
13
8
|
**Install: Setting up the necessary config**
|
|
@@ -15,11 +10,15 @@ A simple tool for scaffolding a reference project. To get started, you need to
|
|
|
15
10
|
$ git config --global.username <Username> #Set your git username
|
|
16
11
|
```
|
|
17
12
|
|
|
18
|
-
Once
|
|
13
|
+
Once the necessary configuration is setup, you can invoke the scaffolding by running
|
|
19
14
|
|
|
20
15
|
**Terminal: Running Generator**
|
|
21
16
|
```bash
|
|
22
17
|
$ npx @travetto/scaffold
|
|
18
|
+
|
|
19
|
+
# or
|
|
20
|
+
|
|
21
|
+
$ npx @travetto/scaffold@<version-or-tag>
|
|
23
22
|
```
|
|
24
23
|
|
|
25
24
|
The generator will ask about enabling the following features:
|
|
@@ -37,12 +36,12 @@ The code will establish some basic routes, specifically, `GET /` as the root end
|
|
|
37
36
|
### Additional Rest Features
|
|
38
37
|
In addition to the core functionality, the `rest` feature has some useful sub-features. Specifically:
|
|
39
38
|
|
|
40
|
-
[OpenAPI Specification](https://github.com/travetto/travetto/tree/main/module/openapi#readme "OpenAPI integration support for the
|
|
39
|
+
[OpenAPI Specification](https://github.com/travetto/travetto/tree/main/module/openapi#readme "OpenAPI integration support for the Travetto framework") support for the restful api. This will automatically expose a `openapi.yml` endpoint, and provide the necessary plumbing to support client generation.
|
|
41
40
|
|
|
42
41
|
[Logging](https://github.com/travetto/travetto/tree/main/module/log#readme "Logging framework that integrates at the console.log level.") support for better formatting, [debug](https://www.npmjs.com/package/debug) like support, and colorized output. This is generally useful for server logs, especially during development.
|
|
43
42
|
|
|
44
43
|
## Authentication
|
|
45
|
-
Authentication is also supported on the Restful endpoints by selecting [Rest Auth](https://github.com/travetto/travetto/tree/main/module/auth-rest#readme "Rest authentication integration support for the
|
|
44
|
+
Authentication is also supported on the Restful endpoints by selecting [Rest Auth](https://github.com/travetto/travetto/tree/main/module/auth-rest#readme "Rest authentication integration support for the Travetto framework") during setup. This will support basic authentication running out of local memory, with user [REST Session](https://github.com/travetto/travetto/tree/main/module/rest-session#readme "Session provider for the travetto rest module.")s.
|
|
46
45
|
|
|
47
46
|
## Testing
|
|
48
47
|
[Testing](https://github.com/travetto/travetto/tree/main/module/test#readme "Declarative test framework") can also be configured out of the box to provide simple test cases for the data model.
|
package/bin/trv-scaffold.js
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
(async function () {
|
|
4
|
+
const args = [...process.argv.slice(0, 2), 'scaffold', ...process.argv.slice(2)];
|
|
5
|
+
if (process.env.npm_lifecycle_script === 'trv-scaffold') { // Is npx run
|
|
6
|
+
const path = await import('path');
|
|
7
|
+
const parts = process.env.PATH.split(path.delimiter);
|
|
8
|
+
const loc = parts.find(p => p.includes('npx') && p.includes('.bin'));
|
|
9
|
+
if (loc) {
|
|
10
|
+
const final = loc.split('/node_modules')[0];
|
|
11
|
+
args.push('-c', process.cwd());
|
|
12
|
+
process.chdir(final);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
process.argv = args;
|
|
16
|
+
await import('@travetto/compiler/bin/trv.js');
|
|
17
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/scaffold",
|
|
3
|
-
"version": "3.0.0
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "App Scaffold for the Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"generator",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"bin",
|
|
19
|
-
"support"
|
|
19
|
+
"support",
|
|
20
|
+
"resources"
|
|
20
21
|
],
|
|
21
22
|
"repository": {
|
|
22
23
|
"url": "https://github.com/travetto/travetto.git",
|
|
@@ -26,8 +27,9 @@
|
|
|
26
27
|
"trv-scaffold": "./bin/trv-scaffold.js"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
|
-
"@travetto/base": "^3.0.0
|
|
30
|
-
"@travetto/
|
|
30
|
+
"@travetto/base": "^3.0.0",
|
|
31
|
+
"@travetto/compiler": "^3.0.0",
|
|
32
|
+
"@travetto/cli": "^3.0.0",
|
|
31
33
|
"enquirer": "^2.3.6",
|
|
32
34
|
"mustache": "^4.2.0"
|
|
33
35
|
},
|
|
@@ -4,11 +4,17 @@
|
|
|
4
4
|
"name": "{{{author.name}}}"
|
|
5
5
|
},
|
|
6
6
|
"dependencies": {
|
|
7
|
-
// {{#
|
|
7
|
+
// {{#dependencies}}
|
|
8
8
|
"{{{.}}}": "^{{{frameworkVersion}}}",
|
|
9
|
-
// {{/
|
|
9
|
+
// {{/dependencies}}
|
|
10
10
|
"@travetto/app": "^{{{frameworkVersion}}}",
|
|
11
|
-
"@travetto/cli": "^{{{frameworkVersion}}}"
|
|
11
|
+
"@travetto/cli": "^{{{frameworkVersion}}}"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
// {{#devDependencies}}
|
|
15
|
+
"{{{.}}}": "^{{{frameworkVersion}}}",
|
|
16
|
+
// {{/devDependencies}}
|
|
17
|
+
"@travetto/compiler": "^{{{frameworkVersion}}}",
|
|
12
18
|
"@travetto/pack": "^{{{frameworkVersion}}}"
|
|
13
19
|
},
|
|
14
20
|
"description": "A Travetto-based project",
|
|
@@ -20,10 +26,11 @@
|
|
|
20
26
|
],
|
|
21
27
|
"license": "MIT",
|
|
22
28
|
"scripts": {
|
|
29
|
+
"watch": "npx trv watch",
|
|
23
30
|
"build": "npx trv build",
|
|
24
31
|
"start": "npx trv run rest",
|
|
25
32
|
"test": "npx trv test",
|
|
26
|
-
"lint": "npx
|
|
33
|
+
"lint": "npx trv lint"
|
|
27
34
|
},
|
|
28
35
|
"files": [
|
|
29
36
|
"src",
|
|
@@ -5,7 +5,7 @@ export class Todo implements ModelType {
|
|
|
5
5
|
id: string;
|
|
6
6
|
text: string;
|
|
7
7
|
completed?: boolean;
|
|
8
|
-
// {{#modules.
|
|
8
|
+
// {{#modules.auth_rest}} // @doc-exclude
|
|
9
9
|
userId?: string; // @doc-exclude
|
|
10
|
-
// {{/modules.
|
|
10
|
+
// {{/modules.auth_rest}} // @doc-exclude
|
|
11
11
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Authorizer, Authenticator } from '@travetto/auth';
|
|
2
|
+
import { SessionModelⲐ } from '@travetto/rest-session';
|
|
2
3
|
import { InjectableFactory } from '@travetto/di';
|
|
3
4
|
import { AppError } from '@travetto/base';
|
|
5
|
+
import { ModelExpirySupport, MemoryModelService } from '@travetto/model';
|
|
4
6
|
|
|
5
7
|
export const BasicAuthⲐ = Symbol.for('AUTH_BASIC');
|
|
6
8
|
|
|
@@ -12,6 +14,11 @@ class AuthConfig {
|
|
|
12
14
|
return { authorize: p => p };
|
|
13
15
|
}
|
|
14
16
|
|
|
17
|
+
@InjectableFactory(SessionModelⲐ)
|
|
18
|
+
static getStore(svc: MemoryModelService): ModelExpirySupport {
|
|
19
|
+
return svc;
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
@InjectableFactory(BasicAuthⲐ)
|
|
16
23
|
static getAuthenticator(): Authenticator<User> {
|
|
17
24
|
return {
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { Controller, Get, Put, Post, Delete } from '@travetto/rest';
|
|
2
2
|
import { NotFoundError } from '@travetto/model';
|
|
3
3
|
import { Inject } from '@travetto/di';
|
|
4
|
-
import { ModelQuery
|
|
4
|
+
import { ModelQuery } from '@travetto/model-query';
|
|
5
5
|
import { Schema } from '@travetto/schema';
|
|
6
|
-
// {{#modules.
|
|
6
|
+
// {{#modules.auth_rest}}
|
|
7
7
|
import { Authenticated } from '@travetto/auth-rest';
|
|
8
|
-
// {{/modules.
|
|
9
|
-
// {{#modules.
|
|
8
|
+
// {{/modules.auth_rest}}
|
|
9
|
+
// {{#modules.auth_rest_context}}
|
|
10
10
|
import { AuthContextService } from '@travetto/auth-rest-context';
|
|
11
|
-
// {{/modules.
|
|
11
|
+
// {{/modules.auth_rest_context}}
|
|
12
|
+
import { $_modelService_$ } from '$_modelImport_$';
|
|
12
13
|
|
|
13
14
|
import { Todo } from '../model/todo';
|
|
14
15
|
|
|
15
16
|
@Schema()
|
|
16
17
|
class Query {
|
|
17
18
|
q: {
|
|
18
|
-
// {{#modules.
|
|
19
|
+
// {{#modules.auth_rest_context}}
|
|
19
20
|
userId?: string;
|
|
20
|
-
// {{/modules.
|
|
21
|
+
// {{/modules.auth_rest_context}}
|
|
21
22
|
} = {};
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -25,18 +26,18 @@ class Query {
|
|
|
25
26
|
* Controller for managing all aspects of the Todo lifecycle
|
|
26
27
|
*/
|
|
27
28
|
@Controller('/todo')
|
|
28
|
-
// {{#modules.
|
|
29
|
+
// {{#modules.auth_rest}}
|
|
29
30
|
@Authenticated()
|
|
30
|
-
// {{/modules.
|
|
31
|
+
// {{/modules.auth_rest}}
|
|
31
32
|
export class TodoController {
|
|
32
33
|
|
|
33
34
|
@Inject()
|
|
34
|
-
source:
|
|
35
|
+
source: $_modelService_$;
|
|
35
36
|
|
|
36
|
-
// {{#modules.
|
|
37
|
+
// {{#modules.auth_rest_context}}
|
|
37
38
|
@Inject()
|
|
38
39
|
auth: AuthContextService;
|
|
39
|
-
// {{/modules.
|
|
40
|
+
// {{/modules.auth_rest_context}}
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Get all Todos
|
|
@@ -44,9 +45,9 @@ export class TodoController {
|
|
|
44
45
|
@Get('/')
|
|
45
46
|
async getAll(query: Query): Promise<Todo[]> {
|
|
46
47
|
query.q ??= {};
|
|
47
|
-
// {{#modules.
|
|
48
|
+
// {{#modules.auth_rest_context}}
|
|
48
49
|
query.q.userId = this.auth.get()?.id;
|
|
49
|
-
// {{/modules.
|
|
50
|
+
// {{/modules.auth_rest_context}}
|
|
50
51
|
return this.source.query(Todo, { where: query.q });
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -56,11 +57,11 @@ export class TodoController {
|
|
|
56
57
|
@Get('/:id')
|
|
57
58
|
async getOne(id: string): Promise<Todo> {
|
|
58
59
|
const q: ModelQuery<Todo> = { where: { id } };
|
|
59
|
-
// {{#modules.
|
|
60
|
+
// {{#modules.auth_rest_context}}
|
|
60
61
|
if (typeof q.where !== 'string') {
|
|
61
62
|
q.where!.userId = this.auth.get()?.id;
|
|
62
63
|
}
|
|
63
|
-
// {{/modules.
|
|
64
|
+
// {{/modules.auth_rest_context}}
|
|
64
65
|
return this.source.queryOne(Todo, q);
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -69,9 +70,9 @@ export class TodoController {
|
|
|
69
70
|
*/
|
|
70
71
|
@Post('/')
|
|
71
72
|
async save(todo: Todo): Promise<Todo> {
|
|
72
|
-
// {{#modules.
|
|
73
|
+
// {{#modules.auth_rest_context}}
|
|
73
74
|
todo.userId = this.auth.get()?.id;
|
|
74
|
-
// {{/modules.
|
|
75
|
+
// {{/modules.auth_rest_context}}
|
|
75
76
|
return this.source.create(Todo, todo);
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -80,9 +81,9 @@ export class TodoController {
|
|
|
80
81
|
*/
|
|
81
82
|
@Put('/:id')
|
|
82
83
|
async update(todo: Todo): Promise<Todo> {
|
|
83
|
-
// {{#modules.
|
|
84
|
+
// {{#modules.auth_rest_context}}
|
|
84
85
|
todo.userId = this.auth.get()?.id;
|
|
85
|
-
// {{/modules.
|
|
86
|
+
// {{/modules.auth_rest_context}}
|
|
86
87
|
return this.source.update(Todo, todo);
|
|
87
88
|
}
|
|
88
89
|
|
|
@@ -92,11 +93,11 @@ export class TodoController {
|
|
|
92
93
|
@Delete('/:id')
|
|
93
94
|
async remove(id: string): Promise<void> {
|
|
94
95
|
const q: ModelQuery<Todo> = { where: { id } };
|
|
95
|
-
// {{#modules.
|
|
96
|
+
// {{#modules.auth_rest_context}}
|
|
96
97
|
if (typeof q.where !== 'string') {
|
|
97
98
|
q.where!.userId = this.auth.get()?.id;
|
|
98
99
|
}
|
|
99
|
-
// {{/modules.
|
|
100
|
+
// {{/modules.auth_rest_context}}
|
|
100
101
|
if (await this.source.deleteByQuery(Todo, q) !== 1) {
|
|
101
102
|
throw new NotFoundError(Todo, id);
|
|
102
103
|
}
|
|
@@ -3,12 +3,16 @@ import assert from 'assert';
|
|
|
3
3
|
import { Suite, Test } from '@travetto/test';
|
|
4
4
|
import { ModelCrudSupport } from '@travetto/model';
|
|
5
5
|
import { BaseModelSuite } from '@travetto/model/support/test/base';
|
|
6
|
+
import { $_modelConfig_$, $_modelService_$ } from '$_modelImport_$';
|
|
6
7
|
|
|
7
8
|
import { Todo } from '../../src/model/todo';
|
|
8
9
|
|
|
9
10
|
@Suite('Simple CRUD')
|
|
10
11
|
class TestCRUD extends BaseModelSuite<ModelCrudSupport> {
|
|
11
12
|
|
|
13
|
+
serviceClass = $_modelService_$;
|
|
14
|
+
configClass = $_modelConfig_$;
|
|
15
|
+
|
|
12
16
|
@Test('save it')
|
|
13
17
|
async save(): Promise<void> {
|
|
14
18
|
const svc = await this.service;
|
package/support/bin/context.ts
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import mustache from 'mustache';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { cliTpl } from '@travetto/cli';
|
|
5
|
+
import { ManifestContext, path, RootIndex } from '@travetto/manifest';
|
|
5
6
|
import { ExecUtil, ExecutionResult } from '@travetto/base';
|
|
7
|
+
import { GlobalTerminal } from '@travetto/terminal';
|
|
6
8
|
|
|
7
9
|
import { Feature } from './features';
|
|
8
10
|
|
|
9
11
|
type ListingEntry = { requires?: string[], rename?: string };
|
|
10
12
|
type Listing = Record<string, ListingEntry>;
|
|
11
13
|
|
|
14
|
+
const DEV_DEPS = new Set([
|
|
15
|
+
'@travetto/test',
|
|
16
|
+
'@travetto/pack',
|
|
17
|
+
'@travetto/compiler',
|
|
18
|
+
'@travetto/command',
|
|
19
|
+
'@travetto/transformer',
|
|
20
|
+
'@travetto/eslint',
|
|
21
|
+
]);
|
|
22
|
+
|
|
12
23
|
export class Context {
|
|
13
24
|
|
|
14
25
|
static #meetsRequirement(modules: string[], desired: string[]): boolean {
|
|
15
26
|
let valid = true;
|
|
16
27
|
for (const mod of desired) {
|
|
17
|
-
|
|
18
|
-
valid = valid && !!modules.find(m => m.startsWith(mod));
|
|
19
|
-
} else {
|
|
20
|
-
valid = valid && modules.includes(mod);
|
|
21
|
-
}
|
|
28
|
+
valid = valid && modules.includes(mod);
|
|
22
29
|
if (!valid) {
|
|
23
30
|
break;
|
|
24
31
|
}
|
|
@@ -28,40 +35,38 @@ export class Context {
|
|
|
28
35
|
|
|
29
36
|
#template: string;
|
|
30
37
|
#targetDir: string;
|
|
31
|
-
#
|
|
38
|
+
#dependencies: string[] = [];
|
|
39
|
+
#devDependencies: string[] = [];
|
|
32
40
|
#peerDependencies: string[] = [];
|
|
33
|
-
#
|
|
41
|
+
#featureContexts: Record<string, unknown>[] = [];
|
|
42
|
+
|
|
43
|
+
packageManager: ManifestContext['packageManager'] = 'npm';
|
|
34
44
|
|
|
35
45
|
readonly name: string;
|
|
36
|
-
readonly frameworkVersion = RootIndex.mainDigest().framework.replace(/[.]\d+$/, '.0');
|
|
37
46
|
|
|
38
47
|
constructor(name: string, template: string, targetDir: string) {
|
|
39
|
-
this.name = name;
|
|
48
|
+
this.name = name.replace(/[^a-zA-Z0-9]+/, '-').replace(/-+$/, '');
|
|
40
49
|
this.#template = template;
|
|
41
50
|
this.#targetDir = path.resolve(targetDir);
|
|
42
51
|
}
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
get frameworkDependencies(): string[] {
|
|
52
|
-
return this.#frameworkDependencies;
|
|
53
|
-
}
|
|
53
|
+
#exec(cmd: string, args: string[]): Promise<ExecutionResult> {
|
|
54
|
+
const res = ExecUtil.spawn(cmd, args, {
|
|
55
|
+
cwd: this.destination(),
|
|
56
|
+
stdio: [0, 'pipe', 'pipe'],
|
|
57
|
+
isolatedEnv: true,
|
|
58
|
+
onStdErrorLine: line => GlobalTerminal.writeLines(cliTpl` ${{ identifier: [cmd, ...args].join(' ') }}: ${line}`)
|
|
59
|
+
}).result;
|
|
54
60
|
|
|
55
|
-
|
|
56
|
-
return this.#peerDependencies;
|
|
61
|
+
return res;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
get
|
|
60
|
-
return
|
|
64
|
+
get selfPath(): string {
|
|
65
|
+
return RootIndex.getModule('@travetto/scaffold')!.sourcePath;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
source(file?: string): string {
|
|
64
|
-
return path.resolve('resources', 'templates', this.#template, ...file ? [file] : []);
|
|
69
|
+
return path.resolve(this.selfPath, 'resources', 'templates', this.#template, ...file ? [file] : []);
|
|
65
70
|
}
|
|
66
71
|
|
|
67
72
|
destination(file?: string): string {
|
|
@@ -73,8 +78,11 @@ export class Context {
|
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
async resolvedSourceListing(): Promise<[string, ListingEntry][]> {
|
|
76
|
-
|
|
77
|
-
.filter(([, conf]) => !conf.requires ||
|
|
81
|
+
const res = Object.entries(await this.sourceListing)
|
|
82
|
+
.filter(([, conf]) => !conf.requires ||
|
|
83
|
+
Context.#meetsRequirement([...this.#dependencies, ...this.#devDependencies], conf.requires));
|
|
84
|
+
|
|
85
|
+
return res;
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
async initialize(): Promise<void> {
|
|
@@ -91,10 +99,35 @@ export class Context {
|
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
101
|
|
|
102
|
+
templateContext(): Record<string, unknown> {
|
|
103
|
+
const modules = [...this.#dependencies, ...this.#devDependencies]
|
|
104
|
+
.map(x => path.basename(x)).reduce((acc, v) => ({ ...acc, [v.replace(/[-]/g, '_')]: true }), {});
|
|
105
|
+
const moduleNames = [...Object.keys(modules)];
|
|
106
|
+
|
|
107
|
+
const context = Object.assign({
|
|
108
|
+
frameworkVersion: RootIndex.mainDigest().framework.replace(/[.]\d+$/, '.0'),
|
|
109
|
+
name: this.name,
|
|
110
|
+
modules,
|
|
111
|
+
moduleNames,
|
|
112
|
+
dependencies: [...new Set(this.#dependencies)].sort((a, b) => a.localeCompare(b)),
|
|
113
|
+
devDependencies: [...new Set(this.#devDependencies)].sort((a, b) => a.localeCompare(b)),
|
|
114
|
+
}, ...this.#featureContexts);
|
|
115
|
+
|
|
116
|
+
return context;
|
|
117
|
+
}
|
|
118
|
+
|
|
94
119
|
async template(file: string, { rename }: ListingEntry): Promise<void> {
|
|
95
120
|
const contents = await fs.readFile(this.source(file), 'utf-8');
|
|
96
121
|
const out = this.destination(rename ?? file);
|
|
97
|
-
const rendered = mustache.render(
|
|
122
|
+
const rendered = mustache.render(
|
|
123
|
+
contents
|
|
124
|
+
.replaceAll('$_', '{{{')
|
|
125
|
+
.replaceAll('_$', '}}}'),
|
|
126
|
+
this.templateContext(),
|
|
127
|
+
)
|
|
128
|
+
.replace(/(\/\/.*@doc-exclude.*)$/g, '')
|
|
129
|
+
.replace(/\s*(\/\/.*@doc-exclude.*)/g, '')
|
|
130
|
+
.replace(/^\s*(\/\/\s*)\n/gsm, '');
|
|
98
131
|
await fs.mkdir(path.dirname(out), { recursive: true });
|
|
99
132
|
await fs.writeFile(out, rendered, 'utf8');
|
|
100
133
|
}
|
|
@@ -105,19 +138,59 @@ export class Context {
|
|
|
105
138
|
}
|
|
106
139
|
}
|
|
107
140
|
|
|
108
|
-
async
|
|
109
|
-
if (feat.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
141
|
+
async resolveFeature(feat: Feature): Promise<void> {
|
|
142
|
+
if (feat.package) {
|
|
143
|
+
if (feat.package.startsWith('@travetto')) {
|
|
144
|
+
if (DEV_DEPS.has(feat.package)) {
|
|
145
|
+
this.#devDependencies.push(feat.package);
|
|
146
|
+
} else {
|
|
147
|
+
this.#dependencies.push(feat.package);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
this.#peerDependencies.push(feat.package);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (feat.field) {
|
|
154
|
+
// @ts-expect-error
|
|
155
|
+
this[feat.field] = feat.value!;
|
|
156
|
+
}
|
|
157
|
+
if (feat.context) {
|
|
158
|
+
this.#featureContexts.push(feat.context);
|
|
113
159
|
}
|
|
114
160
|
|
|
115
161
|
for (const addon of (feat.addons ?? [])) {
|
|
116
|
-
this.
|
|
162
|
+
this.resolveFeature(addon);
|
|
117
163
|
}
|
|
118
164
|
}
|
|
119
165
|
|
|
120
|
-
|
|
121
|
-
|
|
166
|
+
async * install(): AsyncIterable<string | undefined> {
|
|
167
|
+
|
|
168
|
+
yield cliTpl`${{
|
|
169
|
+
type: 'Templating files'
|
|
170
|
+
}}`;
|
|
171
|
+
await this.templateResolvedFiles();
|
|
172
|
+
|
|
173
|
+
yield cliTpl`${{ type: 'Installing dependencies' }} `;
|
|
174
|
+
switch (this.packageManager) {
|
|
175
|
+
case 'npm': await this.#exec('npm', ['i']); break;
|
|
176
|
+
case 'yarn': await this.#exec('yarn', []); break;
|
|
177
|
+
default: throw new Error(`Unknown package manager: ${this.packageManager} `);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
yield cliTpl`${{ type: 'Ensuring latest dependencies' }} `;
|
|
181
|
+
switch (this.packageManager) {
|
|
182
|
+
case 'npm': await this.#exec('npm', ['update', '-S']); break;
|
|
183
|
+
case 'yarn': await this.#exec('yarn', ['upgrade']); break;
|
|
184
|
+
default: throw new Error(`Unknown package manager: ${this.packageManager} `);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
yield cliTpl`${{ type: 'Initial Build' }} `;
|
|
188
|
+
await this.#exec('npx', ['trv', 'build']);
|
|
189
|
+
if (this.#devDependencies.includes('@travetto/eslint')) {
|
|
190
|
+
yield cliTpl`${{ type: 'ESLint Registration' }} `;
|
|
191
|
+
await this.#exec('npx', ['trv', 'lint:register']);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
yield cliTpl`${{ success: 'Successfully created' }} at ${{ path: this.#targetDir }} `;
|
|
122
195
|
}
|
|
123
196
|
}
|
package/support/bin/features.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export type Feature = {
|
|
2
2
|
title?: string;
|
|
3
|
-
|
|
3
|
+
package?: string;
|
|
4
|
+
field?: string;
|
|
5
|
+
value?: string;
|
|
6
|
+
required?: boolean;
|
|
4
7
|
version?: string;
|
|
5
8
|
addons?: Feature[];
|
|
6
9
|
choices?: Feature[];
|
|
@@ -10,42 +13,75 @@ export type Feature = {
|
|
|
10
13
|
};
|
|
11
14
|
|
|
12
15
|
export const FEATURES: Feature[] = [
|
|
16
|
+
{
|
|
17
|
+
title: 'Package Manager',
|
|
18
|
+
choices: [
|
|
19
|
+
{ title: 'NPM', field: 'packageManager', value: 'npm' },
|
|
20
|
+
{ title: 'Yarn', field: 'packageManager', value: 'yarn' }
|
|
21
|
+
],
|
|
22
|
+
required: true,
|
|
23
|
+
default: 'npm'
|
|
24
|
+
},
|
|
13
25
|
{
|
|
14
26
|
title: 'Rest Framework',
|
|
15
|
-
|
|
27
|
+
package: '@travetto/rest',
|
|
16
28
|
choices: [
|
|
17
|
-
{ title: 'Express.js',
|
|
18
|
-
{ title: 'Express.js Lambda',
|
|
19
|
-
{ title: 'KOA',
|
|
20
|
-
{ title: 'KOA Lambda',
|
|
21
|
-
{ title: 'Fastify',
|
|
22
|
-
{ title: 'Fastify Lambda',
|
|
29
|
+
{ title: 'Express.js', package: '@travetto/rest-express' },
|
|
30
|
+
{ title: 'Express.js Lambda', package: '@travetto/rest-express-lambda' },
|
|
31
|
+
{ title: 'KOA', package: '@travetto/rest-koa' },
|
|
32
|
+
{ title: 'KOA Lambda', package: '@travetto/rest-koa-lambda' },
|
|
33
|
+
{ title: 'Fastify', package: '@travetto/rest-fastify' },
|
|
34
|
+
{ title: 'Fastify Lambda', package: '@travetto/rest-fastify-lambda' },
|
|
23
35
|
],
|
|
24
36
|
addons: [
|
|
25
|
-
{
|
|
26
|
-
{ title: '
|
|
37
|
+
{ package: '@travetto/auth-rest-session' },
|
|
38
|
+
{ title: 'OpenAPI', package: '@travetto/openapi' },
|
|
39
|
+
{ title: 'Logging', package: '@travetto/log' }
|
|
27
40
|
],
|
|
28
41
|
default: 'Express.js'
|
|
29
42
|
},
|
|
30
|
-
{ title: 'Test Framework',
|
|
31
|
-
{ title: 'ESLint Support',
|
|
43
|
+
{ title: 'Test Framework', package: '@travetto/test' },
|
|
44
|
+
{ title: 'ESLint Support', package: '@travetto/eslint' },
|
|
32
45
|
{
|
|
33
46
|
title: 'Rest Authentication',
|
|
34
|
-
|
|
47
|
+
package: '@travetto/auth-rest',
|
|
35
48
|
addons: [
|
|
36
|
-
{
|
|
37
|
-
|
|
49
|
+
{
|
|
50
|
+
title: 'Rest Session', package: '@travetto/rest-session', addons: [
|
|
51
|
+
{ package: '@travetto/auth-rest-session' },
|
|
52
|
+
{ package: '@travetto/auth-rest-context' }
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
{ title: 'Context', package: '@travetto/auth-rest-session' }
|
|
38
56
|
]
|
|
39
57
|
},
|
|
40
58
|
{
|
|
41
59
|
title: 'Data Modelling',
|
|
42
|
-
|
|
60
|
+
package: '@travetto/model',
|
|
43
61
|
choices: [
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
{
|
|
62
|
+
{
|
|
63
|
+
title: 'Elasticsearch', package: '@travetto/model-elasticsearch',
|
|
64
|
+
context: { modelService: 'ElasticsearchModelService', modelConfig: 'ElasticsearchModelConfig', modelImport: '@travetto/model-elasticsearch' }
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
title: 'MongoDB', package: '@travetto/model-mongo',
|
|
68
|
+
context: { modelService: 'MongoModelService', modelConfig: 'MongoModelConfig', modelImport: '@travetto/model-mongo' }
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
title: 'MySQL', package: '@travetto/model-mysql',
|
|
72
|
+
context: { modelService: 'SQLModelService', modelConfig: 'SQLModelConfig', modelImport: '@travetto/model-sql' }
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
title: 'PostgreSQL', package: '@travetto/model-postgres',
|
|
76
|
+
context: { modelService: 'SQLModelService', modelConfig: 'SQLModelConfig', modelImport: '@travetto/model-sql' }
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
title: 'SQLite', package: '@travetto/model-sqlite',
|
|
80
|
+
context: { modelService: 'SQLModelService', modelConfig: 'SQLModelConfig', modelImport: '@travetto/model-sql' }
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
addons: [
|
|
84
|
+
{ title: 'Command Support', package: '@travetto/command' }
|
|
49
85
|
],
|
|
50
86
|
default: 'MongoDB'
|
|
51
87
|
},
|
package/support/cli.scaffold.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import enquirer from 'enquirer';
|
|
2
2
|
|
|
3
3
|
import { path } from '@travetto/manifest';
|
|
4
|
-
import { CliCommand, OptionConfig } from '@travetto/cli';
|
|
4
|
+
import { CliCommand, cliTpl, OptionConfig } from '@travetto/cli';
|
|
5
|
+
import { GlobalTerminal } from '@travetto/terminal';
|
|
5
6
|
|
|
6
7
|
import { Context } from './bin/context';
|
|
7
8
|
import { Feature, FEATURES } from './bin/features';
|
|
8
9
|
|
|
9
10
|
type Options = {
|
|
10
11
|
template: OptionConfig<string>;
|
|
12
|
+
cwd: OptionConfig<string>;
|
|
11
13
|
dir: OptionConfig<string>;
|
|
12
14
|
force: OptionConfig<boolean>;
|
|
13
15
|
};
|
|
@@ -21,6 +23,7 @@ export class ScaffoldCommand extends CliCommand<Options> {
|
|
|
21
23
|
getOptions(): Options {
|
|
22
24
|
return {
|
|
23
25
|
template: this.option({ def: 'todo', desc: 'Template' }),
|
|
26
|
+
cwd: this.option({ desc: 'Current Working Directory override' }),
|
|
24
27
|
dir: this.option({ desc: 'Target Directory' }),
|
|
25
28
|
force: this.boolOption({ desc: 'Force writing into an existing directory', def: false })
|
|
26
29
|
};
|
|
@@ -59,7 +62,7 @@ export class ScaffoldCommand extends CliCommand<Options> {
|
|
|
59
62
|
|
|
60
63
|
async * #resolveFeatures(features: Feature[], chosen = false): AsyncGenerator<Feature> {
|
|
61
64
|
for (const feat of features) {
|
|
62
|
-
if (!chosen) {
|
|
65
|
+
if (!chosen && !feat.required) {
|
|
63
66
|
const ans = await enquirer.prompt<{ choice: boolean | string }>([{
|
|
64
67
|
type: 'confirm',
|
|
65
68
|
name: 'choice',
|
|
@@ -95,17 +98,26 @@ export class ScaffoldCommand extends CliCommand<Options> {
|
|
|
95
98
|
return this.exit(1);
|
|
96
99
|
}
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
)
|
|
101
|
+
this.cmd.cwd ??= path.cwd();
|
|
102
|
+
|
|
103
|
+
if (!name && this.cmd.dir) {
|
|
104
|
+
name = path.basename(this.cmd.dir);
|
|
105
|
+
} else if (name && !this.cmd.dir) {
|
|
106
|
+
this.cmd.dir = path.resolve(this.cmd.cwd, name);
|
|
107
|
+
} else if (!name && !this.cmd.dir) {
|
|
108
|
+
console.error('Either a name or a target directory are required');
|
|
109
|
+
return this.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const ctx = new Context(name, this.cmd.template, path.resolve(this.cmd.cwd, this.cmd.dir));
|
|
101
113
|
|
|
102
114
|
if (!this.cmd.force) {
|
|
103
115
|
await ctx.initialize();
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
try {
|
|
107
|
-
for await (const
|
|
108
|
-
await ctx.
|
|
119
|
+
for await (const feature of this.#resolveFeatures(FEATURES)) {
|
|
120
|
+
await ctx.resolveFeature(feature);
|
|
109
121
|
}
|
|
110
122
|
} catch (err) {
|
|
111
123
|
if (err instanceof Error) {
|
|
@@ -114,9 +126,13 @@ export class ScaffoldCommand extends CliCommand<Options> {
|
|
|
114
126
|
return this.exit(1);
|
|
115
127
|
}
|
|
116
128
|
|
|
117
|
-
|
|
129
|
+
console.log(cliTpl`\n${{ title: 'Creating Application' }}\n${'-'.repeat(30)}`);
|
|
118
130
|
|
|
119
|
-
await ctx.
|
|
120
|
-
|
|
131
|
+
await GlobalTerminal.streamLinesWithWaiting(ctx.install(), {
|
|
132
|
+
position: 'inline',
|
|
133
|
+
end: false,
|
|
134
|
+
committedPrefix: '>',
|
|
135
|
+
cycleDelay: 100
|
|
136
|
+
});
|
|
121
137
|
}
|
|
122
138
|
}
|
package/support/.eslintrc.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require('@travetto/eslint-plugin/bin/init');
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|