vona-cli-set-api 1.1.154 → 1.1.156

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,4 +1,5 @@
1
1
  import { metadataCustomSnippet } from '@cabloy/cli';
2
+ import { mergeMetaIndex } from 'vona-cli-set-api/mergeMetaIndex';
2
3
  import { catchError } from '@cabloy/utils';
3
4
  import fs from 'node:fs';
4
5
 
@@ -9,7 +10,7 @@ declare module '@cabloy/cli' {
9
10
  }
10
11
 
11
12
  const __snippet_import1 = "import { $tableColumns } from 'vona-module-a-ormutils';";
12
- const __snippet_update = "...$tableColumns('<%=argv.moduleResourceName%>', 'name'),";
13
+ const __snippet_table = '<%=argv.moduleResourceName%>';
13
14
 
14
15
  export default metadataCustomSnippet({
15
16
  file: 'src/bean/meta.index.ts',
@@ -35,14 +36,7 @@ export default metadataCustomSnippet({
35
36
  `import { Meta } from 'vona-module-a-meta';\n${code}`,
36
37
  );
37
38
  }
38
- // update
39
- const code = await cli.template.renderContent({ content: __snippet_update });
40
- if (ast.includes('indexes: {}')) {
41
- ast = ast.replace('indexes: {', `indexes: {\n ${code}\n `);
42
- } else {
43
- ast = ast.replace('indexes: {', `indexes: {\n ${code}`);
44
- }
45
- // ok
46
- return ast;
39
+ const table = await cli.template.renderContent({ content: __snippet_table });
40
+ return mergeMetaIndex(ast, table, 'name');
47
41
  },
48
42
  });
@@ -1,4 +1,5 @@
1
1
  import { metadataCustomSnippet } from '@cabloy/cli';
2
+ import { mergeMetaIndex } from 'vona-cli-set-api/mergeMetaIndex';
2
3
  import { catchError } from '@cabloy/utils';
3
4
  import fs from 'node:fs';
4
5
 
@@ -9,7 +10,7 @@ declare module '@cabloy/cli' {
9
10
  }
10
11
 
11
12
  const __snippet_import1 = "import { $tableColumns } from 'vona-module-a-ormutils';";
12
- const __snippet_update = "...$tableColumns('<%=argv.moduleResourceName%>', 'name'),";
13
+ const __snippet_table = '<%=argv.moduleResourceName%>';
13
14
 
14
15
  export default metadataCustomSnippet({
15
16
  file: 'src/bean/meta.index.ts',
@@ -35,14 +36,7 @@ export default metadataCustomSnippet({
35
36
  `import { Meta } from 'vona-module-a-meta';\n${code}`,
36
37
  );
37
38
  }
38
- // update
39
- const code = await cli.template.renderContent({ content: __snippet_update });
40
- if (ast.includes('indexes: {}')) {
41
- ast = ast.replace('indexes: {', `indexes: {\n ${code}\n `);
42
- } else {
43
- ast = ast.replace('indexes: {', `indexes: {\n ${code}`);
44
- }
45
- // ok
46
- return ast;
39
+ const table = await cli.template.renderContent({ content: __snippet_table });
40
+ return mergeMetaIndex(ast, table, 'name');
47
41
  },
48
42
  });
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './lib/beans.ts';
2
2
  export * from './lib/commands.ts';
3
3
  export * from './lib/utils.ts';
4
+ export { mergeMetaIndex } from './lib/mergeMetaIndex.ts';
package/dist/index.js CHANGED
@@ -20,11 +20,13 @@ import { getEnvFiles } from '@cabloy/dotenv';
20
20
  import { glob } from '@cabloy/module-glob';
21
21
  import os from 'node:os';
22
22
  import chalk from 'chalk';
23
+ import { spawn } from 'node:child_process';
23
24
  import nodemon from 'nodemon';
24
25
  import { catchError, combineApiPathControllerAndActionRaw, replaceTemplate } from '@cabloy/utils';
25
26
  import { getOnionScenesMeta, getOnionMetasMeta, relativeNameToCapitalize } from '@cabloy/module-info';
26
27
  import { toUpperCaseFirstChar, stringToCapitalize, replaceTemplate as replaceTemplate$1 } from '@cabloy/word-utils';
27
28
  import fs from 'node:fs';
29
+ import gogocode from 'gogocode';
28
30
  import { beanFullNameFromOnionName } from 'vona-core';
29
31
 
30
32
  function getEnvMeta(configMeta) {
@@ -767,6 +769,16 @@ class CliBinDbReset extends BeanCliBase {
767
769
  }
768
770
  }
769
771
 
