@codenotch/codenotch.cli 1.0.36 → 1.0.37
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 +2 -2
- package/dist/commands/generate/app.d.ts +2 -0
- package/dist/commands/generate/app.js +45 -0
- package/dist/commands/generate/app.js.map +1 -0
- package/dist/commands/generate/appmanifest.d.ts +2 -0
- package/dist/commands/generate/appmanifest.js +34 -0
- package/dist/commands/generate/appmanifest.js.map +1 -0
- package/dist/commands/generate/dbcontext.d.ts +2 -0
- package/dist/commands/generate/dbcontext.js +32 -0
- package/dist/commands/generate/dbcontext.js.map +1 -0
- package/dist/commands/generate/i18n.d.ts +2 -0
- package/dist/commands/generate/i18n.js +29 -0
- package/dist/commands/generate/i18n.js.map +1 -0
- package/dist/commands/generate/index.d.ts +0 -1
- package/dist/commands/generate/index.js +14 -33
- package/dist/commands/generate/index.js.map +1 -1
- package/dist/commands/generate/manifest.d.ts +2 -0
- package/dist/commands/generate/manifest.js +29 -0
- package/dist/commands/generate/manifest.js.map +1 -0
- package/dist/commands/generate/openapi.d.ts +2 -0
- package/dist/commands/generate/openapi.js +32 -0
- package/dist/commands/generate/openapi.js.map +1 -0
- package/dist/commands/generate/pkg.d.ts +2 -0
- package/dist/commands/generate/pkg.js +34 -0
- package/dist/commands/generate/pkg.js.map +1 -0
- package/dist/commands/generate/shared.d.ts +17 -0
- package/dist/commands/generate/shared.js +70 -0
- package/dist/commands/generate/shared.js.map +1 -0
- package/dist/commands/project/new.js +108 -96
- package/dist/commands/project/new.js.map +1 -1
- package/dist/commands/react/build.js +11 -5
- package/dist/commands/react/build.js.map +1 -1
- package/dist/commands/react/clean.d.ts +2 -0
- package/dist/commands/react/clean.js +46 -0
- package/dist/commands/react/clean.js.map +1 -0
- package/dist/commands/react/dev.d.ts +2 -0
- package/dist/commands/react/dev.js +27 -0
- package/dist/commands/react/dev.js.map +1 -0
- package/dist/commands/react/index.js +5 -1
- package/dist/commands/react/index.js.map +1 -1
- package/dist/commands/react/serve.js +33 -4
- package/dist/commands/react/serve.js.map +1 -1
- package/dist/commands/react/shared.d.ts +21 -0
- package/dist/commands/react/shared.js +80 -0
- package/dist/commands/react/shared.js.map +1 -0
- package/dist/commands/validation/ValidationUtils.d.ts +1 -1
- package/dist/commands/validation/ValidationUtils.js +3 -0
- package/dist/commands/validation/ValidationUtils.js.map +1 -1
- package/dist/commands/validation/files/I18nValidator.d.ts +29 -0
- package/dist/commands/validation/files/I18nValidator.js +198 -0
- package/dist/commands/validation/files/I18nValidator.js.map +1 -0
- package/dist/commands/validation/files/ProjectManifestValidator.d.ts +5 -0
- package/dist/commands/validation/files/ProjectManifestValidator.js +31 -0
- package/dist/commands/validation/files/ProjectManifestValidator.js.map +1 -0
- package/dist/commands/validation/files/ScriptValidator.js +1 -1
- package/dist/commands/validation/files/ScriptValidator.js.map +1 -1
- package/dist/commands/validation/validate.js +11 -6
- package/dist/commands/validation/validate.js.map +1 -1
- package/dist/utils/BuildUtils.d.ts +61 -7
- package/dist/utils/BuildUtils.js +289 -222
- package/dist/utils/BuildUtils.js.map +1 -1
- package/dist/utils/ConsoleUtils.d.ts +8 -0
- package/dist/utils/ConsoleUtils.js +25 -0
- package/dist/utils/ConsoleUtils.js.map +1 -1
- package/dist/utils/DbSchemaUtils.d.ts +49 -0
- package/dist/utils/DbSchemaUtils.js +246 -0
- package/dist/utils/DbSchemaUtils.js.map +1 -0
- package/dist/utils/FileUtils.d.ts +104 -15
- package/dist/utils/FileUtils.js +285 -100
- package/dist/utils/FileUtils.js.map +1 -1
- package/dist/utils/I18nTsUtils.d.ts +66 -0
- package/dist/utils/I18nTsUtils.js +221 -0
- package/dist/utils/I18nTsUtils.js.map +1 -0
- package/dist/utils/OpenApiUtils.d.ts +81 -0
- package/dist/utils/OpenApiUtils.js +123 -0
- package/dist/utils/OpenApiUtils.js.map +1 -0
- package/dist/utils/ProjectCompilationUtils.d.ts +0 -1
- package/dist/utils/ProjectCompilationUtils.js +4 -52
- package/dist/utils/ProjectCompilationUtils.js.map +1 -1
- package/dist/utils/ProjectGeneratorUtils.d.ts +35 -0
- package/dist/utils/ProjectGeneratorUtils.js +119 -0
- package/dist/utils/ProjectGeneratorUtils.js.map +1 -0
- package/dist/utils/ProjectTemplates.d.ts +102 -0
- package/dist/utils/ProjectTemplates.js +284 -0
- package/dist/utils/ProjectTemplates.js.map +1 -0
- package/dist/utils/ProjectUtils.d.ts +216 -3
- package/dist/utils/ProjectUtils.js +387 -22
- package/dist/utils/ProjectUtils.js.map +1 -1
- package/dist/utils/RuntimeUtils.js +14 -8
- package/dist/utils/RuntimeUtils.js.map +1 -1
- package/dist/utils/ScriptDeclarationUtils.d.ts +169 -0
- package/dist/utils/ScriptDeclarationUtils.js +398 -0
- package/dist/utils/ScriptDeclarationUtils.js.map +1 -0
- package/dist/utils/ServeUtils.js +10 -5
- package/dist/utils/ServeUtils.js.map +1 -1
- package/dist/utils/ShellUtils.d.ts +6 -0
- package/dist/utils/ShellUtils.js +13 -0
- package/dist/utils/ShellUtils.js.map +1 -1
- package/dist/utils/TemplateUtils.d.ts +62 -0
- package/dist/utils/TemplateUtils.js +218 -0
- package/dist/utils/TemplateUtils.js.map +1 -0
- package/dist/utils/TypeSchemaUtils.d.ts +42 -0
- package/dist/utils/TypeSchemaUtils.js +171 -0
- package/dist/utils/TypeSchemaUtils.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const papaparse_1 = __importDefault(require("papaparse"));
|
|
8
|
+
const FileUtils_1 = __importDefault(require("./FileUtils"));
|
|
9
|
+
const ProjectUtils_1 = __importDefault(require("./ProjectUtils"));
|
|
10
|
+
const DbSchemaUtils_1 = __importDefault(require("./DbSchemaUtils"));
|
|
11
|
+
const OpenApiUtils_1 = __importDefault(require("./OpenApiUtils"));
|
|
12
|
+
const I18nTsUtils_1 = __importDefault(require("./I18nTsUtils"));
|
|
13
|
+
/**
|
|
14
|
+
* The generators of the derived files of a project, shared by 'cn generate ...' (one at a time)
|
|
15
|
+
* and 'cn project new' (all of them, once the template is written).
|
|
16
|
+
*
|
|
17
|
+
* Every generator reports rather than prints: the command decides how to show the outcome.
|
|
18
|
+
*/
|
|
19
|
+
class ProjectGeneratorUtils {
|
|
20
|
+
/** 'db-schema.xml' and 'db-schema.ts' from the 'defineEntity' declarations of the project */
|
|
21
|
+
static async generateDbContext(projectPath) {
|
|
22
|
+
const tables = await ProjectUtils_1.default.discoverTableFiles(projectPath);
|
|
23
|
+
if (tables.length === 0) {
|
|
24
|
+
return { written: [], warnings: [], tableCount: 0, skipped: `No table discovered in '${projectPath}', nothing to generate.` };
|
|
25
|
+
}
|
|
26
|
+
const targetSchemaPath = (0, path_1.join)(projectPath, 'db-schema.xml');
|
|
27
|
+
const targetTsPath = (0, path_1.join)(projectPath, 'db-schema.ts');
|
|
28
|
+
// The manifest names the project and versions the schema, but it is not required to generate it
|
|
29
|
+
const manifest = await FileUtils_1.default.readJsonFileAsync((0, path_1.join)(projectPath, 'manifest.json'));
|
|
30
|
+
const projectName = manifest?.projectName ?? FileUtils_1.default.getNameFromPath(projectPath);
|
|
31
|
+
// Each type is imported relative to the generated file
|
|
32
|
+
const imports = new Map();
|
|
33
|
+
for (const table of tables) {
|
|
34
|
+
const importStatement = ProjectUtils_1.default.getTableTypeImport(table, targetTsPath);
|
|
35
|
+
if (table.name !== undefined && importStatement !== undefined) {
|
|
36
|
+
imports.set(table.name, importStatement);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const xml = DbSchemaUtils_1.default.generateXml(tables, manifest?.version ?? '0.0.0');
|
|
40
|
+
const ts = DbSchemaUtils_1.default.generateTs(tables, projectName, imports);
|
|
41
|
+
const warnings = [...xml.warnings, ...ts.warnings];
|
|
42
|
+
// Both files describe the same schema, writing only one of them would leave the project inconsistent
|
|
43
|
+
if (!FileUtils_1.default.writeFileSync(targetSchemaPath, xml.content) || !FileUtils_1.default.writeFileSync(targetTsPath, ts.content)) {
|
|
44
|
+
return { written: [], warnings, tableCount: tables.length, error: `Could not write the schema files in '${projectPath}'.` };
|
|
45
|
+
}
|
|
46
|
+
return { written: [targetSchemaPath, targetTsPath], warnings, tableCount: tables.length };
|
|
47
|
+
}
|
|
48
|
+
/** 'openapi.json' from the processes triggered by an api call */
|
|
49
|
+
static async generateOpenApi(projectPath) {
|
|
50
|
+
const processes = await ProjectUtils_1.default.discoverProcessFiles(projectPath);
|
|
51
|
+
if (!processes.some(process => process.trigger?.kind === 'api')) {
|
|
52
|
+
return { written: [], warnings: [], endpointCount: 0, skipped: `No process triggered by an api call discovered in '${projectPath}', nothing to generate.` };
|
|
53
|
+
}
|
|
54
|
+
// The manifest names and versions the API, but it is not required to generate it
|
|
55
|
+
const manifest = await FileUtils_1.default.readJsonFileAsync((0, path_1.join)(projectPath, 'manifest.json'));
|
|
56
|
+
const result = OpenApiUtils_1.default.generate(processes, {
|
|
57
|
+
title: manifest?.projectName ?? FileUtils_1.default.getNameFromPath(projectPath),
|
|
58
|
+
version: manifest?.version ?? '0.0.0'
|
|
59
|
+
});
|
|
60
|
+
const targetPath = (0, path_1.join)(projectPath, 'openapi.json');
|
|
61
|
+
if (!FileUtils_1.default.writeFileSync(targetPath, JSON.stringify(result.document, null, 4) + '\n')) {
|
|
62
|
+
return { written: [], warnings: result.warnings, endpointCount: result.endpointCount, error: `Could not write '${targetPath}'.` };
|
|
63
|
+
}
|
|
64
|
+
return { written: [targetPath], warnings: result.warnings, endpointCount: result.endpointCount };
|
|
65
|
+
}
|
|
66
|
+
/** The '*.i18n.ts' class of one '*.i18n.csv', written next to it */
|
|
67
|
+
static async generateI18n(csvPath) {
|
|
68
|
+
const content = await FileUtils_1.default.readFileAsync(csvPath);
|
|
69
|
+
if (content === undefined) {
|
|
70
|
+
return { written: [], warnings: [], keyCount: 0, error: `Could not read '${csvPath}'.` };
|
|
71
|
+
}
|
|
72
|
+
const parsed = papaparse_1.default.parse(content, { header: true, skipEmptyLines: 'greedy' });
|
|
73
|
+
const warnings = [];
|
|
74
|
+
// The first column must be the key, otherwise every line would be read as a translation of the wrong key
|
|
75
|
+
const columns = parsed.meta.fields ?? [];
|
|
76
|
+
if (columns[0] !== 'key') {
|
|
77
|
+
return { written: [], warnings, keyCount: 0, error: `The first column of '${csvPath}' is '${columns[0] ?? ''}' but 'key' is expected.` };
|
|
78
|
+
}
|
|
79
|
+
for (const parseError of parsed.errors) {
|
|
80
|
+
warnings.push(`CSV${parseError.row !== undefined ? ` line ${parseError.row + 2}` : ''}: ${parseError.message}`);
|
|
81
|
+
}
|
|
82
|
+
const entries = I18nTsUtils_1.default.getEntries(parsed.data);
|
|
83
|
+
if (entries.length === 0) {
|
|
84
|
+
warnings.push(`'${csvPath}' does not contain any translation key.`);
|
|
85
|
+
}
|
|
86
|
+
const outputPath = csvPath.replace(/\.csv$/i, '.ts');
|
|
87
|
+
const result = I18nTsUtils_1.default.generate(I18nTsUtils_1.default.getClassName(csvPath), (0, path_1.basename)(csvPath), entries);
|
|
88
|
+
warnings.push(...result.warnings);
|
|
89
|
+
if (!FileUtils_1.default.writeFileSync(outputPath, result.content)) {
|
|
90
|
+
return { written: [], warnings, keyCount: entries.length, error: `Could not write '${outputPath}'.` };
|
|
91
|
+
}
|
|
92
|
+
return { written: [outputPath], warnings, keyCount: entries.length };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Every derived file of a project: the database schema when it declares tables, the class of
|
|
96
|
+
* each translation file, the openapi document when it exposes endpoints. Skipped generators
|
|
97
|
+
* are silent, since a project without tables is not an error.
|
|
98
|
+
*/
|
|
99
|
+
static async generateAll(projectPath) {
|
|
100
|
+
const written = [];
|
|
101
|
+
const warnings = [];
|
|
102
|
+
const errors = [];
|
|
103
|
+
const collect = (result) => {
|
|
104
|
+
written.push(...result.written);
|
|
105
|
+
warnings.push(...result.warnings);
|
|
106
|
+
if (result.error !== undefined) {
|
|
107
|
+
errors.push(result.error);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
collect(await this.generateDbContext(projectPath));
|
|
111
|
+
for (const i18n of await ProjectUtils_1.default.discoverI18nsFiles(projectPath)) {
|
|
112
|
+
collect(await this.generateI18n(i18n.filePath));
|
|
113
|
+
}
|
|
114
|
+
collect(await this.generateOpenApi(projectPath));
|
|
115
|
+
return { written, warnings, ...(errors.length === 0 ? {} : { error: errors.join(' ') }) };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.default = ProjectGeneratorUtils;
|
|
119
|
+
//# sourceMappingURL=ProjectGeneratorUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProjectGeneratorUtils.js","sourceRoot":"","sources":["../../src/utils/ProjectGeneratorUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export type TemplateName = 'default' | 'tailwind' | 'todolist' | 'api';
|
|
2
|
+
export interface IProjectTemplate {
|
|
3
|
+
name: TemplateName;
|
|
4
|
+
/** One line shown when choosing a template */
|
|
5
|
+
description: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ITemplateContext {
|
|
8
|
+
projectName: string;
|
|
9
|
+
serviceName: string;
|
|
10
|
+
/** Languages of the manifest, one column each in the translation files */
|
|
11
|
+
languages: string[];
|
|
12
|
+
}
|
|
13
|
+
/** A file of a generated project, its path relative to the project root with posix separators */
|
|
14
|
+
export interface ITemplateFile {
|
|
15
|
+
path: string;
|
|
16
|
+
content: string;
|
|
17
|
+
}
|
|
18
|
+
export interface IProjectScaffold {
|
|
19
|
+
/** Folders to create, even when empty */
|
|
20
|
+
folders: string[];
|
|
21
|
+
files: ITemplateFile[];
|
|
22
|
+
/** What is left to do by hand, shown once the project is created */
|
|
23
|
+
nextSteps: string[];
|
|
24
|
+
}
|
|
25
|
+
/** The install step of the next steps, as 'cn project new' drops it once it ran it */
|
|
26
|
+
export declare const NPM_INSTALL_STEP = "npm install";
|
|
27
|
+
/**
|
|
28
|
+
* The starting points 'cn project new' can scaffold.
|
|
29
|
+
*
|
|
30
|
+
* 'default' is the bare project with a routed React app, 'tailwind' adds Tailwind CSS to it, and
|
|
31
|
+
* 'todolist' builds a complete application on top of that: tables, processes, queries and
|
|
32
|
+
* translations. Each of these extends the previous one, so what is fixed in one is fixed in all.
|
|
33
|
+
* 'api' stands apart: a table and its endpoints, without any app nor React.
|
|
34
|
+
*
|
|
35
|
+
* Where the runtime API is not settled yet (`@codenotch/process`, `@codenotch/orm`, `startProcess`),
|
|
36
|
+
* the generated code carries a TODO rather than a guess.
|
|
37
|
+
*/
|
|
38
|
+
export default abstract class ProjectTemplates {
|
|
39
|
+
static readonly TEMPLATES: IProjectTemplate[];
|
|
40
|
+
static isTemplateName(value: string): value is TemplateName;
|
|
41
|
+
static build(name: TemplateName, context: ITemplateContext): IProjectScaffold;
|
|
42
|
+
private static buildDefault;
|
|
43
|
+
private static createAppManifest;
|
|
44
|
+
/**
|
|
45
|
+
* The entry of the app: a HashRouter, which keeps the routing client side whatever URL the
|
|
46
|
+
* runtime serves the app from, and one route per page
|
|
47
|
+
*/
|
|
48
|
+
private static createRoutedApp;
|
|
49
|
+
private static createLayout;
|
|
50
|
+
private static createHomePage;
|
|
51
|
+
private static createAboutPage;
|
|
52
|
+
private static createHelloPage;
|
|
53
|
+
/**
|
|
54
|
+
* The stylesheet of the default app: scss on purpose, so variables, nesting and the rest
|
|
55
|
+
* of the language are ready to use
|
|
56
|
+
*/
|
|
57
|
+
private static createDefaultScss;
|
|
58
|
+
/**
|
|
59
|
+
* The stylesheet of the tailwind flavoured templates: tailwind first, scss for whatever
|
|
60
|
+
* the utilities do not cover
|
|
61
|
+
*/
|
|
62
|
+
private static createTailwindScss;
|
|
63
|
+
private static buildTailwind;
|
|
64
|
+
private static buildTodolist;
|
|
65
|
+
private static createTodoListTable;
|
|
66
|
+
private static createTodoTable;
|
|
67
|
+
private static createTodoListProcesses;
|
|
68
|
+
private static createTodoProcesses;
|
|
69
|
+
private static createAllTodoListsQuery;
|
|
70
|
+
private static createTodosOfListQuery;
|
|
71
|
+
/** One column per language of the manifest, filled for the ones the template knows */
|
|
72
|
+
private static createTodolistI18n;
|
|
73
|
+
/**
|
|
74
|
+
* The calls the pages make, in one place. Typed with the row types of the tables, so the UI
|
|
75
|
+
* and the processes cannot drift apart silently.
|
|
76
|
+
*/
|
|
77
|
+
private static createTodolistApi;
|
|
78
|
+
private static createTodolistApp;
|
|
79
|
+
private static createTodolistLayout;
|
|
80
|
+
private static createTodoListsPage;
|
|
81
|
+
private static createTodoListPage;
|
|
82
|
+
private static createTodolistReadme;
|
|
83
|
+
/**
|
|
84
|
+
* A project exposing an API and nothing else: no app, no React, no translations.
|
|
85
|
+
* The product catalog it manages is small on purpose, every kind of endpoint is there once.
|
|
86
|
+
*/
|
|
87
|
+
private static buildApi;
|
|
88
|
+
private static createProductTable;
|
|
89
|
+
/** One process per operation of the catalog, each exposed as an endpoint */
|
|
90
|
+
private static createProductProcesses;
|
|
91
|
+
/** What an API project runs on its own: a health check and a scheduled report */
|
|
92
|
+
private static createMaintenanceProcesses;
|
|
93
|
+
private static createAllProductsQuery;
|
|
94
|
+
private static createLowStockQuery;
|
|
95
|
+
private static createApiReadme;
|
|
96
|
+
/** Joins lines, dropping the undefined ones so that optional lines read naturally */
|
|
97
|
+
private static lines;
|
|
98
|
+
/** ` className="..."` when the template is styled, nothing otherwise */
|
|
99
|
+
private static classes;
|
|
100
|
+
private static replaceFile;
|
|
101
|
+
private static toJson;
|
|
102
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NPM_INSTALL_STEP = void 0;
|
|
7
|
+
const TemplateUtils_1 = __importDefault(require("./TemplateUtils"));
|
|
8
|
+
const ManifestUtils_1 = __importDefault(require("./ManifestUtils"));
|
|
9
|
+
const DEFAULT_APP_NAME = 'index';
|
|
10
|
+
/** The install step of the next steps, as 'cn project new' drops it once it ran it */
|
|
11
|
+
exports.NPM_INSTALL_STEP = 'npm install';
|
|
12
|
+
/** Versions pinned by the templates. React 19 is the only supported major */
|
|
13
|
+
const VERSIONS = {
|
|
14
|
+
reactRouter: '^7.6.0',
|
|
15
|
+
tailwind: '^4.1.0',
|
|
16
|
+
tailwindPostcss: '^4.1.0',
|
|
17
|
+
postcss: '^8.5.0'
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The starting points 'cn project new' can scaffold.
|
|
21
|
+
*
|
|
22
|
+
* 'default' is the bare project with a routed React app, 'tailwind' adds Tailwind CSS to it, and
|
|
23
|
+
* 'todolist' builds a complete application on top of that: tables, processes, queries and
|
|
24
|
+
* translations. Each of these extends the previous one, so what is fixed in one is fixed in all.
|
|
25
|
+
* 'api' stands apart: a table and its endpoints, without any app nor React.
|
|
26
|
+
*
|
|
27
|
+
* Where the runtime API is not settled yet (`@codenotch/process`, `@codenotch/orm`, `startProcess`),
|
|
28
|
+
* the generated code carries a TODO rather than a guess.
|
|
29
|
+
*/
|
|
30
|
+
class ProjectTemplates {
|
|
31
|
+
static TEMPLATES = [
|
|
32
|
+
{ name: 'default', description: 'Empty project with a routed React 19 app' },
|
|
33
|
+
{ name: 'tailwind', description: 'Same, styled with Tailwind CSS 4 (light and dark themes)' },
|
|
34
|
+
{ name: 'todolist', description: 'Complete todo list app: tables, CRUD processes, queries, translations' },
|
|
35
|
+
{ name: 'api', description: 'API only: a table and its endpoints, no app at all' }
|
|
36
|
+
];
|
|
37
|
+
static isTemplateName(value) {
|
|
38
|
+
return this.TEMPLATES.some(template => template.name === value);
|
|
39
|
+
}
|
|
40
|
+
static build(name, context) {
|
|
41
|
+
switch (name) {
|
|
42
|
+
case 'default': return this.buildDefault(context);
|
|
43
|
+
case 'tailwind': return this.buildTailwind(context);
|
|
44
|
+
case 'todolist': return this.buildTodolist(context);
|
|
45
|
+
case 'api': return this.buildApi(context);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// ------------------------------------------------------------------ default
|
|
49
|
+
static buildDefault(context, styled = false) {
|
|
50
|
+
const manifest = ManifestUtils_1.default.createManifest(context.projectName, { serviceName: context.serviceName });
|
|
51
|
+
manifest.languages = [...context.languages];
|
|
52
|
+
return {
|
|
53
|
+
folders: [...TemplateUtils_1.default.PROJECT_FOLDERS],
|
|
54
|
+
files: [
|
|
55
|
+
{ path: 'manifest.json', content: ManifestUtils_1.default.serialize(manifest) },
|
|
56
|
+
{ path: 'readme.md', content: TemplateUtils_1.default.createReadmeMd(context.projectName, context.serviceName) },
|
|
57
|
+
{ path: '.gitignore', content: TemplateUtils_1.default.createGitignore() },
|
|
58
|
+
{
|
|
59
|
+
path: 'package.json',
|
|
60
|
+
content: this.toJson(TemplateUtils_1.default.createPackageJson(context.serviceName, {
|
|
61
|
+
dependencies: { 'react-router': VERSIONS.reactRouter }
|
|
62
|
+
}))
|
|
63
|
+
},
|
|
64
|
+
{ path: `apps/${DEFAULT_APP_NAME}.manifest.json`, content: this.toJson(this.createAppManifest(context.projectName)) },
|
|
65
|
+
{ path: `apps/${DEFAULT_APP_NAME}.tsx`, content: this.createRoutedApp(styled) },
|
|
66
|
+
{ path: 'apps/components/Layout.tsx', content: this.createLayout(styled) },
|
|
67
|
+
{ path: 'apps/pages/HomePage.tsx', content: this.createHomePage(styled) },
|
|
68
|
+
{ path: 'apps/pages/AboutPage.tsx', content: this.createAboutPage(styled) },
|
|
69
|
+
{ path: 'apps/pages/HelloPage.tsx', content: this.createHelloPage(styled) },
|
|
70
|
+
{ path: `apps/${DEFAULT_APP_NAME}.scss`, content: this.createDefaultScss() }
|
|
71
|
+
],
|
|
72
|
+
nextSteps: [
|
|
73
|
+
exports.NPM_INSTALL_STEP,
|
|
74
|
+
'cn project start'
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
static createAppManifest(projectName) {
|
|
79
|
+
const manifest = TemplateUtils_1.default.createAppManifest(DEFAULT_APP_NAME);
|
|
80
|
+
// The tab of the browser is better named after the project than after 'index'
|
|
81
|
+
manifest.html = { ...manifest.html, title: projectName };
|
|
82
|
+
return manifest;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The entry of the app: a HashRouter, which keeps the routing client side whatever URL the
|
|
86
|
+
* runtime serves the app from, and one route per page
|
|
87
|
+
*/
|
|
88
|
+
static createRoutedApp(styled) {
|
|
89
|
+
return this.lines(`import React from 'react';`, `import { HashRouter, Route, Routes } from 'react-router';`, `import './${DEFAULT_APP_NAME}.scss';`, `import Layout from './components/Layout';`, `import HomePage from './pages/HomePage';`, `import AboutPage from './pages/AboutPage';`, `import HelloPage from './pages/HelloPage';`, ``, `// HashRouter keeps the routing client side, whatever URL the runtime serves the app from`, `const ${DEFAULT_APP_NAME}: React.FC<{}> = () => {`, `\treturn <HashRouter>`, `\t\t<Routes>`, `\t\t\t<Route element={<Layout />}>`, `\t\t\t\t<Route index element={<HomePage />} />`, `\t\t\t\t<Route path="about" element={<AboutPage />} />`, `\t\t\t\t<Route path="hello/:name" element={<HelloPage />} />`, `\t\t\t</Route>`, `\t\t</Routes>`, `\t</HashRouter>;`, `};`, ``, `export default ${DEFAULT_APP_NAME};`, ``);
|
|
90
|
+
}
|
|
91
|
+
static createLayout(styled) {
|
|
92
|
+
const c = this.classes(styled);
|
|
93
|
+
return this.lines(`import React from 'react';`, `import { NavLink, Outlet } from 'react-router';`, `import { useCodenotch } from '@codenotch/codenotch.react';`, ``, `// Shared frame of every page: the navigation, then the page itself in <Outlet />`, `const Layout: React.FC<{}> = () => {`, `\tconst cn = useCodenotch();`, styled ? `\tconst theme = cn.getTheme() ?? 'light';` : undefined, ``, `\treturn <div${c('min-h-screen bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100')}>`, `\t\t<nav${c('flex items-center gap-4 border-b border-gray-200 px-6 py-3 dark:border-gray-700')}>`, `\t\t\t<NavLink to="/"${c('hover:underline')}>Home</NavLink>`, `\t\t\t<NavLink to="/about"${c('hover:underline')}>About</NavLink>`, `\t\t\t<NavLink to="/hello/codenotch"${c('hover:underline')}>Hello</NavLink>`, styled ? `\t\t\t<button${c('ml-auto rounded border border-gray-300 px-3 py-1 text-sm dark:border-gray-600')} onClick={() => cn.setTheme(theme === 'dark' ? 'light' : 'dark')}>` : undefined, styled ? `\t\t\t\t{theme === 'dark' ? 'Light theme' : 'Dark theme'}` : undefined, styled ? `\t\t\t</button>` : undefined, `\t\t</nav>`, `\t\t<main${c('p-6')}>`, `\t\t\t<Outlet />`, `\t\t</main>`, `\t</div>;`, `};`, ``, `export default Layout;`, ``);
|
|
94
|
+
}
|
|
95
|
+
static createHomePage(styled) {
|
|
96
|
+
const c = this.classes(styled);
|
|
97
|
+
return this.lines(`import React from 'react';`, `import { useCodenotch } from '@codenotch/codenotch.react';`, ``, `const HomePage: React.FC<{}> = () => {`, `\tconst cn = useCodenotch();`, ``, `\treturn <div>`, `\t\t<h1${c('text-2xl font-bold')}>My new Codenotch App</h1>`, `\t\t<p${c('mt-2 text-gray-600 dark:text-gray-400')}>The environment the runtime gave this app:</p>`, `\t\t<pre${c('mt-4 overflow-x-auto rounded bg-gray-100 p-4 text-sm dark:bg-gray-800')}>{JSON.stringify(cn.env, null, 4)}</pre>`, `\t</div>;`, `};`, ``, `export default HomePage;`, ``);
|
|
98
|
+
}
|
|
99
|
+
static createAboutPage(styled) {
|
|
100
|
+
const c = this.classes(styled);
|
|
101
|
+
return this.lines(`import React from 'react';`, `import { Link } from 'react-router';`, ``, `const AboutPage: React.FC<{}> = () => {`, `\treturn <div>`, `\t\t<h1${c('text-2xl font-bold')}>About</h1>`, `\t\t<p${c('mt-2')}>This page is reached through the router, without reloading the app.</p>`, `\t\t<Link to="/"${c('mt-4 inline-block underline')}>Back home</Link>`, `\t</div>;`, `};`, ``, `export default AboutPage;`, ``);
|
|
102
|
+
}
|
|
103
|
+
static createHelloPage(styled) {
|
|
104
|
+
const c = this.classes(styled);
|
|
105
|
+
return this.lines(`import React from 'react';`, `import { useParams } from 'react-router';`, ``, `// The ':name' segment of the route is read with useParams()`, `const HelloPage: React.FC<{}> = () => {`, `\tconst { name } = useParams<{ name: string }>();`, ``, `\treturn <div>`, `\t\t<h1${c('text-2xl font-bold')}>Hello {name}!</h1>`, `\t\t<p${c('mt-2')}>Change the name in the URL to greet someone else.</p>`, `\t</div>;`, `};`, ``, `export default HelloPage;`, ``);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The stylesheet of the default app: scss on purpose, so variables, nesting and the rest
|
|
109
|
+
* of the language are ready to use
|
|
110
|
+
*/
|
|
111
|
+
static createDefaultScss() {
|
|
112
|
+
return this.lines(`// The stylesheet of the app. Scss is the default here: variables, nesting, functions...`, `$accent: #2563eb;`, `$spacing: 1.5rem;`, ``, `nav {`, ` display: flex;`, ` gap: $spacing;`, ` padding: 0.75rem $spacing;`, ` border-bottom: 1px solid #e5e7eb;`, ``, ` a {`, ` color: inherit;`, ` text-decoration: none;`, ``, ` &:hover {`, ` color: $accent;`, ` text-decoration: underline;`, ` }`, ` }`, `}`, ``, `main {`, ` padding: $spacing;`, ``, ` pre {`, ` overflow-x: auto;`, ` padding: 1rem;`, ` background: #f3f4f6;`, ` border-radius: 0.25rem;`, ` }`, `}`, ``);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The stylesheet of the tailwind flavoured templates: tailwind first, scss for whatever
|
|
116
|
+
* the utilities do not cover
|
|
117
|
+
*/
|
|
118
|
+
static createTailwindScss() {
|
|
119
|
+
return this.lines(`@import "tailwindcss";`, ``, `/* useCodenotch().setTheme('dark') toggles the 'dark' class on <html>: key the dark: variants on it */`, `@custom-variant dark (&:where(.dark, .dark *));`, ``, `// Scss on top of tailwind, for what the utilities do not cover`, `$accent: #2563eb;`, ``, `.accent-link {`, ` color: $accent;`, ``, ` &:hover {`, ` text-decoration: underline;`, ` }`, `}`, ``);
|
|
120
|
+
}
|
|
121
|
+
// ------------------------------------------------------------------ tailwind
|
|
122
|
+
static buildTailwind(context) {
|
|
123
|
+
const scaffold = this.buildDefault(context, true);
|
|
124
|
+
this.replaceFile(scaffold, 'package.json', this.toJson(TemplateUtils_1.default.createPackageJson(context.serviceName, {
|
|
125
|
+
dependencies: { 'react-router': VERSIONS.reactRouter },
|
|
126
|
+
devDependencies: {
|
|
127
|
+
'tailwindcss': VERSIONS.tailwind,
|
|
128
|
+
'@tailwindcss/postcss': VERSIONS.tailwindPostcss,
|
|
129
|
+
'postcss': VERSIONS.postcss
|
|
130
|
+
}
|
|
131
|
+
})));
|
|
132
|
+
this.replaceFile(scaffold, `apps/${DEFAULT_APP_NAME}.scss`, this.createTailwindScss());
|
|
133
|
+
scaffold.files.push({
|
|
134
|
+
path: 'postcss.config.js',
|
|
135
|
+
// Picked up by the Vite build of 'cn react build', which mirrors the project layout
|
|
136
|
+
content: this.lines(`module.exports = {`, ` plugins: {`, ` '@tailwindcss/postcss': {}`, ` }`, `};`, ``)
|
|
137
|
+
});
|
|
138
|
+
return scaffold;
|
|
139
|
+
}
|
|
140
|
+
// ------------------------------------------------------------------ todolist
|
|
141
|
+
static buildTodolist(context) {
|
|
142
|
+
const scaffold = this.buildTailwind(context);
|
|
143
|
+
scaffold.folders.push('queries');
|
|
144
|
+
// The routed pages of the default app give way to the ones of the todo list
|
|
145
|
+
scaffold.files = scaffold.files.filter(file => !file.path.startsWith('apps/pages/'));
|
|
146
|
+
this.replaceFile(scaffold, `apps/${DEFAULT_APP_NAME}.tsx`, this.createTodolistApp());
|
|
147
|
+
this.replaceFile(scaffold, 'apps/components/Layout.tsx', this.createTodolistLayout(context.languages));
|
|
148
|
+
this.replaceFile(scaffold, 'readme.md', TemplateUtils_1.default.createReadmeMd(context.projectName, context.serviceName) + this.createTodolistReadme());
|
|
149
|
+
scaffold.files.push({ path: 'tables/todolist.ts', content: this.createTodoListTable() }, { path: 'tables/todo.ts', content: this.createTodoTable() }, { path: 'processes/todolists.ts', content: this.createTodoListProcesses() }, { path: 'processes/todos.ts', content: this.createTodoProcesses() }, { path: 'queries/all-todolists.cnql', content: this.createAllTodoListsQuery(context.serviceName) }, { path: 'queries/todos-of-list.cnql', content: this.createTodosOfListQuery(context.serviceName) }, { path: 'globals.i18n.csv', content: this.createTodolistI18n(context.languages) }, { path: 'apps/api.ts', content: this.createTodolistApi() }, { path: 'apps/pages/TodoListsPage.tsx', content: this.createTodoListsPage() }, { path: 'apps/pages/TodoListPage.tsx', content: this.createTodoListPage() });
|
|
150
|
+
scaffold.nextSteps.push(`Search the sources for 'TODO' to see the three points waiting for the runtime API`);
|
|
151
|
+
return scaffold;
|
|
152
|
+
}
|
|
153
|
+
static createTodoListTable() {
|
|
154
|
+
return this.lines(`import { defineEntity, type InferEntity, p } from '@codenotch/orm';`, ``, `export const TodoList = defineEntity({`, ` name: 'TodoList',`, ` properties: {`, ` id: p.uuid().primary(),`, ` name: p.string().notNull(),`, ` // Deleting a list deletes its todos`, ` todos: p.hasMany('Todo').linkBehaviour('Delete'),`, ` },`, `});`, ``, `export type ITodoList = InferEntity<typeof TodoList>;`, ``);
|
|
155
|
+
}
|
|
156
|
+
static createTodoTable() {
|
|
157
|
+
return this.lines(`import { defineEntity, type InferEntity, p } from '@codenotch/orm';`, ``, `export const Todo = defineEntity({`, ` name: 'Todo',`, ` properties: {`, ` id: p.uuid().primary(),`, ` title: p.string().notNull(),`, ` done: p.boolean().default(false),`, ` list: p.belongsTo('TodoList').notNull(),`, ` },`, `});`, ``, `export type ITodo = InferEntity<typeof Todo>;`, ``);
|
|
158
|
+
}
|
|
159
|
+
static createTodoListProcesses() {
|
|
160
|
+
return this.lines(`import { process, api, Context } from '@codenotch/process'`, `import type { ITodoList } from '../tables/todolist'`, ``, `// TODO: 'ctx.tables.<name>' follows the collections of the generated db-schema.ts ('todolists', 'todos').`, `// Adjust the names here if the runtime exposes the tables differently.`, ``, `export const listTodoLists = process({`, ` id: 'ListTodoLists',`, ` description: 'Every todo list, oldest first',`, ` auth: { scope: 'internal' },`, ` trigger: api.get('/todolists'),`, ` run: async (ctx: Context): Promise<ITodoList[]> => {`, ` return ctx.query<ITodoList>('queries/all-todolists.cnql')`, ` },`, `})`, ``, `export const createTodoList = process({`, ` id: 'CreateTodoList',`, ` description: 'Creates an empty todo list',`, ` auth: { scope: 'internal' },`, ` trigger: api.post('/todolists'),`, ` run: async (ctx: Context, input: { name: string }): Promise<{ id: string }> => {`, ` const id = ctx.uuid()`, ` await ctx.tables.todolists.write(id, { name: input.name })`, ` return { id }`, ` },`, `})`, ``, `export const deleteTodoList = process({`, ` id: 'DeleteTodoList',`, ` description: 'Deletes a todo list and, through the relation, its todos',`, ` auth: { scope: 'internal' },`, ` trigger: api.delete('/todolists/:listId'),`, ` run: async (ctx: Context, input: { listId: string }): Promise<{ deleted: boolean }> => {`, ` await ctx.tables.todolists.delete(input.listId)`, ` return { deleted: true }`, ` },`, `})`, ``);
|
|
161
|
+
}
|
|
162
|
+
static createTodoProcesses() {
|
|
163
|
+
return this.lines(`import { process, api, Context } from '@codenotch/process'`, `import type { ITodo } from '../tables/todo'`, ``, `export const listTodos = process({`, ` id: 'ListTodos',`, ` description: 'The todos of a list',`, ` auth: { scope: 'internal' },`, ` trigger: api.get('/todolists/:listId/todos'),`, ` run: async (ctx: Context, input: { listId: string }): Promise<ITodo[]> => {`, ` return ctx.query<ITodo>('queries/todos-of-list.cnql', { listId: input.listId })`, ` },`, `})`, ``, `export const createTodo = process({`, ` id: 'CreateTodo',`, ` description: 'Adds a todo to a list',`, ` auth: { scope: 'internal' },`, ` trigger: api.post('/todolists/:listId/todos'),`, ` run: async (ctx: Context, input: { listId: string; title: string }): Promise<{ id: string }> => {`, ` const id = ctx.uuid()`, ` await ctx.tables.todos.write(id, { title: input.title, done: false, listId: input.listId })`, ` return { id }`, ` },`, `})`, ``, `export const toggleTodo = process({`, ` id: 'ToggleTodo',`, ` description: 'Marks a todo as done, or as to do again',`, ` auth: { scope: 'internal' },`, ` trigger: api.put('/todos/:todoId/toggle'),`, ` run: async (ctx: Context, input: { todoId: string }): Promise<{ done: boolean }> => {`, ` const todo = await ctx.tables.todos.require(input.todoId)`, ` await ctx.tables.todos.write(todo.id, { done: !todo.done })`, ` return { done: !todo.done }`, ` },`, `})`, ``, `export const deleteTodo = process({`, ` id: 'DeleteTodo',`, ` description: 'Removes a todo',`, ` auth: { scope: 'internal' },`, ` trigger: api.delete('/todos/:todoId'),`, ` run: async (ctx: Context, input: { todoId: string }): Promise<{ deleted: boolean }> => {`, ` await ctx.tables.todos.delete(input.todoId)`, ` return { deleted: true }`, ` },`, `})`, ``);
|
|
164
|
+
}
|
|
165
|
+
static createAllTodoListsQuery(serviceName) {
|
|
166
|
+
return this.lines(`<CNQL xmlns="${serviceName}" PageSize="100" PageIndex="0">`, ` <TodoList Ref="results">`, ` <Id />`, ` <Name />`, ` <CreatedAt />`, ` </TodoList>`, `</CNQL>`, ``);
|
|
167
|
+
}
|
|
168
|
+
static createTodosOfListQuery(serviceName) {
|
|
169
|
+
return this.lines(`<!-- TODO: filter on the 'listId' parameter passed by the process. The filtering syntax of CNQL is not settled yet. -->`, `<CNQL xmlns="${serviceName}" PageSize="100" PageIndex="0">`, ` <Todo Ref="results">`, ` <Id />`, ` <Title />`, ` <Done />`, ` <ListId />`, ` <CreatedAt />`, ` </Todo>`, `</CNQL>`, ``);
|
|
170
|
+
}
|
|
171
|
+
/** One column per language of the manifest, filled for the ones the template knows */
|
|
172
|
+
static createTodolistI18n(languages) {
|
|
173
|
+
const translations = {
|
|
174
|
+
appTitle: { en: 'My todo lists', de: 'Meine Aufgabenlisten', fr: 'Mes listes de tâches' },
|
|
175
|
+
lists: { en: 'Lists', de: 'Listen', fr: 'Listes' },
|
|
176
|
+
newListPlaceholder: { en: 'Name of the new list', de: 'Name der neuen Liste', fr: 'Nom de la nouvelle liste' },
|
|
177
|
+
newTodoPlaceholder: { en: 'What has to be done?', de: 'Was ist zu tun?', fr: 'Que faut-il faire ?' },
|
|
178
|
+
add: { en: 'Add', de: 'Hinzufügen', fr: 'Ajouter' },
|
|
179
|
+
delete: { en: 'Delete', de: 'Löschen', fr: 'Supprimer' },
|
|
180
|
+
backToLists: { en: 'Back to the lists', de: 'Zurück zu den Listen', fr: 'Retour aux listes' },
|
|
181
|
+
noLists: { en: 'No list yet, create the first one.', de: 'Noch keine Liste, erstelle die erste.', fr: 'Aucune liste, créez la première.' },
|
|
182
|
+
noTodos: { en: 'Nothing to do here.', de: 'Hier gibt es nichts zu tun.', fr: 'Rien à faire ici.' },
|
|
183
|
+
todoCount: { en: '{0} todo(s), {1} done', de: '{0} Aufgabe(n), {1} erledigt', fr: '{0} tâche(s), {1} terminée(s)' },
|
|
184
|
+
loading: { en: 'Loading...', de: 'Laden...', fr: 'Chargement...' },
|
|
185
|
+
error: { en: 'Something went wrong: {0}', de: 'Etwas ist schiefgelaufen: {0}', fr: 'Une erreur est survenue : {0}' }
|
|
186
|
+
};
|
|
187
|
+
const quote = (value) => /[",\n]/.test(value) ? `"${value.replaceAll('"', '""')}"` : value;
|
|
188
|
+
const rows = Object.entries(translations).map(([key, values]) =>
|
|
189
|
+
// A language the template does not know gets the english text, to be translated
|
|
190
|
+
[key, ...languages.map(language => quote(values[language] ?? values['en']))].join(','));
|
|
191
|
+
return [['key', ...languages].join(','), ...rows].join('\n') + '\n';
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The calls the pages make, in one place. Typed with the row types of the tables, so the UI
|
|
195
|
+
* and the processes cannot drift apart silently.
|
|
196
|
+
*/
|
|
197
|
+
static createTodolistApi() {
|
|
198
|
+
return this.lines(`import { getCodenotch } from '@codenotch/codenotch.react';`, `import type { ITodoList } from '../tables/todolist';`, `import type { ITodo } from '../tables/todo';`, ``, `// TODO: startProcess() is not implemented yet in @codenotch/codenotch.react 2.0.1 (it throws 'Not implemented.').`, `// The calls below are typed against the processes of ./processes and will work as is once it lands;`, `// until then the pages show the error it raises.`, `async function call<TInput, TOutput>(processId: string, input: TInput): Promise<TOutput> {`, `\tconst result = await getCodenotch().startProcess<string, TInput, TOutput>(processId, input);`, `\tif (result.isError) {`, `\t\tthrow new Error(result.errorMessage);`, `\t}`, `\treturn result.output;`, `}`, ``, `export const listTodoLists = () => call<{}, ITodoList[]>('ListTodoLists', {});`, `export const createTodoList = (name: string) => call<{ name: string }, { id: string }>('CreateTodoList', { name });`, `export const deleteTodoList = (listId: string) => call<{ listId: string }, { deleted: boolean }>('DeleteTodoList', { listId });`, ``, `export const listTodos = (listId: string) => call<{ listId: string }, ITodo[]>('ListTodos', { listId });`, `export const createTodo = (listId: string, title: string) => call<{ listId: string; title: string }, { id: string }>('CreateTodo', { listId, title });`, `export const toggleTodo = (todoId: string) => call<{ todoId: string }, { done: boolean }>('ToggleTodo', { todoId });`, `export const deleteTodo = (todoId: string) => call<{ todoId: string }, { deleted: boolean }>('DeleteTodo', { todoId });`, ``);
|
|
199
|
+
}
|
|
200
|
+
static createTodolistApp() {
|
|
201
|
+
return this.lines(`import React from 'react';`, `import { HashRouter, Route, Routes } from 'react-router';`, `import './${DEFAULT_APP_NAME}.scss';`, `import Layout from './components/Layout';`, `import TodoListsPage from './pages/TodoListsPage';`, `import TodoListPage from './pages/TodoListPage';`, ``, `// HashRouter keeps the routing client side, whatever URL the runtime serves the app from`, `const ${DEFAULT_APP_NAME}: React.FC<{}> = () => {`, `\treturn <HashRouter>`, `\t\t<Routes>`, `\t\t\t<Route element={<Layout />}>`, `\t\t\t\t<Route index element={<TodoListsPage />} />`, `\t\t\t\t<Route path="lists/:listId" element={<TodoListPage />} />`, `\t\t\t</Route>`, `\t\t</Routes>`, `\t</HashRouter>;`, `};`, ``, `export default ${DEFAULT_APP_NAME};`, ``);
|
|
202
|
+
}
|
|
203
|
+
static createTodolistLayout(languages) {
|
|
204
|
+
return this.lines(`import React from 'react';`, `import { Link, Outlet } from 'react-router';`, `import { useCodenotch } from '@codenotch/codenotch.react';`, `import globals from '../../globals.i18n';`, ``, `// Shared frame of every page: title, language and theme switches, then the page itself in <Outlet />`, `const Layout: React.FC<{}> = () => {`, `\tconst cn = useCodenotch();`, `\tconst theme = cn.getTheme() ?? 'light';`, `\tconst language = cn.getLanguage() ?? '${languages[0] ?? 'en'}';`, ``, `\t// The generated translation class follows the language of the runtime`, `\tglobals.setLanguage(language);`, ``, `\treturn <div className="min-h-screen bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">`, `\t\t<nav className="flex items-center gap-4 border-b border-gray-200 px-6 py-3 dark:border-gray-700">`, `\t\t\t<Link to="/" className="text-lg font-semibold">{globals.appTitle()}</Link>`, `\t\t\t<div className="ml-auto flex items-center gap-2 text-sm">`, `\t\t\t\t{cn.getLanguages().map(lang =>`, `\t\t\t\t\t<button key={lang} onClick={() => cn.setLanguage(lang)}`, `\t\t\t\t\t\tclassName={'rounded px-2 py-1 ' + (lang === language ? 'bg-gray-200 dark:bg-gray-700' : 'hover:underline')}>`, `\t\t\t\t\t\t{lang.toUpperCase()}`, `\t\t\t\t\t</button>)}`, `\t\t\t\t<button className="rounded border border-gray-300 px-3 py-1 dark:border-gray-600" onClick={() => cn.setTheme(theme === 'dark' ? 'light' : 'dark')}>`, `\t\t\t\t\t{theme === 'dark' ? 'Light' : 'Dark'}`, `\t\t\t\t</button>`, `\t\t\t</div>`, `\t\t</nav>`, `\t\t<main className="mx-auto max-w-2xl p-6">`, `\t\t\t<Outlet />`, `\t\t</main>`, `\t</div>;`, `};`, ``, `export default Layout;`, ``);
|
|
205
|
+
}
|
|
206
|
+
static createTodoListsPage() {
|
|
207
|
+
return this.lines(`import React, { useEffect, useState } from 'react';`, `import { Link } from 'react-router';`, `import type { ITodoList } from '../../tables/todolist';`, `import globals from '../../globals.i18n';`, `import { createTodoList, deleteTodoList, listTodoLists } from '../api';`, ``, `const TodoListsPage: React.FC<{}> = () => {`, `\tconst [lists, setLists] = useState<ITodoList[] | undefined>(undefined);`, `\tconst [name, setName] = useState('');`, `\tconst [error, setError] = useState<string | undefined>(undefined);`, ``, `\tconst refresh = () => listTodoLists().then(setLists).catch(e => setError(String(e)));`, ``, `\tuseEffect(() => { refresh(); }, []);`, ``, `\tconst add = async (event: React.FormEvent) => {`, `\t\tevent.preventDefault();`, `\t\tif (name.trim() === '') {`, `\t\t\treturn;`, `\t\t}`, `\t\ttry {`, `\t\t\tawait createTodoList(name.trim());`, `\t\t\tsetName('');`, `\t\t\tawait refresh();`, `\t\t}`, `\t\tcatch (e) {`, `\t\t\tsetError(String(e));`, `\t\t}`, `\t};`, ``, `\tconst remove = async (listId: string) => {`, `\t\ttry {`, `\t\t\tawait deleteTodoList(listId);`, `\t\t\tawait refresh();`, `\t\t}`, `\t\tcatch (e) {`, `\t\t\tsetError(String(e));`, `\t\t}`, `\t};`, ``, `\treturn <div>`, `\t\t<h1 className="text-2xl font-bold">{globals.lists()}</h1>`, ``, `\t\t<form onSubmit={add} className="mt-4 flex gap-2">`, `\t\t\t<input value={name} onChange={e => setName(e.target.value)} placeholder={globals.newListPlaceholder()}`, `\t\t\t\tclassName="flex-1 rounded border border-gray-300 px-3 py-2 dark:border-gray-600 dark:bg-gray-800" />`, `\t\t\t<button type="submit" className="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700">{globals.add()}</button>`, `\t\t</form>`, ``, `\t\t{error && <p className="mt-4 rounded bg-red-100 p-3 text-red-800 dark:bg-red-900 dark:text-red-200">{globals.error(error)}</p>}`, `\t\t{lists === undefined && !error && <p className="mt-4 text-gray-500">{globals.loading()}</p>}`, `\t\t{lists !== undefined && lists.length === 0 && <p className="mt-4 text-gray-500">{globals.noLists()}</p>}`, ``, `\t\t<ul className="mt-4 divide-y divide-gray-200 dark:divide-gray-700">`, `\t\t\t{lists?.map(list =>`, `\t\t\t\t<li key={list.id} className="flex items-center justify-between py-3">`, `\t\t\t\t\t<Link to={'/lists/' + list.id} className="hover:underline">{list.name}</Link>`, `\t\t\t\t\t<button onClick={() => remove(list.id)} className="text-sm text-red-600 hover:underline">{globals.delete()}</button>`, `\t\t\t\t</li>)}`, `\t\t</ul>`, `\t</div>;`, `};`, ``, `export default TodoListsPage;`, ``);
|
|
208
|
+
}
|
|
209
|
+
static createTodoListPage() {
|
|
210
|
+
return this.lines(`import React, { useEffect, useState } from 'react';`, `import { Link, useParams } from 'react-router';`, `import type { ITodo } from '../../tables/todo';`, `import globals from '../../globals.i18n';`, `import { createTodo, deleteTodo, listTodos, toggleTodo } from '../api';`, ``, `const TodoListPage: React.FC<{}> = () => {`, `\tconst { listId } = useParams<{ listId: string }>();`, `\tconst [todos, setTodos] = useState<ITodo[] | undefined>(undefined);`, `\tconst [title, setTitle] = useState('');`, `\tconst [error, setError] = useState<string | undefined>(undefined);`, ``, `\tconst refresh = () => listTodos(listId!).then(setTodos).catch(e => setError(String(e)));`, ``, `\tuseEffect(() => { refresh(); }, [listId]);`, ``, `\t// Every change goes through a process, then the list is read again: the server is the truth`, `\tconst run = async (action: () => Promise<unknown>) => {`, `\t\ttry {`, `\t\t\tawait action();`, `\t\t\tawait refresh();`, `\t\t}`, `\t\tcatch (e) {`, `\t\t\tsetError(String(e));`, `\t\t}`, `\t};`, ``, `\tconst add = (event: React.FormEvent) => {`, `\t\tevent.preventDefault();`, `\t\tif (title.trim() !== '') {`, `\t\t\trun(() => createTodo(listId!, title.trim())).then(() => setTitle(''));`, `\t\t}`, `\t};`, ``, `\tconst done = todos?.filter(todo => todo.done).length ?? 0;`, ``, `\treturn <div>`, `\t\t<Link to="/" className="text-sm underline">{globals.backToLists()}</Link>`, `\t\t<h1 className="mt-2 text-2xl font-bold">{globals.appTitle()}</h1>`, `\t\t{todos !== undefined && <p className="text-sm text-gray-500">{globals.todoCount(String(todos.length), String(done))}</p>}`, ``, `\t\t<form onSubmit={add} className="mt-4 flex gap-2">`, `\t\t\t<input value={title} onChange={e => setTitle(e.target.value)} placeholder={globals.newTodoPlaceholder()}`, `\t\t\t\tclassName="flex-1 rounded border border-gray-300 px-3 py-2 dark:border-gray-600 dark:bg-gray-800" />`, `\t\t\t<button type="submit" className="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700">{globals.add()}</button>`, `\t\t</form>`, ``, `\t\t{error && <p className="mt-4 rounded bg-red-100 p-3 text-red-800 dark:bg-red-900 dark:text-red-200">{globals.error(error)}</p>}`, `\t\t{todos === undefined && !error && <p className="mt-4 text-gray-500">{globals.loading()}</p>}`, `\t\t{todos !== undefined && todos.length === 0 && <p className="mt-4 text-gray-500">{globals.noTodos()}</p>}`, ``, `\t\t<ul className="mt-4 divide-y divide-gray-200 dark:divide-gray-700">`, `\t\t\t{todos?.map(todo =>`, `\t\t\t\t<li key={todo.id} className="flex items-center gap-3 py-3">`, `\t\t\t\t\t<input type="checkbox" checked={todo.done} onChange={() => run(() => toggleTodo(todo.id))} />`, `\t\t\t\t\t<span className={'flex-1 ' + (todo.done ? 'text-gray-400 line-through' : '')}>{todo.title}</span>`, `\t\t\t\t\t<button onClick={() => run(() => deleteTodo(todo.id))} className="text-sm text-red-600 hover:underline">{globals.delete()}</button>`, `\t\t\t\t</li>)}`, `\t\t</ul>`, `\t</div>;`, `};`, ``, `export default TodoListPage;`, ``);
|
|
211
|
+
}
|
|
212
|
+
static createTodolistReadme() {
|
|
213
|
+
return this.lines(``, `## The todo list template`, ``, `| Path | Role |`, `| --- | --- |`, `| \`tables/todolist.ts\`, \`tables/todo.ts\` | the two tables, a list having many todos |`, `| \`processes/todolists.ts\`, \`processes/todos.ts\` | one process per operation, each exposed as an API endpoint |`, `| \`queries/*.cnql\` | the read queries the processes run |`, `| \`globals.i18n.csv\` | the texts of the app in every language of the manifest |`, `| \`apps/api.ts\` | the typed calls the pages make to the processes |`, `| \`apps/pages/\` | the list of lists, and the todos of one list |`, `| \`db-schema.xml\`, \`db-schema.ts\`, \`globals.i18n.ts\`, \`openapi.json\` | generated from the files above, never edit them |`, ``, `After a change to the tables, the translations or the processes, regenerate the derived files:`, ``, '```sh', `cn generate dbcontext -f .`, `cn generate i18n -f globals.i18n.csv`, `cn generate openapi -f .`, '```', ``, `Three points wait for the runtime API and are marked \`TODO\` in the sources: the client call of a`, `process (\`apps/api.ts\`), the filter of a query (\`queries/todos-of-list.cnql\`) and the names the`, `tables are reached under from a process (\`processes/*.ts\`).`, ``);
|
|
214
|
+
}
|
|
215
|
+
// ------------------------------------------------------------------ api
|
|
216
|
+
/**
|
|
217
|
+
* A project exposing an API and nothing else: no app, no React, no translations.
|
|
218
|
+
* The product catalog it manages is small on purpose, every kind of endpoint is there once.
|
|
219
|
+
*/
|
|
220
|
+
static buildApi(context) {
|
|
221
|
+
const manifest = ManifestUtils_1.default.createManifest(context.projectName, { serviceName: context.serviceName });
|
|
222
|
+
manifest.languages = [...context.languages];
|
|
223
|
+
return {
|
|
224
|
+
folders: [...TemplateUtils_1.default.PROJECT_FOLDERS.filter(folder => folder !== 'apps'), 'queries'],
|
|
225
|
+
files: [
|
|
226
|
+
{ path: 'manifest.json', content: ManifestUtils_1.default.serialize(manifest) },
|
|
227
|
+
{ path: 'readme.md', content: TemplateUtils_1.default.createReadmeMd(context.projectName, context.serviceName) + this.createApiReadme() },
|
|
228
|
+
{ path: '.gitignore', content: TemplateUtils_1.default.createGitignore() },
|
|
229
|
+
{ path: 'package.json', content: this.toJson(TemplateUtils_1.default.createPackageJson(context.serviceName, {}, false)) },
|
|
230
|
+
{ path: 'tables/product.ts', content: this.createProductTable() },
|
|
231
|
+
{ path: 'processes/products.ts', content: this.createProductProcesses() },
|
|
232
|
+
{ path: 'processes/maintenance.ts', content: this.createMaintenanceProcesses() },
|
|
233
|
+
{ path: 'queries/all-products.cnql', content: this.createAllProductsQuery(context.serviceName) },
|
|
234
|
+
{ path: 'queries/low-stock.cnql', content: this.createLowStockQuery(context.serviceName) }
|
|
235
|
+
],
|
|
236
|
+
nextSteps: [
|
|
237
|
+
exports.NPM_INSTALL_STEP,
|
|
238
|
+
'cn project start',
|
|
239
|
+
`Search the sources for 'TODO' to see the points waiting for the runtime API`
|
|
240
|
+
]
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
static createProductTable() {
|
|
244
|
+
return this.lines(`import { defineEntity, type InferEntity, p } from '@codenotch/orm';`, ``, `export const Product = defineEntity({`, ` name: 'Product',`, ` properties: {`, ` id: p.uuid().primary(),`, ` sku: p.string().notNull().unique(),`, ` name: p.string().notNull(),`, ` price: p.decimal().notNull(),`, ` stock: p.integer().default(0),`, ` active: p.boolean().default(true),`, ` },`, `});`, ``, `export type IProduct = InferEntity<typeof Product>;`, ``);
|
|
245
|
+
}
|
|
246
|
+
/** One process per operation of the catalog, each exposed as an endpoint */
|
|
247
|
+
static createProductProcesses() {
|
|
248
|
+
return this.lines(`import { process, api, Context } from '@codenotch/process'`, `import type { IProduct } from '../tables/product'`, ``, `// TODO: 'ctx.tables.<name>' follows the collections of the generated db-schema.ts ('products').`, `// Adjust the name here if the runtime exposes the table differently.`, ``, `export const listProducts = process({`, ` id: 'ListProducts',`, ` description: 'The products of the catalog, optionally the active ones only',`, ` auth: { scope: 'public' },`, ` trigger: api.get('/products'),`, ` run: async (ctx: Context, input: { activeOnly?: boolean }): Promise<IProduct[]> => {`, ` return ctx.query<IProduct>('queries/all-products.cnql', { activeOnly: input.activeOnly ?? false })`, ` },`, `})`, ``, `export const getProduct = process({`, ` id: 'GetProduct',`, ` description: 'One product by id, 404 when it does not exist',`, ` auth: { scope: 'public' },`, ` trigger: api.get('/products/:productId'),`, ` run: async (ctx: Context, input: { productId: string }): Promise<IProduct> => {`, ` // require() throws when the id is unknown, which an api process turns into a 404`, ` return ctx.tables.products.require(input.productId)`, ` },`, `})`, ``, `export const createProduct = process({`, ` id: 'CreateProduct',`, ` description: 'Adds a product to the catalog',`, ` auth: { scope: 'internal' },`, ` trigger: api.post('/products'),`, ` run: async (ctx: Context, input: { sku: string; name: string; price: number; stock?: number }): Promise<{ id: string }> => {`, ` const id = ctx.uuid()`, ` await ctx.tables.products.write(id, { sku: input.sku, name: input.name, price: input.price, stock: input.stock ?? 0, active: true })`, ` return { id }`, ` },`, `})`, ``, `export const updateProduct = process({`, ` id: 'UpdateProduct',`, ` description: 'Changes the given fields of a product, the others are left as they are',`, ` auth: { scope: 'internal' },`, ` trigger: api.put('/products/:productId'),`, ` run: async (ctx: Context, input: { productId: string; name?: string; price?: number; stock?: number; active?: boolean }): Promise<{ updated: boolean }> => {`, ` const { productId, ...changes } = input`, ` // Only the fields actually sent are written, an absent one must not erase the current value`, ` const patch = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== undefined))`, ` await ctx.tables.products.require(productId)`, ` await ctx.tables.products.write(productId, patch)`, ` return { updated: true }`, ` },`, `})`, ``, `export const deleteProduct = process({`, ` id: 'DeleteProduct',`, ` description: 'Removes a product from the catalog',`, ` auth: { scope: 'internal' },`, ` trigger: api.delete('/products/:productId'),`, ` run: async (ctx: Context, input: { productId: string }): Promise<{ deleted: boolean }> => {`, ` await ctx.tables.products.delete(input.productId)`, ` return { deleted: true }`, ` },`, `})`, ``);
|
|
249
|
+
}
|
|
250
|
+
/** What an API project runs on its own: a health check and a scheduled report */
|
|
251
|
+
static createMaintenanceProcesses() {
|
|
252
|
+
return this.lines(`import { process, api, timer, Context } from '@codenotch/process'`, `import type { IProduct } from '../tables/product'`, ``, `export const health = process({`, ` id: 'Health',`, ` description: 'Tells the monitoring the service is up',`, ` auth: { scope: 'public' },`, ` trigger: api.get('/health'),`, ` run: async (): Promise<{ status: 'ok' }> => ({ status: 'ok' }),`, `})`, ``, `// No endpoint here: the runtime starts this one every morning at 7`, `export const lowStockReport = process({`, ` id: 'LowStockReport',`, ` description: 'Logs the products running out of stock',`, ` auth: { scope: 'system' },`, ` trigger: timer('0 7 * * *'),`, ` run: async (ctx: Context): Promise<{ lowStock: number }> => {`, ` const products = await ctx.query<IProduct>('queries/low-stock.cnql', { threshold: 5 })`, ` for (const product of products) {`, ` ctx.log.info('low stock', { sku: product.sku, stock: product.stock })`, ` }`, ` return { lowStock: products.length }`, ` },`, `})`, ``);
|
|
253
|
+
}
|
|
254
|
+
static createAllProductsQuery(serviceName) {
|
|
255
|
+
return this.lines(`<!-- TODO: filter on 'activeOnly' when it is true. The filtering syntax of CNQL is not settled yet. -->`, `<CNQL xmlns="${serviceName}" PageSize="100" PageIndex="0">`, ` <Product Ref="results">`, ` <Id />`, ` <Sku />`, ` <Name />`, ` <Price />`, ` <Stock />`, ` <Active />`, ` </Product>`, `</CNQL>`, ``);
|
|
256
|
+
}
|
|
257
|
+
static createLowStockQuery(serviceName) {
|
|
258
|
+
return this.lines(`<!-- TODO: keep the products whose Stock is below the 'threshold' parameter. The filtering syntax of CNQL is not settled yet. -->`, `<CNQL xmlns="${serviceName}" PageSize="100" PageIndex="0">`, ` <Product Ref="results">`, ` <Id />`, ` <Sku />`, ` <Stock />`, ` </Product>`, `</CNQL>`, ``);
|
|
259
|
+
}
|
|
260
|
+
static createApiReadme() {
|
|
261
|
+
return this.lines(``, `## The API template`, ``, `This project has no app: it exposes the endpoints of \`openapi.json\` and nothing else, so`, `\`package.json\` carries no React at all.`, ``, `| Path | Role |`, `| --- | --- |`, `| \`tables/product.ts\` | the catalog table |`, `| \`processes/products.ts\` | one process per operation, each exposed as an endpoint |`, `| \`processes/maintenance.ts\` | the health check, and a report the runtime schedules every morning |`, `| \`queries/*.cnql\` | the read queries the processes run |`, `| \`db-schema.xml\`, \`db-schema.ts\`, \`openapi.json\` | generated from the files above, never edit them |`, ``, `After a change to the table or the processes, regenerate the derived files:`, ``, '```sh', `cn generate dbcontext -f .`, `cn generate openapi -f .`, '```', ``, `Two points wait for the runtime API and are marked \`TODO\` in the sources: the filters of the`, `queries (\`queries/*.cnql\`) and the name the table is reached under from a process (\`processes/*.ts\`).`, ``);
|
|
262
|
+
}
|
|
263
|
+
// ------------------------------------------------------------------ helpers
|
|
264
|
+
/** Joins lines, dropping the undefined ones so that optional lines read naturally */
|
|
265
|
+
static lines(...lines) {
|
|
266
|
+
return lines.filter((line) => line !== undefined).join('\n');
|
|
267
|
+
}
|
|
268
|
+
/** ` className="..."` when the template is styled, nothing otherwise */
|
|
269
|
+
static classes(styled) {
|
|
270
|
+
return classNames => styled ? ` className="${classNames}"` : '';
|
|
271
|
+
}
|
|
272
|
+
static replaceFile(scaffold, path, content) {
|
|
273
|
+
const file = scaffold.files.find(candidate => candidate.path === path);
|
|
274
|
+
if (file === undefined) {
|
|
275
|
+
throw new Error(`Template file '${path}' not found`);
|
|
276
|
+
}
|
|
277
|
+
file.content = content;
|
|
278
|
+
}
|
|
279
|
+
static toJson(value) {
|
|
280
|
+
return JSON.stringify(value, null, 4) + '\n';
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
exports.default = ProjectTemplates;
|
|
284
|
+
//# sourceMappingURL=ProjectTemplates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProjectTemplates.js","sourceRoot":"","sources":["../../src/utils/ProjectTemplates.ts"],"names":[],"mappings":"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"}
|