aegisnode 0.0.4 → 0.1.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.
@@ -1,7 +1,11 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { loadProjectConfig } from '../../runtime/config.js';
4
- import { resolveProjectRoot } from '../utils/project.js';
4
+ import { ensureValidName } from '../utils/fs.js';
5
+ import { getProjectSourceExtension, resolveProjectRoot } from '../utils/project.js';
6
+ import { getAppScaffoldEntries, toImportName } from '../utils/apps.js';
7
+ import { withSourceExtension } from '../utils/scaffolds.js';
8
+ import { resolveSourceFile } from '../../utils/source-files.js';
5
9
 
6
10
  function createCollector() {
7
11
  const entries = [];
@@ -26,25 +30,49 @@ async function fileExists(filePath) {
26
30
  }
27
31
  }
28
32
 
29
- async function runAppChecks(rootDir, config, collector) {
33
+ function escapeRegExp(value) {
34
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
35
+ }
36
+
37
+ async function runAppChecks(rootDir, config, collector, targetAppName = null) {
30
38
  const apps = Array.isArray(config.apps) ? config.apps : [];
39
+ const sourceExtension = getProjectSourceExtension(rootDir);
40
+ const routesFile = resolveSourceFile(path.join(rootDir, 'routes'), [sourceExtension]) || resolveSourceFile(path.join(rootDir, 'routes'));
41
+ const routesLabel = routesFile ? path.basename(routesFile) : withSourceExtension('routes', sourceExtension);
42
+ const declaredApps = new Map();
43
+
44
+ for (const app of apps) {
45
+ const appName = app?.name;
46
+ if (typeof appName === 'string' && appName.trim().length > 0) {
47
+ declaredApps.set(appName, app);
48
+ }
49
+ }
31
50
 
32
- if (apps.length === 0) {
51
+ if (!targetAppName && apps.length === 0) {
33
52
  collector.warn('No apps declared in settings.apps.');
34
53
  return;
35
54
  }
36
55
 
37
- collector.ok(`Declared apps: ${apps.map((app) => app.name).join(', ')}`);
56
+ if (!targetAppName) {
57
+ collector.ok(`Declared apps: ${apps.map((app) => app.name).join(', ')}`);
58
+ }
38
59
 
39
- for (const app of apps) {
40
- const appName = app?.name;
60
+ const routesFileExists = await fileExists(routesFile);
61
+ const routesContent = routesFileExists ? await fs.readFile(routesFile, 'utf8') : '';
62
+ const targetApps = targetAppName ? [targetAppName] : apps;
63
+
64
+ for (const appEntry of targetApps) {
65
+ const appName = targetAppName || appEntry?.name;
66
+ const app = targetAppName ? declaredApps.get(appName) || null : appEntry;
41
67
  const mount = app?.mount;
42
68
  if (typeof appName !== 'string' || appName.trim().length === 0) {
43
- collector.error(`Invalid app entry: ${JSON.stringify(app)}`);
69
+ collector.error(`Invalid app entry: ${JSON.stringify(appEntry)}`);
44
70
  continue;
45
71
  }
46
72
 
47
- if (typeof mount !== 'string' || !mount.startsWith('/')) {
73
+ if (!app) {
74
+ collector.warn(`App "${appName}" is not declared in settings.apps.`);
75
+ } else if (typeof mount !== 'string' || !mount.startsWith('/')) {
48
76
  collector.error(`App "${appName}" has invalid mount "${String(mount)}" (must start with /).`);
49
77
  }
50
78
 
@@ -55,34 +83,56 @@ async function runAppChecks(rootDir, config, collector) {
55
83
  continue;
56
84
  }
57
85
 
58
- const requiredFiles = ['routes.js', 'views.js', 'services.js', 'models.js', 'validators.js'];
59
- for (const fileName of requiredFiles) {
60
- const target = path.join(appRoot, fileName);
86
+ for (const entry of getAppScaffoldEntries(appName, sourceExtension)) {
87
+ const target = path.join(rootDir, entry.target);
61
88
  if (!(await fileExists(target))) {
62
- collector.warn(`App "${appName}" missing ${fileName}.`);
89
+ collector.warn(`App "${appName}" missing ${path.relative(appRoot, target)}.`);
63
90
  }
64
91
  }
65
92
 
66
- const subscribersFile = path.join(appRoot, 'subscribers.js');
67
- if (!(await fileExists(subscribersFile))) {
68
- collector.warn(`App "${appName}" missing subscribers.js.`);
93
+ if (!app) {
94
+ continue;
95
+ }
96
+
97
+ if (config.autoMountApps === true) {
98
+ collector.ok(`App "${appName}" will be mounted automatically from settings.apps.`);
99
+ continue;
100
+ }
101
+
102
+ if (!routesFileExists) {
103
+ collector.warn(`Project ${routesLabel} is missing; app "${appName}" cannot be mounted centrally.`);
104
+ continue;
105
+ }
106
+
107
+ const importPath = `./apps/${appName}/routes${sourceExtension}`;
108
+ const importName = toImportName(appName);
109
+ const routePattern = new RegExp(`route\\.use\\([^\\n]*,\\s*${escapeRegExp(importName)}\\s*\\);`);
110
+
111
+ if (!routesContent.includes(importPath)) {
112
+ collector.warn(`Project ${routesLabel} is missing import for app "${appName}".`);
113
+ }
114
+
115
+ if (!routePattern.test(routesContent)) {
116
+ collector.warn(`Project ${routesLabel} is missing route.use(...) mount for app "${appName}".`);
69
117
  }
70
118
  }
71
119
  }
72
120
 
73
121
  async function runStartupEntryChecks(rootDir, config, collector) {
74
122
  const env = String(config.env || process.env.NODE_ENV || 'development').trim().toLowerCase();
75
- const appEntryPath = path.join(rootDir, 'app.js');
123
+ const sourceExtension = getProjectSourceExtension(rootDir);
124
+ const appEntryPath = resolveSourceFile(path.join(rootDir, 'app'), [sourceExtension])
125
+ || path.join(rootDir, withSourceExtension('app', sourceExtension));
76
126
  const loaderEntryPath = path.join(rootDir, 'loader.cjs');
77
127
  const appEntryExists = await fileExists(appEntryPath);
78
128
  const loaderEntryExists = await fileExists(loaderEntryPath);
79
129
 
80
130
  if (appEntryExists) {
81
- collector.ok('app.js exists.');
131
+ collector.ok(`${path.basename(appEntryPath)} exists.`);
82
132
  } else if (env === 'production') {
83
- collector.error('app.js is missing for production startup. Run "aegisnode generateloader" to restore startup entry files.');
133
+ collector.error(`${path.basename(appEntryPath)} is missing for production startup. Run "aegisnode generateloader" to restore startup entry files.`);
84
134
  } else {
85
- collector.warn('app.js is missing. Run "aegisnode generateloader" to restore startup entry files.');
135
+ collector.warn(`${path.basename(appEntryPath)} is missing. Run "aegisnode generateloader" to restore startup entry files.`);
86
136
  }
87
137
 
88
138
  if (loaderEntryExists) {
@@ -193,7 +243,12 @@ export async function runDoctor({
193
243
  projectRoot,
194
244
  failOnError = true,
195
245
  output = console,
246
+ appName = null,
196
247
  } = {}) {
248
+ if (appName) {
249
+ ensureValidName(appName, 'app');
250
+ }
251
+
197
252
  const resolvedRoot = await resolveProjectRoot(projectRoot || process.cwd());
198
253
  const collector = createCollector();
199
254
 
@@ -202,12 +257,17 @@ export async function runDoctor({
202
257
  const config = await loadProjectConfig(resolvedRoot);
203
258
  collector.ok(`Environment: ${config.env || 'development'}`);
204
259
 
205
- await runAppChecks(resolvedRoot, config, collector);
206
- await runStartupEntryChecks(resolvedRoot, config, collector);
207
- runSecurityChecks(config, collector);
208
- runAuthChecks(config, collector);
209
- runApiChecks(config, collector);
210
- await runTemplateChecks(resolvedRoot, config, collector);
260
+ if (appName) {
261
+ collector.ok(`Doctor scope: app "${appName}"`);
262
+ await runAppChecks(resolvedRoot, config, collector, appName);
263
+ } else {
264
+ await runAppChecks(resolvedRoot, config, collector);
265
+ await runStartupEntryChecks(resolvedRoot, config, collector);
266
+ runSecurityChecks(config, collector);
267
+ runAuthChecks(config, collector);
268
+ runApiChecks(config, collector);
269
+ await runTemplateChecks(resolvedRoot, config, collector);
270
+ }
211
271
 
212
272
  const summary = printSummary(collector.entries, output);
213
273
 
@@ -0,0 +1,70 @@
1
+ import path from 'path';
2
+ import { ensureValidName, normalizeMountPath } from '../utils/fs.js';
3
+ import { getProjectSourceExtension, resolveProjectRoot } from '../utils/project.js';
4
+ import { withSourceExtension } from '../utils/scaffolds.js';
5
+ import {
6
+ detectSettingsMode,
7
+ ensureAppScaffold,
8
+ readAppsConfig,
9
+ updateAppRegistry,
10
+ updateProjectRoutesFile,
11
+ } from '../utils/apps.js';
12
+
13
+ export async function runFixApp({ appName, projectRoot, mount } = {}) {
14
+ if (!appName) {
15
+ throw new Error('Missing app name. Usage: aegisnode fix --app <app-name>');
16
+ }
17
+
18
+ ensureValidName(appName, 'app');
19
+
20
+ const resolvedRoot = await resolveProjectRoot(projectRoot || process.cwd());
21
+ const sourceExtension = getProjectSourceExtension(resolvedRoot);
22
+ const settingsMode = await detectSettingsMode(resolvedRoot);
23
+ const existingApps = await readAppsConfig(settingsMode);
24
+ const existingApp = existingApps.find((entry) => entry.name === appName) || null;
25
+ const appMount = existingApp?.mount || normalizeMountPath(mount || `/${appName}`);
26
+
27
+ const scaffoldResult = await ensureAppScaffold(resolvedRoot, appName, {
28
+ overwrite: false,
29
+ sourceExtension,
30
+ });
31
+
32
+ let registryUpdated = false;
33
+ if (!existingApp) {
34
+ await updateAppRegistry(
35
+ resolvedRoot,
36
+ [...existingApps, { name: appName, mount: appMount }],
37
+ settingsMode,
38
+ );
39
+ registryUpdated = true;
40
+ }
41
+
42
+ const routesResult = await updateProjectRoutesFile(resolvedRoot, appName, appMount, sourceExtension);
43
+ const relativeWritten = scaffoldResult.written.map((target) => path.relative(resolvedRoot, target));
44
+
45
+ if (relativeWritten.length === 0 && !registryUpdated && !routesResult.updatedImport && !routesResult.updatedRoute) {
46
+ console.log(`App "${appName}" is already complete.`);
47
+ } else {
48
+ console.log(`App "${appName}" repaired at ${resolvedRoot}/apps/${appName}`);
49
+ if (relativeWritten.length > 0) {
50
+ console.log(`Created missing files: ${relativeWritten.join(', ')}`);
51
+ }
52
+ if (registryUpdated) {
53
+ console.log(`Added app "${appName}" to settings.apps with mount ${appMount}`);
54
+ }
55
+ if (routesResult.updatedImport || routesResult.updatedRoute) {
56
+ console.log(`Updated ${path.basename(routesResult.routesFile || withSourceExtension('routes', sourceExtension))} registration for app "${appName}"`);
57
+ }
58
+ }
59
+
60
+ return {
61
+ rootDir: resolvedRoot,
62
+ appName,
63
+ mount: appMount,
64
+ createdFiles: scaffoldResult.written,
65
+ skippedFiles: scaffoldResult.skipped,
66
+ registryUpdated,
67
+ routesUpdated: routesResult.updatedImport || routesResult.updatedRoute,
68
+ routesFile: routesResult.routesFile,
69
+ };
70
+ }
@@ -1,7 +1,9 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { ensureValidName, exists, writeFile } from '../utils/fs.js';
4
- import { toPascalCase } from '../utils/scaffolds.js';
4
+ import { toPascalCase, withSourceExtension } from '../utils/scaffolds.js';
5
+ import { getProjectSourceExtension } from '../utils/project.js';
6
+ import { resolveSourceFile, resolveSourceIndexFile } from '../../utils/source-files.js';
5
7
 
6
8
  const SUPPORTED_TYPES = new Set(['view', 'controller', 'model', 'validator', 'dto', 'service', 'subscriber', 'route']);
7
9
 
@@ -22,12 +24,14 @@ function assertType(type) {
22
24
  }
23
25
 
24
26
  async function assertProjectRoot(projectRoot) {
25
- const hasSingleSettings = await exists(path.join(projectRoot, 'settings.js'));
26
- const hasLegacySettings = (await exists(path.join(projectRoot, 'settings', 'index.js')))
27
- || (await exists(path.join(projectRoot, 'settings', 'apps.js')));
27
+ const hasSingleSettings = Boolean(resolveSourceFile(path.join(projectRoot, 'settings')));
28
+ const hasLegacySettings = Boolean(
29
+ resolveSourceIndexFile(path.join(projectRoot, 'settings'))
30
+ || resolveSourceFile(path.join(projectRoot, 'settings', 'apps')),
31
+ );
28
32
 
29
33
  if (!hasSingleSettings && !hasLegacySettings) {
30
- throw new Error('Not an AegisNode project root: missing settings.js');
34
+ throw new Error('Not an AegisNode project root: missing settings.js/settings.ts');
31
35
  }
32
36
  }
33
37
 
@@ -110,35 +114,35 @@ function renderSubscriber(name, appName) {
110
114
  `;
111
115
  }
112
116
 
113
- async function getAppRoutesFile(appRoot) {
114
- const primary = path.join(appRoot, 'routes.js');
115
- if (await exists(primary)) {
117
+ async function getAppRoutesFile(appRoot, sourceExtension) {
118
+ const primary = resolveSourceFile(path.join(appRoot, 'routes'), [sourceExtension]) || resolveSourceFile(path.join(appRoot, 'routes'));
119
+ if (primary && await exists(primary)) {
116
120
  return primary;
117
121
  }
118
122
 
119
- const legacy = path.join(appRoot, 'routes', 'index.js');
120
- if (await exists(legacy)) {
123
+ const legacy = resolveSourceIndexFile(path.join(appRoot, 'routes'), [sourceExtension]) || resolveSourceIndexFile(path.join(appRoot, 'routes'));
124
+ if (legacy && await exists(legacy)) {
121
125
  return legacy;
122
126
  }
123
127
 
124
- throw new Error(`Missing app routes file in ${appRoot}. Expected routes.js or routes/index.js`);
128
+ throw new Error(`Missing app routes file in ${appRoot}. Expected routes${sourceExtension} or routes/index${sourceExtension}`);
125
129
  }
126
130
 
127
- async function appendRouteToApp({ appRoot, routeName }) {
128
- const routesFile = await getAppRoutesFile(appRoot);
129
- const usesFlatRoutesFile = path.basename(routesFile) === 'routes.js';
130
- const flatViewPath = path.join(appRoot, `${routeName}.view.js`);
131
- const nestedViewPath = path.join(appRoot, 'views', `${routeName}.view.js`);
131
+ async function appendRouteToApp({ appRoot, routeName, sourceExtension }) {
132
+ const routesFile = await getAppRoutesFile(appRoot, sourceExtension);
133
+ const usesFlatRoutesFile = path.basename(routesFile) === withSourceExtension('routes', sourceExtension);
134
+ const flatViewPath = path.join(appRoot, `${routeName}.view${sourceExtension}`);
135
+ const nestedViewPath = path.join(appRoot, 'views', `${routeName}.view${sourceExtension}`);
132
136
 
133
137
  let importPath = null;
134
138
  if (await exists(flatViewPath)) {
135
139
  importPath = usesFlatRoutesFile
136
- ? `./${routeName}.view.js`
137
- : `../${routeName}.view.js`;
140
+ ? `./${routeName}.view${sourceExtension}`
141
+ : `../${routeName}.view${sourceExtension}`;
138
142
  } else if (await exists(nestedViewPath)) {
139
143
  importPath = usesFlatRoutesFile
140
- ? `./views/${routeName}.view.js`
141
- : `../views/${routeName}.view.js`;
144
+ ? `./views/${routeName}.view${sourceExtension}`
145
+ : `../views/${routeName}.view${sourceExtension}`;
142
146
  } else {
143
147
  throw new Error(`Missing view for route generation: ${flatViewPath} (or ${nestedViewPath}). Create it first with generate view ${routeName}.`);
144
148
  }
@@ -193,18 +197,18 @@ async function appendRouteToApp({ appRoot, routeName }) {
193
197
  return routesFile;
194
198
  }
195
199
 
196
- function resolveTarget(appRoot, type, name) {
200
+ function resolveTarget(appRoot, type, name, sourceExtension) {
197
201
  switch (type) {
198
202
  case 'view':
199
- return path.join(appRoot, `${name}.view.js`);
203
+ return path.join(appRoot, `${name}.view${sourceExtension}`);
200
204
  case 'model':
201
- return path.join(appRoot, `${name}.model.js`);
205
+ return path.join(appRoot, `${name}.model${sourceExtension}`);
202
206
  case 'service':
203
- return path.join(appRoot, `${name}.service.js`);
207
+ return path.join(appRoot, `${name}.service${sourceExtension}`);
204
208
  case 'validator':
205
- return path.join(appRoot, `${name}.validator.js`);
209
+ return path.join(appRoot, `${name}.validator${sourceExtension}`);
206
210
  case 'subscriber':
207
- return path.join(appRoot, `${name}.subscriber.js`);
211
+ return path.join(appRoot, `${name}.subscriber${sourceExtension}`);
208
212
  default:
209
213
  throw new Error(`Unsupported type: ${type}`);
210
214
  }
@@ -248,14 +252,15 @@ export async function generateArtifact({ type, name, appName, projectRoot }) {
248
252
  const resolvedRoot = path.resolve(projectRoot);
249
253
  await assertProjectRoot(resolvedRoot);
250
254
  const appRoot = await assertAppRoot(resolvedRoot, appName);
255
+ const sourceExtension = getProjectSourceExtension(resolvedRoot);
251
256
 
252
257
  if (normalizedType === 'route') {
253
- const routesFile = await appendRouteToApp({ appRoot, routeName: name });
258
+ const routesFile = await appendRouteToApp({ appRoot, routeName: name, sourceExtension });
254
259
  console.log(`Generated route /${name} in ${routesFile}`);
255
260
  return;
256
261
  }
257
262
 
258
- const targetFile = resolveTarget(appRoot, normalizedType, name);
263
+ const targetFile = resolveTarget(appRoot, normalizedType, name, sourceExtension);
259
264
  if (await exists(targetFile)) {
260
265
  throw new Error(`File already exists: ${targetFile}`);
261
266
  }
@@ -1,7 +1,7 @@
1
1
  import path from 'path';
2
2
  import { exists, writeFile } from '../utils/fs.js';
3
- import { resolveProjectRoot } from '../utils/project.js';
4
- import { renderProjectAppJs, renderProjectLoaderCjs } from '../utils/scaffolds.js';
3
+ import { getProjectSourceExtension, resolveProjectRoot } from '../utils/project.js';
4
+ import { renderProjectAppJs, renderProjectLoaderCjs, withSourceExtension } from '../utils/scaffolds.js';
5
5
 
6
6
  async function ensureStartupFile(rootDir, fileName, content, output) {
7
7
  const target = path.join(rootDir, fileName);
@@ -20,8 +20,14 @@ export async function runGenerateLoader({
20
20
  output = console,
21
21
  } = {}) {
22
22
  const resolvedRoot = await resolveProjectRoot(projectRoot || process.cwd());
23
- const createdApp = await ensureStartupFile(resolvedRoot, 'app.js', renderProjectAppJs(), output);
24
- const createdLoader = await ensureStartupFile(resolvedRoot, 'loader.cjs', renderProjectLoaderCjs(), output);
23
+ const sourceExtension = getProjectSourceExtension(resolvedRoot);
24
+ const createdApp = await ensureStartupFile(
25
+ resolvedRoot,
26
+ withSourceExtension('app', sourceExtension),
27
+ renderProjectAppJs(),
28
+ output,
29
+ );
30
+ const createdLoader = await ensureStartupFile(resolvedRoot, 'loader.cjs', renderProjectLoaderCjs(sourceExtension), output);
25
31
 
26
32
  if (!createdApp && !createdLoader) {
27
33
  output.log(`Startup entry files already exist in ${resolvedRoot}`);
@@ -10,6 +10,8 @@ import {
10
10
  renderProjectPackageJson,
11
11
  renderProjectRoutes,
12
12
  renderProjectSettings,
13
+ renderTsConfig,
14
+ withSourceExtension,
13
15
  } from '../utils/scaffolds.js';
14
16
 
15
17
  async function createSecret() {
@@ -37,32 +39,36 @@ async function assertCanCreateProject(projectDir) {
37
39
  }
38
40
  }
39
41
 
40
- async function createBaseProjectFiles(projectRoot, projectName) {
42
+ async function createBaseProjectFiles(projectRoot, projectName, { typescript = false } = {}) {
41
43
  const apps = [];
42
44
  const appSecret = await createSecret();
45
+ const sourceExtension = typescript ? '.ts' : '.js';
43
46
 
44
47
  await ensureDir(projectRoot);
45
48
  await Promise.all([
46
49
  ensureDir(path.join(projectRoot, 'apps')),
47
50
  ]);
48
51
 
49
- await writeFile(path.join(projectRoot, 'app.js'), renderProjectAppJs());
50
- await writeFile(path.join(projectRoot, 'loader.cjs'), renderProjectLoaderCjs());
51
- await writeFile(path.join(projectRoot, 'package.json'), renderProjectPackageJson(projectName));
52
+ await writeFile(path.join(projectRoot, withSourceExtension('app', sourceExtension)), renderProjectAppJs());
53
+ await writeFile(path.join(projectRoot, 'loader.cjs'), renderProjectLoaderCjs(sourceExtension));
54
+ await writeFile(path.join(projectRoot, 'package.json'), renderProjectPackageJson(projectName, { typescript }));
52
55
  await writeFile(path.join(projectRoot, '.gitignore'), renderProjectGitIgnore());
53
56
  await writeFile(path.join(projectRoot, '.env'), renderProjectEnv(appSecret));
54
57
  await writeFile(path.join(projectRoot, '.env.example'), renderEnvExample());
55
58
 
56
- await writeFile(path.join(projectRoot, 'settings.js'), renderProjectSettings(projectName, apps, appSecret));
57
- await writeFile(path.join(projectRoot, 'routes.js'), renderProjectRoutes());
59
+ await writeFile(path.join(projectRoot, withSourceExtension('settings', sourceExtension)), renderProjectSettings(projectName, apps, appSecret));
60
+ await writeFile(path.join(projectRoot, withSourceExtension('routes', sourceExtension)), renderProjectRoutes());
61
+ if (typescript) {
62
+ await writeFile(path.join(projectRoot, 'tsconfig.json'), renderTsConfig());
63
+ }
58
64
  }
59
65
 
60
- export async function startProject({ projectName, cwd }) {
66
+ export async function startProject({ projectName, cwd, typescript = false }) {
61
67
  ensureValidName(projectName, 'project');
62
68
 
63
69
  const projectRoot = path.resolve(cwd, projectName);
64
70
  await assertCanCreateProject(projectRoot);
65
- await createBaseProjectFiles(projectRoot, projectName);
71
+ await createBaseProjectFiles(projectRoot, projectName, { typescript });
66
72
 
67
73
  console.log(`AegisNode project created at ${projectRoot}`);
68
74
  console.log('Next steps:');
package/src/cli/index.js CHANGED
@@ -5,28 +5,33 @@ import { generateArtifact } from './commands/generate.js';
5
5
  import { runDoctor } from './commands/doctor.js';
6
6
  import { runUpdateDependencies } from './commands/updatedeps.js';
7
7
  import { runGenerateLoader } from './commands/generateloader.js';
8
+ import { runFixApp } from './commands/fixapp.js';
8
9
 
9
10
  function printHelp() {
10
11
  console.log(`AegisNode CLI
11
12
 
12
13
  Usage:
13
- aegisnode startproject <project-name>
14
+ aegisnode startproject <project-name> [--typescript]
14
15
  aegisnode createapp <app-name> [--project <path>] [--mount </path>]
16
+ aegisnode fix [--app <app-name>] [--project <path>]
15
17
  aegisnode generate <type> <name> --app <app-name> [--project <path>]
16
18
  aegisnode runserver [--project <path>] [--port <number>]
17
19
  aegisnode generateloader [--project <path>]
18
- aegisnode doctor [--project <path>]
20
+ aegisnode doctor [--app <app-name>] [--project <path>]
19
21
  aegisnode updatedeps [--project <path>]
20
22
 
21
23
  Examples:
22
24
  aegisnode startproject blog
25
+ aegisnode startproject blog --typescript
23
26
  cd blog
24
27
  npm install
25
28
  aegisnode runserver
26
29
  aegisnode createapp users
30
+ aegisnode fix --app users
27
31
  aegisnode generate view user --app users
28
32
  aegisnode generate validator user --app users
29
33
  aegisnode generateloader --project blog
34
+ aegisnode doctor --app users --project blog
30
35
  aegisnode updatedeps --project blog
31
36
  `);
32
37
  }
@@ -66,6 +71,11 @@ function parseFlags(tokens) {
66
71
  continue;
67
72
  }
68
73
 
74
+ if (token === '--typescript' || token === '--ts') {
75
+ flags.typescript = true;
76
+ continue;
77
+ }
78
+
69
79
  if (token === '-h' || token === '--help') {
70
80
  flags.help = true;
71
81
  continue;
@@ -97,7 +107,11 @@ export async function runCli(argv) {
97
107
  if (!projectName) {
98
108
  throw new Error('Missing project name. Usage: aegisnode startproject <project-name>');
99
109
  }
100
- await startProject({ projectName, cwd: process.cwd() });
110
+ await startProject({
111
+ projectName,
112
+ cwd: process.cwd(),
113
+ typescript: flags.typescript === true,
114
+ });
101
115
  return;
102
116
  }
103
117
 
@@ -123,8 +137,24 @@ export async function runCli(argv) {
123
137
  }
124
138
 
125
139
  case 'doctor': {
140
+ if (positional.length > 0) {
141
+ throw new Error('Doctor app target must use --app <app-name>. Usage: aegisnode doctor [--app <app-name>] [--project <path>]');
142
+ }
126
143
  await runDoctor({
127
144
  projectRoot: flags.project ? String(flags.project) : process.cwd(),
145
+ appName: flags.app ? String(flags.app) : null,
146
+ });
147
+ return;
148
+ }
149
+
150
+ case 'fix':
151
+ case 'fixapp': {
152
+ if (positional.length > 0) {
153
+ throw new Error('Fix app target must use --app <app-name>. Usage: aegisnode fix [--app <app-name>] [--project <path>]');
154
+ }
155
+ await runFixApp({
156
+ appName: flags.app ? String(flags.app) : undefined,
157
+ projectRoot: flags.project ? String(flags.project) : process.cwd(),
128
158
  });
129
159
  return;
130
160
  }