772
+ function resolveDevMode(mode) {
773
+ if (mode === undefined || mode === 'dev') return 'dev';
774
+ if (mode === 'test') return 'test';
775
+ throw new Error('Invalid --mode: expected dev or test');
776
+ }
777
+ function resolveTestWorkers(mode, workers) {
778
+ if (mode !== 'test') return workers;
779
+ if (workers === undefined || workers === 1) return 1;
780
+ throw new Error('--mode=test requires --workers=1');
781
+ }
770
782
  class CliBinDev extends BeanCliBase {
771
783
  async execute() {
772
784
  const {
@@ -782,7 +794,8 @@ class CliBinDev extends BeanCliBase {
782
794
  const {
783
795
  argv
784
796
  } = this.context;
785
- const mode = 'dev';
797
+ const mode = resolveDevMode(argv.mode);
798
+ const workers = resolveTestWorkers(mode, argv.workers);
786
799
  const flavor = argv.flavor || 'normal';
787
800
  const configMeta = {
788
801
  flavor,
@@ -791,13 +804,42 @@ class CliBinDev extends BeanCliBase {
791
804
  const configOptions = {
792
805
  appDir: projectPath,
793
806
  runtimeDir: '.vona',
794
- workers: argv.workers
807
+ workers
795
808
  };
796
- const {
797
- modulesMeta
798
- } = await generateVonaMeta(configMeta, configOptions);
799
- await this._run(projectPath, modulesMeta);
800
- await rimraf(path.join(projectPath, '.vona'));
809
+ try {
810
+ const {
811
+ modulesMeta
812
+ } = await generateVonaMeta(configMeta, configOptions);
813
+ if (mode === 'test') {
814
+ await this._runTest(projectPath);
815
+ } else {
816
+ await this._run(projectPath, modulesMeta);
817
+ }
818
+ } finally {
819
+ await rimraf(path.join(projectPath, '.vona'));
820
+ }
821
+ }
822
+ async _runTest(projectPath) {
823
+ const child = spawn(process.execPath, [getImportEsm(), '.vona/bootstrap.ts'], {
824
+ cwd: projectPath,
825
+ stdio: 'inherit'
826
+ });
827
+ const forwardSignal = signal => child.kill(signal);
828
+ const onInterrupt = () => forwardSignal('SIGINT');
829
+ const onTerminate = () => forwardSignal('SIGTERM');
830
+ process.on('SIGINT', onInterrupt);
831
+ process.on('SIGTERM', onTerminate);
832
+ try {
833
+ await new Promise((resolve, reject) => {
834
+ child.once('error', reject);
835
+ child.once('exit', (code, signal) => {
836
+ if (code === 0) resolve();else reject(new Error(`Test-mode dev server exited: ${signal ?? code}`));
837
+ });
838
+ });
839
+ } finally {
840
+ process.off('SIGINT', onInterrupt);
841
+ process.off('SIGTERM', onTerminate);
842
+ }
801
843
  }
802
844
  async _run(projectPath, _modulesMeta) {
803
845
  let closed = false;
@@ -2018,6 +2060,120 @@ class CliToolsDeps extends BeanCliBase {
2018
2060
  }
2019
2061
  }
2020
2062
 
2063
+ function propertyName(node) {
2064
+ if (node.computed) return;
2065
+ if (node.key?.type === 'Identifier') return node.key.name;
2066
+ if (node.key?.type === 'StringLiteral') return node.key.value;
2067
+ }
2068
+ function literalValue(node) {
2069
+ return node?.type === 'StringLiteral' ? node.value : undefined;
2070
+ }
2071
+ function indexObject(source) {
2072
+ const file = gogocode(source, {
2073
+ parseOptions: {
2074
+ sourceType: 'module',
2075
+ plugins: ['typescript', 'decorators-legacy']
2076
+ }
2077
+ }).node;
2078
+ const objects = [];
2079
+ for (const statement of file.program.body) {
2080
+ const declaration = statement.declaration ?? statement;
2081
+ for (const decorator of declaration.decorators ?? []) {
2082
+ const call = decorator.expression;
2083
+ if (call.type !== 'CallExpression' || call.callee.name !== 'Meta') continue;
2084
+ const options = call.arguments[0];
2085
+ if (options?.type !== 'ObjectExpression') continue;
2086
+ for (const property of options.properties) {
2087
+ if (property.type === 'ObjectProperty' && propertyName(property) === 'indexes') {
2088
+ if (property.value.type !== 'ObjectExpression') {
2089
+ throw new Error('meta.index indexes must be an object literal');
2090
+ }
2091
+ objects.push(property.value);
2092
+ }
2093
+ }
2094
+ }
2095
+ }
2096
+ if (objects.length !== 1) throw new Error('expected exactly one @Meta indexes object');
2097
+ return objects[0];
2098
+ }
2099
+ function quote(value) {
2100
+ return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'")}'`;
2101
+ }
2102
+ function mergeMetaIndex(source, tableName, field) {
2103
+ const indexes = indexObject(source);
2104
+ const declarations = [];
2105
+ const fields = [];
2106
+ for (const property of indexes.properties) {
2107
+ if (property.type === 'ObjectProperty') {
2108
+ const key = propertyName(property);
2109
+ if (!key || key === tableName) {
2110
+ throw new Error(`unsupported index property for ${tableName}`);
2111
+ }
2112
+ continue;
2113
+ }
2114
+ if (property.type !== 'SpreadElement') {
2115
+ throw new Error(`unsupported index declaration for ${tableName}`);
2116
+ }
2117
+ const call = property.argument;
2118
+ if (call.type !== 'CallExpression' || call.callee.name !== '$tableColumns') {
2119
+ throw new Error(`unsupported index spread for ${tableName}`);
2120
+ }
2121
+ const name = literalValue(call.arguments[0]);
2122
+ if (!name) throw new Error(`index table name must be a string literal: ${tableName}`);
2123
+ if (name !== tableName) continue;
2124
+ const value = call.arguments[1];
2125
+ let values;
2126
+ if (value?.type === 'StringLiteral') values = [value];else if (value?.type === 'ArrayExpression') values = value.elements;else throw new Error(`unsupported index fields for ${tableName}`);
2127
+ for (const item of values) {
2128
+ const text = literalValue(item);
2129
+ if (text === undefined) throw new Error(`unsupported index fields for ${tableName}`);
2130
+ for (const spec of text.split(',')) {
2131
+ if (!fields.includes(spec)) fields.push(spec);
2132
+ }
2133
+ }
2134
+ declarations.push(property);
2135
+ }
2136
+ if (!fields.includes(field)) fields.push(field);
2137
+ if (declarations.length === 1) {
2138
+ const value = declarations[0].argument.arguments[1];
2139
+ if (value.type === 'StringLiteral' && fields.length === 1 && value.value === field) return source;
2140
+ if (value.type === 'ArrayExpression') {
2141
+ const values = value.elements.map(literalValue);
2142
+ if (values.length === fields.length && values.every((item, index) => item === fields[index])) {
2143
+ return source;
2144
+ }
2145
+ }
2146
+ }
2147
+ const columns = fields.length === 1 ? quote(fields[0]) : `[${fields.map(quote).join(', ')}]`;
2148
+ const declaration = `...$tableColumns(${quote(tableName)}, ${columns})`;
2149
+ if (!declarations.length) {
2150
+ const content = source.slice(indexes.start + 1, indexes.end - 1);
2151
+ if (content.trim() && !content.trimEnd().endsWith(',')) {
2152
+ throw new Error(`indexes object must use trailing commas before adding ${tableName}`);
2153
+ }
2154
+ return `${source.slice(0, indexes.start + 1)}\n ${declaration},${source.slice(indexes.start + 1)}`;
2155
+ }
2156
+ const patches = declarations.map((item, index) => {
2157
+ if (!index) return {
2158
+ start: item.start,
2159
+ end: item.end,
2160
+ text: declaration
2161
+ };
2162
+ let end = item.end;
2163
+ while (/\s/.test(source[end]) && end < indexes.end) end++;
2164
+ if (source[end] === ',') end++;
2165
+ return {
2166
+ start: item.start,
2167
+ end,
2168
+ text: ''
2169
+ };
2170
+ });
2171
+ for (const patch of patches.reverse()) {
2172
+ source = `${source.slice(0, patch.start)}${patch.text}${source.slice(patch.end)}`;
2173
+ }
2174
+ return source;
2175
+ }
2176
+
2021
2177
  class CliToolsMasterDetail extends BeanCliBase {
2022
2178
  async execute() {
2023
2179
  await super.execute();
@@ -2153,13 +2309,11 @@ class CliToolsMasterDetail extends BeanCliBase {
2153
2309
  const {
2154
2310
  argv
2155
2311
  } = this.context;
2156
- await this._patchFileByMarker({
2157
- fileName: this._detailPaths().metaIndex,
2158
- existsNeedle: `'${argv.fk}'`,
2159
- marker: ' indexes: {\n',
2160
- line: this._detailMetaIndexLine(),
2161
- shapeName: 'detail meta.index'
2162
- });
2312
+ const fileName = this._detailPaths().metaIndex;
2313
+ const content = this._readFile(fileName);
2314
+ const table = this.helper.combineModuleNameAndResource(argv.detailModuleInfo.relativeName, argv.detailResourceName);
2315
+ const updated = mergeMetaIndex(content, table, argv.fk);
2316
+ if (updated !== content) await this._saveFile(fileName, updated);
2163
2317
  }
2164
2318
  _detailMetaVersionLine() {
2165
2319
  const {
@@ -2167,12 +2321,6 @@ class CliToolsMasterDetail extends BeanCliBase {
2167
2321
  } = this.context;
2168
2322
  return ` table.tableIdentity(entity${argv.detailResourceNameCapitalize}.${argv.fk}).comment(entity${argv.detailResourceNameCapitalize}.$comment.${argv.fk});\n`;
2169
2323
  }
2170
- _detailMetaIndexLine() {
2171
- const {
2172
- argv
2173
- } = this.context;
2174
- return ` ...$tableColumns('${this.helper.combineModuleNameAndResource(argv.detailModuleInfo.relativeName, argv.detailResourceName)}', '${argv.fk}'),\n`;
2175
- }
2176
2324
  async _patchFileByMarker({
2177
2325
  fileName,
2178
2326
  existsNeedle,
@@ -3364,7 +3512,7 @@ var binDev = {
3364
3512
  info: {
3365
3513
  version: '5.0.0',
3366
3514
  title: 'Cli: Bin: Dev',
3367
- usage: 'npm run vona :bin:dev -- [--workers=] [--flavor=]'
3515
+ usage: 'npm run vona :bin:dev -- [--workers=] [--flavor=] [--mode=dev|test]'
3368
3516
  },
3369
3517
  options: {
3370
3518
  workers: {
@@ -3374,6 +3522,11 @@ var binDev = {
3374
3522
  flavor: {
3375
3523
  description: 'flavor',
3376
3524
  type: 'string'
3525
+ },
3526
+ mode: {
3527
+ description: 'dev or test',
3528
+ type: 'string',
3529
+ choices: ['dev', 'test']
3377
3530
  }
3378
3531
  }
3379
3532
  };
@@ -3994,4 +4147,4 @@ const commands = {
3994
4147
  }
3995
4148
  };
3996
4149
 
3997
- export { beans, commands, copyTemplateFile, copyTemplateIfNeed, generateConfigDefine, getAbsolutePathOfModule, getEnvMeta, getImportEsm, getNodeEnv, getOutDir, getOutReleasesDir, loadJSONFile, pathToHref, requireModule, saveJSONFile };
4150
+ export { beans, commands, copyTemplateFile, copyTemplateIfNeed, generateConfigDefine, getAbsolutePathOfModule, getEnvMeta, getImportEsm, getNodeEnv, getOutDir, getOutReleasesDir, loadJSONFile, mergeMetaIndex, pathToHref, requireModule, saveJSONFile };
@@ -1,14 +1,18 @@
1
1
  import type { glob } from '@cabloy/module-glob';
2
- import type { VonaMetaFlavor } from '@cabloy/module-info';
2
+ import type { VonaMetaFlavor, VonaMetaMode } from '@cabloy/module-info';
3
3
  import { BeanCliBase } from '@cabloy/cli';
4
+ export declare function resolveDevMode(mode: unknown): 'dev' | 'test';
5
+ export declare function resolveTestWorkers(mode: 'dev' | 'test', workers: unknown): number | undefined;
4
6
  declare module '@cabloy/cli' {
5
7
  interface ICommandArgv {
6
8
  workers?: number;
7
9
  flavor?: VonaMetaFlavor;
10
+ mode?: VonaMetaMode;
8
11
  }
9
12
  }
10
13
  export declare class CliBinDev extends BeanCliBase {
11
14
  execute(): Promise<void>;
12
15
  _dev(projectPath: string): Promise<void>;
16
+ _runTest(projectPath: string): Promise<void>;
13
17
  _run(projectPath: string, _modulesMeta: Awaited<ReturnType<typeof glob>>): Promise<unknown>;
14
18
  }
@@ -44,7 +44,6 @@ export declare class CliToolsMasterDetail extends BeanCliBase {
44
44
  private _patchDetailMetaVersion;
45
45
  private _patchDetailMetaIndex;
46
46
  private _detailMetaVersionLine;
47
- private _detailMetaIndexLine;
48
47
  private _patchFileByMarker;
49
48
  private _patchFileContent;
50
49
  private _renderMasterDetailDtos;
@@ -14,6 +14,11 @@ declare const _default: {
14
14
  description: string;
15
15
  type: string;
16
16
  };
17
+ mode: {
18
+ description: string;
19
+ type: string;
20
+ choices: string[];
21
+ };
17
22
  };
18
23
  };
19
24
  export default _default;
@@ -104,6 +104,11 @@ export declare const commands: {
104
104
  description: string;
105
105
  type: string;
106
106
  };
107
+ mode: {
108
+ description: string;
109
+ type: string;
110
+ choices: string[];
111
+ };
107
112
  };
108
113
  };
109
114
  play: {
@@ -0,0 +1 @@
1
+ export declare function mergeMetaIndex(source: string, tableName: string, field: string): string;
@@ -1,8 +1,9 @@
1
1
  import { metadataCustomSnippet } from '@cabloy/cli';
2
+ import { mergeMetaIndex } from 'vona-cli-set-api/mergeMetaIndex';
2
3
  import { catchError } from '@cabloy/utils';
3
4
  import fs from 'node:fs';
4
5
  const __snippet_import1 = "import { $tableColumns } from 'vona-module-a-ormutils';";
5
- const __snippet_update = "...$tableColumns('<%=argv.moduleResourceName%>', 'name'),";
6
+ const __snippet_table = '<%=argv.moduleResourceName%>';
6
7
  export default metadataCustomSnippet({
7
8
  file: 'src/bean/meta.index.ts',
8
9
  language: 'plain',
@@ -21,15 +22,7 @@ export default metadataCustomSnippet({
21
22
  const code = await cli.template.renderContent({ content: __snippet_import1 });
22
23
  ast = ast.replace("import { Meta } from 'vona-module-a-meta';", `import { Meta } from 'vona-module-a-meta';\n${code}`);
23
24
  }
24
- // update
25
- const code = await cli.template.renderContent({ content: __snippet_update });
26
- if (ast.includes('indexes: {}')) {
27
- ast = ast.replace('indexes: {', `indexes: {\n ${code}\n `);
28
- }
29
- else {
30
- ast = ast.replace('indexes: {', `indexes: {\n ${code}`);
31
- }
32
- // ok
33
- return ast;
25
+ const table = await cli.template.renderContent({ content: __snippet_table });
26
+ return mergeMetaIndex(ast, table, 'name');
34
27
  },
35
28
  });
@@ -1,8 +1,9 @@
1
1
  import { metadataCustomSnippet } from '@cabloy/cli';
2
+ import { mergeMetaIndex } from 'vona-cli-set-api/mergeMetaIndex';
2
3
  import { catchError } from '@cabloy/utils';
3
4
  import fs from 'node:fs';
4
5
  const __snippet_import1 = "import { $tableColumns } from 'vona-module-a-ormutils';";
5
- const __snippet_update = "...$tableColumns('<%=argv.moduleResourceName%>', 'name'),";
6
+ const __snippet_table = '<%=argv.moduleResourceName%>';
6
7
  export default metadataCustomSnippet({
7
8
  file: 'src/bean/meta.index.ts',
8
9
  language: 'plain',
@@ -21,15 +22,7 @@ export default metadataCustomSnippet({
21
22
  const code = await cli.template.renderContent({ content: __snippet_import1 });
22
23
  ast = ast.replace("import { Meta } from 'vona-module-a-meta';", `import { Meta } from 'vona-module-a-meta';\n${code}`);
23
24
  }
24
- // update
25
- const code = await cli.template.renderContent({ content: __snippet_update });
26
- if (ast.includes('indexes: {}')) {
27
- ast = ast.replace('indexes: {', `indexes: {\n ${code}\n `);
28
- }
29
- else {
30
- ast = ast.replace('indexes: {', `indexes: {\n ${code}`);
31
- }
32
- // ok
33
- return ast;
25
+ const table = await cli.template.renderContent({ content: __snippet_table });
26
+ return mergeMetaIndex(ast, table, 'name');
34
27
  },
35
28
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vona-cli-set-api",
3
- "version": "1.1.154",
4
- "gitHead": "94435329d69fc19d0d15e4e10c3b1111e99ab1cf",
3
+ "version": "1.1.156",
4
+ "gitHead": "6933fa57c12a517fe3819ccf16f3d619e5a22471",
5
5
  "description": "vona cli-set-api",
6
6
  "keywords": [
7
7
  "framework",
@@ -25,6 +25,10 @@
25
25
  ],
26
26
  "default": "./dist/index.js"
27
27
  },
28
+ "./mergeMetaIndex": {
29
+ "types": "./dist/lib/mergeMetaIndex.d.ts",
30
+ "default": "./dist/index.js"
31
+ },
28
32
  "./package.json": "./package.json"
29
33
  },
30
34
  "publishConfig": {