@travetto/scaffold 7.0.0-rc.1 → 7.0.0-rc.3
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 +6 -6
- package/bin/trv-scaffold.js +9 -9
- package/package.json +4 -4
- package/resources/templates/todo/src/web/auth.config.ts +6 -6
- package/resources/templates/todo/src/web/todo.ts +8 -8
- package/resources/templates/todo/test/model/todo.ts +2 -2
- package/support/bin/context.ts +22 -21
- package/support/cli.scaffold.ts +17 -13
package/README.md
CHANGED
|
@@ -111,10 +111,10 @@ export class TodoController {
|
|
|
111
111
|
*/
|
|
112
112
|
@Get('/:id')
|
|
113
113
|
async getOne(id: string): Promise<Todo> {
|
|
114
|
-
const
|
|
115
|
-
if (typeof
|
|
114
|
+
const query: ModelQuery<Todo> = { where: { id } };
|
|
115
|
+
if (typeof query.where !== 'string') {
|
|
116
116
|
}
|
|
117
|
-
return this.source.queryOne(Todo,
|
|
117
|
+
return this.source.queryOne(Todo, query);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/**
|
|
@@ -138,10 +138,10 @@ export class TodoController {
|
|
|
138
138
|
*/
|
|
139
139
|
@Delete('/:id')
|
|
140
140
|
async remove(id: string): Promise<void> {
|
|
141
|
-
const
|
|
142
|
-
if (typeof
|
|
141
|
+
const query: ModelQuery<Todo> = { where: { id } };
|
|
142
|
+
if (typeof query.where !== 'string') {
|
|
143
143
|
}
|
|
144
|
-
if (await this.source.deleteByQuery(Todo,
|
|
144
|
+
if (await this.source.deleteByQuery(Todo, query) !== 1) {
|
|
145
145
|
throw new NotFoundError(Todo, id);
|
|
146
146
|
}
|
|
147
147
|
}
|
package/bin/trv-scaffold.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @ts-check
|
|
3
3
|
|
|
4
|
-
async function
|
|
4
|
+
async function getModuleDirectory() {
|
|
5
5
|
if (process.env.npm_lifecycle_script?.includes('trv-scaffold')) { // Is npx run
|
|
6
6
|
const { delimiter } = await import('node:path');
|
|
7
7
|
const parts = process.env.PATH?.split(delimiter) ?? [];
|
|
8
|
-
const loc = parts.find(
|
|
8
|
+
const loc = parts.find(part => part.includes('npx') && part.includes('.bin'));
|
|
9
9
|
if (loc) {
|
|
10
10
|
return loc.split('/node_modules')[0];
|
|
11
11
|
}
|
|
@@ -14,22 +14,22 @@ async function getScaffoldCwd() {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* @param {string}
|
|
17
|
+
* @param {string} workingDirectory
|
|
18
18
|
*/
|
|
19
|
-
async function getVersion(
|
|
19
|
+
async function getVersion(workingDirectory) {
|
|
20
20
|
const fs = await import('node:fs/promises');
|
|
21
|
-
const pkg = JSON.parse(await fs.readFile(`${
|
|
21
|
+
const pkg = JSON.parse(await fs.readFile(`${workingDirectory}/package.json`, 'utf8'));
|
|
22
22
|
const version = `${pkg.dependencies['@travetto/scaffold']}`.replace(/\d+$/, '0');
|
|
23
23
|
return version;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
(async function () {
|
|
27
|
-
const
|
|
28
|
-
const version = await getVersion(
|
|
27
|
+
const workingDirectory = await getModuleDirectory();
|
|
28
|
+
const version = await getVersion(workingDirectory);
|
|
29
29
|
|
|
30
30
|
const { spawn, execSync } = await import('node:child_process');
|
|
31
31
|
// Ensure we install the compiler first
|
|
32
|
-
execSync(`npm i @travetto/compiler@${version}`, { stdio: 'pipe', cwd:
|
|
32
|
+
execSync(`npm i @travetto/compiler@${version}`, { stdio: 'pipe', cwd: workingDirectory });
|
|
33
33
|
|
|
34
34
|
spawn('npx', [
|
|
35
35
|
'trvc', 'exec',
|
|
@@ -39,7 +39,7 @@ async function getVersion(cwd) {
|
|
|
39
39
|
...process.argv.slice(2)
|
|
40
40
|
], {
|
|
41
41
|
stdio: 'inherit',
|
|
42
|
-
cwd:
|
|
42
|
+
cwd: workingDirectory,
|
|
43
43
|
env: {
|
|
44
44
|
...process.env,
|
|
45
45
|
TRV_QUIET: '1',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/scaffold",
|
|
3
|
-
"version": "7.0.0-rc.
|
|
3
|
+
"version": "7.0.0-rc.3",
|
|
4
4
|
"description": "App Scaffold for the Travetto framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"generator",
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
"trv-scaffold": "bin/trv-scaffold.js"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/cli": "^7.0.0-rc.
|
|
31
|
-
"@travetto/runtime": "^7.0.0-rc.
|
|
30
|
+
"@travetto/cli": "^7.0.0-rc.3",
|
|
31
|
+
"@travetto/runtime": "^7.0.0-rc.3",
|
|
32
32
|
"enquirer": "^2.4.1",
|
|
33
33
|
"mustache": "^4.2.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@travetto/model": "^7.0.0-rc.
|
|
36
|
+
"@travetto/model": "^7.0.0-rc.3",
|
|
37
37
|
"@types/mustache": "^4.2.6"
|
|
38
38
|
},
|
|
39
39
|
"travetto": {
|
|
@@ -11,22 +11,22 @@ type User = { username: string, password: string };
|
|
|
11
11
|
class AuthConfig {
|
|
12
12
|
@InjectableFactory()
|
|
13
13
|
static getAuthorizer(): Authorizer { // Simply mirrors the identity back as the principal
|
|
14
|
-
return { authorize:
|
|
14
|
+
return { authorize: principal => principal };
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
@InjectableFactory(SessionModelSymbol)
|
|
18
|
-
static getStore(
|
|
19
|
-
return
|
|
18
|
+
static getStore(service: MemoryModelService): ModelExpirySupport {
|
|
19
|
+
return service;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
@InjectableFactory(BasicAuthSymbol)
|
|
23
23
|
static getAuthenticator(): Authenticator<User> {
|
|
24
24
|
return {
|
|
25
|
-
authenticate(
|
|
26
|
-
if (
|
|
25
|
+
authenticate(user) {
|
|
26
|
+
if (user.username && user.password === 'password') {
|
|
27
27
|
return {
|
|
28
28
|
issuer: 'self',
|
|
29
|
-
id:
|
|
29
|
+
id: user.username,
|
|
30
30
|
permissions: [],
|
|
31
31
|
details: {},
|
|
32
32
|
source: 'insecure'
|
|
@@ -57,13 +57,13 @@ export class TodoController {
|
|
|
57
57
|
*/
|
|
58
58
|
@Get('/:id')
|
|
59
59
|
async getOne(id: string): Promise<Todo> {
|
|
60
|
-
const
|
|
60
|
+
const query: ModelQuery<Todo> = { where: { id } };
|
|
61
61
|
// {{#modules.auth_web}}
|
|
62
|
-
if (typeof
|
|
63
|
-
|
|
62
|
+
if (typeof query.where !== 'string') {
|
|
63
|
+
query.where!.userId = this.authContext.principal?.id;
|
|
64
64
|
}
|
|
65
65
|
// {{/modules.auth_web}}
|
|
66
|
-
return this.source.queryOne(Todo,
|
|
66
|
+
return this.source.queryOne(Todo, query);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|
|
@@ -93,13 +93,13 @@ export class TodoController {
|
|
|
93
93
|
*/
|
|
94
94
|
@Delete('/:id')
|
|
95
95
|
async remove(id: string): Promise<void> {
|
|
96
|
-
const
|
|
96
|
+
const query: ModelQuery<Todo> = { where: { id } };
|
|
97
97
|
// {{#modules.auth_web}}
|
|
98
|
-
if (typeof
|
|
99
|
-
|
|
98
|
+
if (typeof query.where !== 'string') {
|
|
99
|
+
query.where!.userId = this.authContext.principal?.id;
|
|
100
100
|
}
|
|
101
101
|
// {{/modules.auth_web}}
|
|
102
|
-
if (await this.source.deleteByQuery(Todo,
|
|
102
|
+
if (await this.source.deleteByQuery(Todo, query) !== 1) {
|
|
103
103
|
throw new NotFoundError(Todo, id);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -15,9 +15,9 @@ class TestCRUD extends BaseModelSuite<ModelCrudSupport> {
|
|
|
15
15
|
|
|
16
16
|
@Test('save it')
|
|
17
17
|
async save(): Promise<void> {
|
|
18
|
-
const
|
|
18
|
+
const service = await this.service;
|
|
19
19
|
|
|
20
|
-
const saved = await
|
|
20
|
+
const saved = await service.create(Todo, Todo.from({
|
|
21
21
|
text: 'A saved todo',
|
|
22
22
|
completed: false
|
|
23
23
|
}));
|
package/support/bin/context.ts
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
|
|
5
5
|
import mustache from 'mustache';
|
|
6
6
|
|
|
7
|
-
import { castKey, castTo, ExecUtil, RuntimeIndex } from '@travetto/runtime';
|
|
7
|
+
import { castKey, castTo, ExecUtil, JSONUtil, RuntimeIndex } from '@travetto/runtime';
|
|
8
8
|
import { cliTpl } from '@travetto/cli';
|
|
9
9
|
import { NodePackageManager, PackageUtil } from '@travetto/manifest';
|
|
10
10
|
import { Terminal } from '@travetto/terminal';
|
|
@@ -14,7 +14,7 @@ import { Feature } from './features.ts';
|
|
|
14
14
|
type ListingEntry = { requires?: string[], rename?: string };
|
|
15
15
|
type Listing = Record<string, ListingEntry>;
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const DEV_DEPENDENCIES = new Set([
|
|
18
18
|
'@travetto/test',
|
|
19
19
|
'@travetto/pack',
|
|
20
20
|
'@travetto/compiler',
|
|
@@ -36,7 +36,7 @@ export class Context {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
#template: string;
|
|
39
|
-
#
|
|
39
|
+
#targetDirectory: string;
|
|
40
40
|
#dependencies: string[] = [];
|
|
41
41
|
#devDependencies: string[] = [];
|
|
42
42
|
#peerDependencies: string[] = [];
|
|
@@ -46,26 +46,26 @@ export class Context {
|
|
|
46
46
|
|
|
47
47
|
readonly name: string;
|
|
48
48
|
|
|
49
|
-
constructor(name: string, template: string,
|
|
49
|
+
constructor(name: string, template: string, targetDirectory: string) {
|
|
50
50
|
this.name = name.replace(/[^a-zA-Z0-9]+/, '-').replace(/-+$/, '');
|
|
51
51
|
this.#template = template;
|
|
52
|
-
this.#
|
|
52
|
+
this.#targetDirectory = path.resolve(targetDirectory);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
#exec(cmd: string, args: string[]): Promise<void> {
|
|
56
|
-
const
|
|
57
|
-
const
|
|
56
|
+
const terminal = new Terminal();
|
|
57
|
+
const subProcess = spawn(cmd, args, {
|
|
58
58
|
cwd: this.destination(),
|
|
59
59
|
stdio: [0, 'pipe', 'pipe'],
|
|
60
60
|
env: { PATH: process.env.PATH },
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
if (
|
|
64
|
-
ExecUtil.readLines(
|
|
65
|
-
line =>
|
|
63
|
+
if (subProcess.stderr) {
|
|
64
|
+
ExecUtil.readLines(subProcess.stderr,
|
|
65
|
+
line => terminal.writer.writeLine(cliTpl` ${{ identifier: [cmd, ...args].join(' ') }}: ${line.trimEnd()}`).commit());
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
return ExecUtil.getResult(
|
|
68
|
+
return ExecUtil.getResult(subProcess).then(() => { });
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
get selfPath(): string {
|
|
@@ -77,19 +77,19 @@ export class Context {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
destination(file?: string): string {
|
|
80
|
-
return path.resolve(this.#
|
|
80
|
+
return path.resolve(this.#targetDirectory, ...file ? [file] : []);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
get sourceListing(): Promise<Listing> {
|
|
84
|
-
return
|
|
84
|
+
return JSONUtil.readFile<Listing>(this.source('listing.json'));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
async resolvedSourceListing(): Promise<[string, ListingEntry][]> {
|
|
88
|
-
const
|
|
89
|
-
.filter(([,
|
|
90
|
-
Context.#meetsRequirement([...this.#dependencies, ...this.#devDependencies],
|
|
88
|
+
const listing = Object.entries(await this.sourceListing)
|
|
89
|
+
.filter(([, entry]) => !entry.requires ||
|
|
90
|
+
Context.#meetsRequirement([...this.#dependencies, ...this.#devDependencies], entry.requires));
|
|
91
91
|
|
|
92
|
-
return
|
|
92
|
+
return listing;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
async initialize(): Promise<void> {
|
|
@@ -108,7 +108,8 @@ export class Context {
|
|
|
108
108
|
|
|
109
109
|
templateContext(): Record<string, unknown> {
|
|
110
110
|
const modules = [...this.#dependencies, ...this.#devDependencies]
|
|
111
|
-
.map(
|
|
111
|
+
.map(mod => path.basename(mod))
|
|
112
|
+
.reduce((ctx, value) => ({ ...ctx, [value.replace(/[-]/g, '_')]: true }), {});
|
|
112
113
|
const moduleNames = [...Object.keys(modules)];
|
|
113
114
|
|
|
114
115
|
/** Get framework version at runtime */
|
|
@@ -126,7 +127,7 @@ export class Context {
|
|
|
126
127
|
devDependencies: [...new Set(this.#devDependencies)].toSorted((a, b) => a.localeCompare(b)),
|
|
127
128
|
},
|
|
128
129
|
...this.#featureContexts,
|
|
129
|
-
...moduleNames.map(
|
|
130
|
+
...moduleNames.map(mod => ({ [`module_${mod}`]: true }))
|
|
130
131
|
);
|
|
131
132
|
|
|
132
133
|
return context;
|
|
@@ -160,7 +161,7 @@ export class Context {
|
|
|
160
161
|
const pkgs = Array.isArray(feat.package) ? feat.package : [feat.package];
|
|
161
162
|
for (const pkg of pkgs) {
|
|
162
163
|
if (pkg.startsWith('@travetto')) {
|
|
163
|
-
if (
|
|
164
|
+
if (DEV_DEPENDENCIES.has(pkg)) {
|
|
164
165
|
this.#devDependencies.push(pkg);
|
|
165
166
|
} else {
|
|
166
167
|
this.#dependencies.push(pkg);
|
|
@@ -210,6 +211,6 @@ export class Context {
|
|
|
210
211
|
await this.#exec('npx', ['trv', 'eslint:register']);
|
|
211
212
|
}
|
|
212
213
|
|
|
213
|
-
yield cliTpl`${{ success: 'Successfully created' }} at ${{ path: this.#
|
|
214
|
+
yield cliTpl`${{ success: 'Successfully created' }} at ${{ path: this.#targetDirectory }} `;
|
|
214
215
|
}
|
|
215
216
|
}
|
package/support/cli.scaffold.ts
CHANGED
|
@@ -2,7 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
|
|
3
3
|
import { prompt } from 'enquirer';
|
|
4
4
|
|
|
5
|
-
import { CliCommandShape, CliCommand, cliTpl } from '@travetto/cli';
|
|
5
|
+
import { CliCommandShape, CliCommand, cliTpl, CliFlag } from '@travetto/cli';
|
|
6
6
|
import { Terminal } from '@travetto/terminal';
|
|
7
7
|
|
|
8
8
|
import { Context } from './bin/context.ts';
|
|
@@ -16,9 +16,11 @@ export class ScaffoldCommand implements CliCommandShape {
|
|
|
16
16
|
/** Template */
|
|
17
17
|
template = 'todo';
|
|
18
18
|
/** Current Working Directory override */
|
|
19
|
-
|
|
19
|
+
@CliFlag({ short: 'c', full: 'cwd' })
|
|
20
|
+
workingDirectory: string = path.resolve();
|
|
20
21
|
/** Target Directory */
|
|
21
|
-
dir
|
|
22
|
+
@CliFlag({ short: 'd', full: 'dir' })
|
|
23
|
+
targetDirectory?: string;
|
|
22
24
|
/** Force writing into an existing directory */
|
|
23
25
|
force = false;
|
|
24
26
|
|
|
@@ -42,24 +44,26 @@ export class ScaffoldCommand implements CliCommandShape {
|
|
|
42
44
|
name: 'choice',
|
|
43
45
|
message: 'Please select one',
|
|
44
46
|
initial: feature.default,
|
|
45
|
-
choices: feature.choices
|
|
47
|
+
choices: feature.choices!
|
|
48
|
+
.map(subFeature => subFeature.title)
|
|
49
|
+
.filter((name?: string): name is string => !!name),
|
|
46
50
|
};
|
|
47
51
|
|
|
48
52
|
const response = await prompt<{ choice: string }>(choice);
|
|
49
|
-
return feature.choices?.find(
|
|
53
|
+
return feature.choices?.find(subFeature => subFeature.title === response.choice);
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
async * #resolveFeatures(features: Feature[], chosen = false, depth = 0): AsyncGenerator<Feature> {
|
|
53
57
|
for (const feat of features) {
|
|
54
58
|
if (!chosen && !feat.required) {
|
|
55
|
-
const
|
|
59
|
+
const answer = await prompt<{ choice: boolean | string }>([{
|
|
56
60
|
type: 'confirm',
|
|
57
61
|
name: 'choice',
|
|
58
62
|
message: `${'='.repeat(depth * 2)}${depth > 0 ? '| ' : ''}Include ${feat.title} support?`,
|
|
59
63
|
initial: true
|
|
60
64
|
}]);
|
|
61
65
|
|
|
62
|
-
if (
|
|
66
|
+
if (answer.choice === 'No' || answer.choice === false) {
|
|
63
67
|
continue;
|
|
64
68
|
}
|
|
65
69
|
}
|
|
@@ -82,15 +86,15 @@ export class ScaffoldCommand implements CliCommandShape {
|
|
|
82
86
|
async main(name?: string): Promise<void> {
|
|
83
87
|
name = await this.#getName(name);
|
|
84
88
|
|
|
85
|
-
if (!name && this.
|
|
86
|
-
name = path.basename(this.
|
|
87
|
-
} else if (name && !this.
|
|
88
|
-
this.
|
|
89
|
-
} else if (!name && !this.
|
|
89
|
+
if (!name && this.targetDirectory) {
|
|
90
|
+
name = path.basename(this.targetDirectory);
|
|
91
|
+
} else if (name && !this.targetDirectory) {
|
|
92
|
+
this.targetDirectory = path.resolve(this.workingDirectory, name);
|
|
93
|
+
} else if (!name && !this.targetDirectory) {
|
|
90
94
|
throw new Error('Either a name or a target directory are required');
|
|
91
95
|
}
|
|
92
96
|
|
|
93
|
-
const ctx = new Context(name, this.template, path.resolve(this.
|
|
97
|
+
const ctx = new Context(name, this.template, path.resolve(this.workingDirectory, this.targetDirectory!));
|
|
94
98
|
|
|
95
99
|
if (!this.force) {
|
|
96
100
|
await ctx.initialize();
|