@kinotic-ai/kinotic-cli 2.2.0 → 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 +1 -393
- package/dist/commands/create/project.d.ts +5 -1
- package/dist/commands/create/project.js +17 -11
- package/dist/commands/generate.d.ts +2 -2
- package/dist/commands/initialize.d.ts +4 -4
- package/dist/commands/login.d.ts +9 -0
- package/dist/commands/login.js +21 -0
- package/dist/commands/spawn/lint.d.ts +13 -0
- package/dist/commands/spawn/lint.js +51 -0
- package/dist/commands/synchronize.d.ts +5 -6
- package/dist/commands/synchronize.js +39 -38
- package/dist/internal/CliAuthenticator.d.ts +46 -0
- package/dist/internal/CliAuthenticator.js +211 -0
- package/dist/internal/Logger.d.ts +1 -1
- package/dist/internal/Utils.d.ts +0 -16
- package/dist/internal/Utils.js +0 -156
- package/dist/internal/spawn/FileSystemSpawnEngine.d.ts +23 -0
- package/dist/internal/spawn/FileSystemSpawnEngine.js +33 -0
- package/dist/internal/spawn/InquirerPropertyResolver.d.ts +9 -0
- package/dist/internal/spawn/InquirerPropertyResolver.js +30 -0
- package/dist/templates/entity/BaseRepository.liquid +2 -2
- package/dist/templates/spawns/project/package.json.liquid +2 -2
- package/dist/templates/spawns/project/spawn.json +10 -1
- package/package.json +19 -34
- package/bin/dev.cmd +0 -3
- package/bin/dev.js +0 -6
- package/bin/run.cmd +0 -3
- package/bin/run.js +0 -6
- package/dist/internal/spawn/SpawnConfig.d.ts +0 -31
- package/dist/internal/spawn/SpawnConfig.js +0 -12
- package/dist/internal/spawn/SpawnEngine.d.ts +0 -27
- package/dist/internal/spawn/SpawnEngine.js +0 -187
- package/oclif.manifest.json +0 -213
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { confirm, input, number, select } from '@inquirer/prompts';
|
|
2
|
+
/**
|
|
3
|
+
* Prompts the user on the terminal for spawn properties missing from the
|
|
4
|
+
* render context, choosing the prompt type from the property's schema.
|
|
5
|
+
*/
|
|
6
|
+
export class InquirerPropertyResolver {
|
|
7
|
+
hasPrompted = false;
|
|
8
|
+
async resolve(key, schema, message, defaultValue) {
|
|
9
|
+
if (!this.hasPrompted) {
|
|
10
|
+
console.log('Please provide the following...\n');
|
|
11
|
+
this.hasPrompted = true;
|
|
12
|
+
}
|
|
13
|
+
if (schema.type === 'boolean') {
|
|
14
|
+
return confirm({ message, default: typeof defaultValue === 'boolean' ? defaultValue : undefined });
|
|
15
|
+
}
|
|
16
|
+
else if (schema.enum) {
|
|
17
|
+
return select({
|
|
18
|
+
message,
|
|
19
|
+
choices: schema.enum.map((v) => ({ value: v })),
|
|
20
|
+
default: defaultValue !== undefined ? String(defaultValue) : undefined
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
else if (schema.type === 'number' || schema.type === 'integer') {
|
|
24
|
+
return number({ message, default: typeof defaultValue === 'number' ? defaultValue : undefined });
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return input({ message, default: typeof defaultValue === 'string' ? defaultValue : undefined });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -19,7 +19,7 @@ export class Base{{ entityName }}Repository extends EntityRepository<{{ entityNa
|
|
|
19
19
|
this.shouldValidate = shouldValidate
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
protected async beforeSaveOrUpdate(entity: {{ entityName }}): Promise<{{ entityName }}> {
|
|
22
|
+
protected override async beforeSaveOrUpdate(entity: {{ entityName }}): Promise<{{ entityName }}> {
|
|
23
23
|
if (this.shouldValidate) {
|
|
24
24
|
return this.validate(entity)
|
|
25
25
|
} else {
|
|
@@ -27,7 +27,7 @@ export class Base{{ entityName }}Repository extends EntityRepository<{{ entityNa
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
protected async beforeBulkSaveOrUpdate(entities: {{ entityName }}[]): Promise<{{ entityName }}[]> {
|
|
30
|
+
protected override async beforeBulkSaveOrUpdate(entities: {{ entityName }}[]): Promise<{{ entityName }}[]> {
|
|
31
31
|
if (this.shouldValidate) {
|
|
32
32
|
const validatedEntities: {{ entityName }}[] = []
|
|
33
33
|
for (let entity of entities) {
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"typescript": "^5.9.3"
|
|
13
13
|
},
|
|
14
14
|
"catalog": {
|
|
15
|
-
"@kinotic-ai/core": "{{
|
|
16
|
-
"@kinotic-ai/persistence": "{{
|
|
15
|
+
"@kinotic-ai/core": "{{ kinoticCoreVersion }}",
|
|
16
|
+
"@kinotic-ai/persistence": "{{ kinoticPersistenceVersion }}"
|
|
17
17
|
},
|
|
18
18
|
"workspaces": [
|
|
19
19
|
"packages/*"
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"globals": {
|
|
3
|
-
"
|
|
3
|
+
"kinoticCoreVersion": "^1.9.1",
|
|
4
|
+
"kinoticPersistenceVersion": "^2.0.0"
|
|
4
5
|
},
|
|
5
6
|
"propertySchema":{
|
|
6
7
|
"projectName": {
|
|
7
8
|
"type": "string",
|
|
8
9
|
"description": "The name of the project you want to create"
|
|
10
|
+
},
|
|
11
|
+
"organization": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"description": "The organization that owns the project"
|
|
14
|
+
},
|
|
15
|
+
"application": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "The application the project belongs to"
|
|
9
18
|
}
|
|
10
19
|
}
|
|
11
20
|
}
|
package/package.json
CHANGED
|
@@ -1,56 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kinotic-ai/kinotic-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Kinotic CLI provides the ability to build, deploy, and manage Kinotic applications that run on the Kinotic OS.",
|
|
5
5
|
"author": "Kinotic Developers",
|
|
6
6
|
"bin": {
|
|
7
7
|
"kinotic": "./bin/run.js"
|
|
8
8
|
},
|
|
9
9
|
"homepage": "https://kinotic-ai",
|
|
10
|
-
"repository":
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/kinotic-ai/kinotic"
|
|
13
|
+
},
|
|
11
14
|
"license": "Elastic License 2.0\"",
|
|
12
15
|
"main": "dist/index.js",
|
|
13
16
|
"type": "module",
|
|
14
17
|
"files": [
|
|
15
18
|
"/bin",
|
|
16
19
|
"/dist",
|
|
17
|
-
"/npm-shrinkwrap.json"
|
|
18
|
-
"/oclif.manifest.json"
|
|
20
|
+
"/npm-shrinkwrap.json"
|
|
19
21
|
],
|
|
20
22
|
"dependencies": {
|
|
21
23
|
"@inquirer/prompts": "^8.3.2",
|
|
22
|
-
"@kinotic-ai/core": "^
|
|
24
|
+
"@kinotic-ai/core": "^3.1.0",
|
|
23
25
|
"@kinotic-ai/idl": "^1.0.9",
|
|
24
|
-
"@kinotic-ai/os-api": "^
|
|
25
|
-
"@kinotic-ai/persistence": "^1.
|
|
26
|
+
"@kinotic-ai/os-api": "^3.1.0",
|
|
27
|
+
"@kinotic-ai/persistence": "^3.1.0",
|
|
28
|
+
"@kinotic-ai/spawn": "^0.5.0",
|
|
26
29
|
"@oclif/core": "^4.9.0",
|
|
27
|
-
"@oclif/plugin-autocomplete": "^3.2.41",
|
|
28
|
-
"@oclif/plugin-help": "^6",
|
|
29
|
-
"@oclif/plugin-not-found": "^3.2.75",
|
|
30
|
-
"@oclif/plugin-plugins": "^5.4.58",
|
|
31
|
-
"@oclif/plugin-update": "^4.7.23",
|
|
32
|
-
"@oclif/plugin-warn-if-update-available": "^3.1.55",
|
|
33
30
|
"c12": "^3.3.3",
|
|
34
31
|
"chalk": "^5.6.2",
|
|
35
32
|
"execa": "^9.6.1",
|
|
36
33
|
"glob": "^13.0.6",
|
|
37
34
|
"graphql": "^16.13.1",
|
|
38
|
-
"liquidjs": "10.
|
|
35
|
+
"liquidjs": "10.27.0",
|
|
39
36
|
"open": "^11.0.0",
|
|
40
37
|
"ora": "^9.3.0",
|
|
41
38
|
"p-timeout": "^7.0.1",
|
|
42
39
|
"radash": "^12.1.1",
|
|
43
|
-
"
|
|
44
|
-
"simple-git": "^3.33.0",
|
|
40
|
+
"simple-git": "3.36.0",
|
|
45
41
|
"terminal-link": "^5.0.0",
|
|
46
42
|
"ts-morph": "^27.0.2",
|
|
47
43
|
"uuid": "^13.0.0",
|
|
48
44
|
"ws": "^8.19.0",
|
|
49
|
-
"yaml": "2.8.3"
|
|
50
|
-
"zod": "^4.3.6"
|
|
45
|
+
"yaml": "2.8.3"
|
|
51
46
|
},
|
|
52
47
|
"devDependencies": {
|
|
53
|
-
"@oclif/test": "^4.1.16",
|
|
54
48
|
"@types/chai": "^5",
|
|
55
49
|
"@types/inquirer": "^9.0.9",
|
|
56
50
|
"@types/mocha": "^10.0.10",
|
|
@@ -59,48 +53,39 @@
|
|
|
59
53
|
"@types/ws": "^8.18.1",
|
|
60
54
|
"chai": "^6",
|
|
61
55
|
"eslint": "^9",
|
|
62
|
-
"eslint-config-oclif": "^6",
|
|
63
56
|
"eslint-config-prettier": "^10",
|
|
64
57
|
"mocha": "^11",
|
|
65
|
-
"oclif": "^4.22.92",
|
|
66
58
|
"shx": "^0.4.0",
|
|
59
|
+
"tsc-alias": "^1.8.16",
|
|
67
60
|
"tslib": "^2.8.1",
|
|
68
61
|
"tsx": "^4.21.0",
|
|
69
|
-
"tsc-alias": "^1.8.16",
|
|
70
62
|
"typescript": "^5.9.3"
|
|
71
63
|
},
|
|
72
64
|
"oclif": {
|
|
73
65
|
"bin": "kinotic",
|
|
74
66
|
"dirname": "kinotic",
|
|
75
67
|
"commands": "./dist/commands",
|
|
76
|
-
"plugins": [
|
|
77
|
-
"@oclif/plugin-help",
|
|
78
|
-
"@oclif/plugin-plugins",
|
|
79
|
-
"@oclif/plugin-update",
|
|
80
|
-
"@oclif/plugin-not-found",
|
|
81
|
-
"@oclif/plugin-warn-if-update-available",
|
|
82
|
-
"@oclif/plugin-autocomplete"
|
|
83
|
-
],
|
|
84
68
|
"topicSeparator": " ",
|
|
85
69
|
"topics": {
|
|
86
70
|
"create": {
|
|
87
71
|
"description": "Creates Kinotic projects, libraries, and applications"
|
|
72
|
+
},
|
|
73
|
+
"spawn": {
|
|
74
|
+
"description": "Works with Kinotic spawn templates"
|
|
88
75
|
}
|
|
89
76
|
}
|
|
90
77
|
},
|
|
91
78
|
"engines": {
|
|
92
79
|
"node": ">=18.0.0"
|
|
93
80
|
},
|
|
94
|
-
"keywords": [
|
|
95
|
-
"oclif"
|
|
96
|
-
],
|
|
81
|
+
"keywords": [],
|
|
97
82
|
"types": "dist/index.d.ts",
|
|
98
83
|
"scripts": {
|
|
99
84
|
"build": "shx rm -rf dist && tsc -b && tsc-alias -f && pnpm run copy-files",
|
|
100
85
|
"lint": "eslint . --ext .ts --config .eslintrc",
|
|
101
86
|
"posttest": "pnpm lint",
|
|
102
87
|
"test": "mocha --forbid-only \"test/**/*.test.ts\"",
|
|
103
|
-
"version": "
|
|
88
|
+
"version": "git add README.md",
|
|
104
89
|
"copy-files": "cp -r ./src/templates/ ./dist/templates/"
|
|
105
90
|
}
|
|
106
91
|
}
|
package/bin/dev.cmd
DELETED
package/bin/dev.js
DELETED
package/bin/run.cmd
DELETED
package/bin/run.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export declare const PropertySchemaSchema: z.ZodObject<{
|
|
3
|
-
type: z.ZodOptional<z.ZodEnum<{
|
|
4
|
-
string: "string";
|
|
5
|
-
number: "number";
|
|
6
|
-
boolean: "boolean";
|
|
7
|
-
integer: "integer";
|
|
8
|
-
}>>;
|
|
9
|
-
description: z.ZodOptional<z.ZodString>;
|
|
10
|
-
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
11
|
-
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
12
|
-
}, z.core.$strip>;
|
|
13
|
-
export declare const SpawnConfigSchema: z.ZodObject<{
|
|
14
|
-
inherits: z.ZodOptional<z.ZodString>;
|
|
15
|
-
globals: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
16
|
-
propertySchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
17
|
-
type: z.ZodOptional<z.ZodEnum<{
|
|
18
|
-
string: "string";
|
|
19
|
-
number: "number";
|
|
20
|
-
boolean: "boolean";
|
|
21
|
-
integer: "integer";
|
|
22
|
-
}>>;
|
|
23
|
-
description: z.ZodOptional<z.ZodString>;
|
|
24
|
-
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
25
|
-
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
26
|
-
}, z.core.$strip>>>;
|
|
27
|
-
}, z.core.$strip>;
|
|
28
|
-
export type PropertySchema = z.infer<typeof PropertySchemaSchema>;
|
|
29
|
-
export type SpawnConfig = z.infer<typeof SpawnConfigSchema>;
|
|
30
|
-
export type GlobalsType = Record<string, unknown>;
|
|
31
|
-
export type PropertySchemaType = Record<string, PropertySchema>;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export const PropertySchemaSchema = z.object({
|
|
3
|
-
type: z.enum(['string', 'number', 'integer', 'boolean']).optional(),
|
|
4
|
-
description: z.string().optional(),
|
|
5
|
-
default: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
6
|
-
enum: z.array(z.string()).optional(),
|
|
7
|
-
});
|
|
8
|
-
export const SpawnConfigSchema = z.object({
|
|
9
|
-
inherits: z.string().optional(),
|
|
10
|
-
globals: z.record(z.string(), z.unknown()).optional(),
|
|
11
|
-
propertySchema: z.record(z.string(), PropertySchemaSchema).optional(),
|
|
12
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { SpawnResolver } from './SpawnResolver.js';
|
|
2
|
-
export default class SpawnEngine {
|
|
3
|
-
private engine;
|
|
4
|
-
private spawnResolver;
|
|
5
|
-
constructor(resolver: SpawnResolver);
|
|
6
|
-
/**
|
|
7
|
-
* Renders the specified Spawn
|
|
8
|
-
* A Spawn is a directory that contains templates, an optional spawn.json file, and template parameters in folder and filenames
|
|
9
|
-
*
|
|
10
|
-
* This is done by performing the following
|
|
11
|
-
*
|
|
12
|
-
* - Will recursively walk the spawn copying any files or directories encountered
|
|
13
|
-
* -- If a template file is encountered, it will be parsed and rendered prior to copying
|
|
14
|
-
*
|
|
15
|
-
* @param spawn the name of the spawn to parse and render. This is the name of the directory containing the spawn.json
|
|
16
|
-
* @param destination the target directory where rendered data will be sent
|
|
17
|
-
* @param context the values to be provided to the templates while rendering
|
|
18
|
-
* @return a promise containing all the original values plus any added during rendering
|
|
19
|
-
*/
|
|
20
|
-
renderSpawn(spawn: string, destination: string, context?: Record<string, unknown>): Promise<Record<string, unknown> | undefined>;
|
|
21
|
-
private promptForMissingProperties;
|
|
22
|
-
private _renderFile;
|
|
23
|
-
private _renderDirectory;
|
|
24
|
-
private logDebug;
|
|
25
|
-
private shouldIgnore;
|
|
26
|
-
}
|
|
27
|
-
export declare const spawnEngine: SpawnEngine;
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import fsP from 'node:fs/promises';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { Liquid } from 'liquidjs';
|
|
5
|
-
import { confirm, input, number, select } from '@inquirer/prompts';
|
|
6
|
-
import { SpawnConfigSchema } from './SpawnConfig.js';
|
|
7
|
-
import { spawnResolver } from './SpawnResolver.js';
|
|
8
|
-
function upperFirst(s) {
|
|
9
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
10
|
-
}
|
|
11
|
-
function camelCase(s) {
|
|
12
|
-
return s
|
|
13
|
-
.replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase())
|
|
14
|
-
.replace(/^(.)/, (_, c) => c.toLowerCase());
|
|
15
|
-
}
|
|
16
|
-
export default class SpawnEngine {
|
|
17
|
-
engine;
|
|
18
|
-
spawnResolver;
|
|
19
|
-
constructor(resolver) {
|
|
20
|
-
this.spawnResolver = resolver;
|
|
21
|
-
this.engine = new Liquid({ cache: true });
|
|
22
|
-
this.engine.registerFilter('packageToPath', (v) => v.replaceAll('.', '/'));
|
|
23
|
-
this.engine.registerFilter('encodePackage', (v) => {
|
|
24
|
-
v = v.replaceAll('-', '_');
|
|
25
|
-
v = v.replace(/\.(\d+)/g, '._$1');
|
|
26
|
-
return v;
|
|
27
|
-
});
|
|
28
|
-
this.engine.registerFilter('camelCase', (v) => camelCase(v));
|
|
29
|
-
this.engine.registerFilter('upperFirst', (v) => upperFirst(v));
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Renders the specified Spawn
|
|
33
|
-
* A Spawn is a directory that contains templates, an optional spawn.json file, and template parameters in folder and filenames
|
|
34
|
-
*
|
|
35
|
-
* This is done by performing the following
|
|
36
|
-
*
|
|
37
|
-
* - Will recursively walk the spawn copying any files or directories encountered
|
|
38
|
-
* -- If a template file is encountered, it will be parsed and rendered prior to copying
|
|
39
|
-
*
|
|
40
|
-
* @param spawn the name of the spawn to parse and render. This is the name of the directory containing the spawn.json
|
|
41
|
-
* @param destination the target directory where rendered data will be sent
|
|
42
|
-
* @param context the values to be provided to the templates while rendering
|
|
43
|
-
* @return a promise containing all the original values plus any added during rendering
|
|
44
|
-
*/
|
|
45
|
-
async renderSpawn(spawn, destination, context) {
|
|
46
|
-
let contextInternal;
|
|
47
|
-
if (!fs.existsSync(destination)) {
|
|
48
|
-
const source = await this.spawnResolver.resolveSpawn(spawn);
|
|
49
|
-
const sources = [source];
|
|
50
|
-
const currentConfigPath = path.resolve(source, 'spawn.json');
|
|
51
|
-
if (fs.existsSync(currentConfigPath)) {
|
|
52
|
-
const spawns = [];
|
|
53
|
-
let currentConfig = currentConfigPath;
|
|
54
|
-
let currentSpawn = SpawnConfigSchema.parse(JSON.parse(fs.readFileSync(currentConfig, { encoding: 'utf8' })));
|
|
55
|
-
spawns.push(currentSpawn);
|
|
56
|
-
// follow inheritance and build stack
|
|
57
|
-
while (currentSpawn.inherits) {
|
|
58
|
-
const inheritDir = path.resolve(path.dirname(currentConfig), currentSpawn.inherits);
|
|
59
|
-
currentConfig = path.resolve(inheritDir, 'spawn.json');
|
|
60
|
-
this.logDebug(`Inheriting from ${currentConfig}\n`);
|
|
61
|
-
if (!fs.existsSync(currentConfig)) {
|
|
62
|
-
throw new Error(`Inherited spawn ${currentConfig} does not exist`);
|
|
63
|
-
}
|
|
64
|
-
currentSpawn = SpawnConfigSchema.parse(JSON.parse(fs.readFileSync(currentConfig, { encoding: 'utf8' })));
|
|
65
|
-
spawns.push(currentSpawn);
|
|
66
|
-
sources.push(inheritDir);
|
|
67
|
-
}
|
|
68
|
-
let globals = {};
|
|
69
|
-
let propertySchemas = {};
|
|
70
|
-
for (const spawnConfig of spawns.reverse()) {
|
|
71
|
-
if (spawnConfig.globals) {
|
|
72
|
-
globals = { ...globals, ...spawnConfig.globals };
|
|
73
|
-
}
|
|
74
|
-
if (spawnConfig.propertySchema) {
|
|
75
|
-
propertySchemas = { ...propertySchemas, ...spawnConfig.propertySchema };
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
contextInternal = { ...globals, ...context };
|
|
79
|
-
contextInternal = await this.promptForMissingProperties(propertySchemas, contextInternal);
|
|
80
|
-
}
|
|
81
|
-
await fsP.mkdir(destination, { recursive: true });
|
|
82
|
-
for (const src of sources.reverse()) {
|
|
83
|
-
await this._renderDirectory(src, src, destination, contextInternal);
|
|
84
|
-
}
|
|
85
|
-
return contextInternal;
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
throw new Error(`The target directory ${destination} already exists`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
async promptForMissingProperties(propertySchema, context) {
|
|
92
|
-
const ret = context ? { ...context } : {};
|
|
93
|
-
let hasPrompted = false;
|
|
94
|
-
for (const key in propertySchema) {
|
|
95
|
-
if (!Object.prototype.hasOwnProperty.call(ret, key)) {
|
|
96
|
-
if (!hasPrompted) {
|
|
97
|
-
console.log('Please provide the following...\n');
|
|
98
|
-
hasPrompted = true;
|
|
99
|
-
}
|
|
100
|
-
const schema = propertySchema[key];
|
|
101
|
-
let message;
|
|
102
|
-
if (schema.description?.includes('{{')) {
|
|
103
|
-
message = this.engine.parseAndRenderSync(schema.description, ret);
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
message = schema.description ?? key;
|
|
107
|
-
}
|
|
108
|
-
let defaultValue;
|
|
109
|
-
if (typeof schema.default === 'string' && schema.default.includes('{{')) {
|
|
110
|
-
defaultValue = this.engine.parseAndRenderSync(schema.default, ret);
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
defaultValue = schema.default;
|
|
114
|
-
}
|
|
115
|
-
if (schema.type === 'boolean') {
|
|
116
|
-
ret[key] = await confirm({ message, default: typeof defaultValue === 'boolean' ? defaultValue : undefined });
|
|
117
|
-
}
|
|
118
|
-
else if (schema.enum) {
|
|
119
|
-
ret[key] = await select({
|
|
120
|
-
message,
|
|
121
|
-
choices: schema.enum.map((v) => ({ value: v })),
|
|
122
|
-
default: defaultValue !== undefined ? String(defaultValue) : undefined
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
else if (schema.type === 'number' || schema.type === 'integer') {
|
|
126
|
-
ret[key] = await number({ message, default: typeof defaultValue === 'number' ? defaultValue : undefined });
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
ret[key] = await input({ message, default: typeof defaultValue === 'string' ? defaultValue : undefined });
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return ret;
|
|
134
|
-
}
|
|
135
|
-
async _renderFile(source, destination, errorIfExists, context) {
|
|
136
|
-
if (destination.includes('{{')) {
|
|
137
|
-
destination = await this.engine.parseAndRender(destination, context ?? {});
|
|
138
|
-
}
|
|
139
|
-
let overwritingFile = false;
|
|
140
|
-
if ((errorIfExists) && fs.existsSync(destination)) {
|
|
141
|
-
if (errorIfExists) {
|
|
142
|
-
throw new Error(`The target file ${destination} already exists`);
|
|
143
|
-
}
|
|
144
|
-
overwritingFile = true;
|
|
145
|
-
}
|
|
146
|
-
let readStream;
|
|
147
|
-
if (destination.endsWith('.liquid')) {
|
|
148
|
-
destination = destination.substring(0, destination.length - 7);
|
|
149
|
-
readStream = await this.engine.renderFileToNodeStream(source, context ?? {});
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
readStream = fs.createReadStream(source);
|
|
153
|
-
}
|
|
154
|
-
this.logDebug(`${overwritingFile ? 'Overwriting' : 'Writing'} File\n${source}\nto\n${destination}\n`);
|
|
155
|
-
await fsP.mkdir(path.dirname(destination), { recursive: true });
|
|
156
|
-
const writeStream = fs.createWriteStream(destination);
|
|
157
|
-
readStream.pipe(writeStream);
|
|
158
|
-
}
|
|
159
|
-
async _renderDirectory(baseFrom, from, baseTo, context) {
|
|
160
|
-
const files = await fsP.readdir(from, { withFileTypes: true });
|
|
161
|
-
for (const file of files) {
|
|
162
|
-
const filePath = path.resolve(from, file.name);
|
|
163
|
-
const to = filePath.replace(baseFrom, baseTo);
|
|
164
|
-
if (file.isFile()) {
|
|
165
|
-
if (!this.shouldIgnore(file.name)) {
|
|
166
|
-
await this._renderFile(filePath, to, false, context);
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
this.logDebug(`Skipping File\n${filePath}\n`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
await this._renderDirectory(baseFrom, filePath, baseTo, context);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
logDebug(message) {
|
|
178
|
-
if (process.env.DEBUG) {
|
|
179
|
-
console.debug(message);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
shouldIgnore(fileName) {
|
|
183
|
-
const filesToSkip = ['.DS_Store', 'spawn.json'];
|
|
184
|
-
return filesToSkip.includes(fileName);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
export const spawnEngine = new SpawnEngine(spawnResolver);
|