vovk-cli 0.0.1-beta.57 → 0.0.1-beta.59

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/dist/index.mjs CHANGED
@@ -179,6 +179,7 @@ program
179
179
  .option('--skip-install', 'skip installing dependencies')
180
180
  .option('--update-ts-config', 'update tsconfig.json')
181
181
  .option('--update-scripts <mode>', 'update package.json scripts ("implicit" or "explicit")')
182
+ .option('--bundle', 'set up "tsdown" bundler')
182
183
  .option('--lang <languages...>', 'generate client for other programming languages by default ("py" for Python and "rs" for Rust are supported)')
183
184
  .option('--validation-library <library>', 'validation library to use ("zod", "valibot" or "arktype"); set to "none" to skip')
184
185
  .option('--channel <channel>', 'channel to use for fetching packages', 'latest')
@@ -1,9 +1,9 @@
1
1
  import type { getLogger } from '../utils/getLogger.mjs';
2
2
  import type { InitOptions } from '../types.mjs';
3
- export declare function createConfig({ root, options: { validationLibrary, lang, dryRun }, }: {
3
+ export declare function createConfig({ root, options: { validationLibrary, bundle, lang, dryRun }, }: {
4
4
  root: string;
5
5
  log: ReturnType<typeof getLogger>;
6
- options: Pick<InitOptions, 'validationLibrary' | 'lang' | 'channel' | 'dryRun'>;
6
+ options: Pick<InitOptions, 'validationLibrary' | 'bundle' | 'lang' | 'channel' | 'dryRun'>;
7
7
  }): Promise<{
8
8
  configAbsolutePath: string;
9
9
  }>;
@@ -2,7 +2,8 @@ import path from 'node:path';
2
2
  import fs from 'node:fs/promises';
3
3
  import { prettify } from '../utils/prettify.mjs';
4
4
  import { getFileSystemEntryType, FileSystemEntryType } from '../utils/getFileSystemEntryType.mjs';
5
- export async function createConfig({ root, options: { validationLibrary, lang, dryRun }, }) {
5
+ import { updateConfigProperty } from '../utils/updateConfigProperty.mjs';
6
+ export async function createConfig({ root, options: { validationLibrary, bundle, lang, dryRun }, }) {
6
7
  const config = {};
7
8
  const dotConfigPath = path.join(root, '.config');
8
9
  const dir = (await getFileSystemEntryType(dotConfigPath)) === FileSystemEntryType.DIRECTORY ? dotConfigPath : root;
@@ -38,10 +39,25 @@ export async function createConfig({ root, options: { validationLibrary, lang, d
38
39
  config.composedClient.fromTemplates = ['mjs', 'cjs', ...lang];
39
40
  }
40
41
  config.moduleTemplates = moduleTemplates;
41
- const configStr = await prettify(`// @ts-check
42
+ let configStr = await prettify(`// @ts-check
42
43
  /** @type {import('vovk').VovkConfig} */
43
44
  const config = ${JSON.stringify(config, null, 2)};
44
45
  ${isModule ? '\nexport config;' : 'module.exports = config;'}`, configAbsolutePath);
46
+ if (bundle) {
47
+ configStr = await updateConfigProperty(configStr, ['bundle', 'build'], async ({ entry, outDir }) => {
48
+ const { build } = await import('tsdown');
49
+ await build({
50
+ entry,
51
+ dts: true,
52
+ format: ['cjs', 'esm'],
53
+ hash: false,
54
+ fixedExtension: true,
55
+ clean: true,
56
+ outDir,
57
+ tsconfig: './tsconfig.bundle.json',
58
+ });
59
+ });
60
+ }
45
61
  if (!dryRun)
46
62
  await fs.writeFile(configAbsolutePath, configStr, 'utf-8');
47
63
  return { configAbsolutePath };
@@ -4,5 +4,5 @@ export declare class Init {
4
4
  #private;
5
5
  root: string;
6
6
  log: ReturnType<typeof getLogger>;
7
- main({ prefix, yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, lang, dryRun, channel, }: InitOptions): Promise<void>;
7
+ main({ prefix, yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, bundle, lang, dryRun, channel, }: InitOptions): Promise<void>;
8
8
  }
@@ -17,7 +17,7 @@ import { chalkHighlightThing } from '../utils/chalkHighlightThing.mjs';
17
17
  export class Init {
18
18
  root;
19
19
  log;
20
- async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, lang, dryRun, channel, }) {
20
+ async #init({ configPaths, pkgJson, }, { useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, bundle, lang, dryRun, channel, }) {
21
21
  const { log, root } = this;
22
22
  const dependencies = ['vovk', 'vovk-client', 'vovk-ajv'];
23
23
  const devDependencies = ['vovk-cli'];
@@ -39,10 +39,31 @@ export class Init {
39
39
  arktype: ['arktype'],
40
40
  }[validationLibrary] ?? []));
41
41
  }
42
+ if (bundle) {
43
+ devDependencies.push('tsdown');
44
+ if (!dryRun) {
45
+ const bundleTsconfig = path.join(root, 'tsconfig.bundle.json');
46
+ const bundleTsconfigContent = {
47
+ compilerOptions: {
48
+ moduleResolution: 'bundler',
49
+ paths: {
50
+ 'vovk/*': ['./node_modules/vovk/*'],
51
+ },
52
+ },
53
+ };
54
+ try {
55
+ await fs.writeFile(bundleTsconfig, JSON.stringify(bundleTsconfigContent, null, 2));
56
+ log.info(`Created bundle tsconfig at ${chalkHighlightThing(bundleTsconfig)}`);
57
+ }
58
+ catch (error) {
59
+ log.error(`Failed to create bundle tsconfig at ${chalkHighlightThing(bundleTsconfig)}: ${error.message}`);
60
+ }
61
+ }
62
+ }
42
63
  if (updateScripts) {
43
64
  try {
44
65
  if (!dryRun && pkgJson)
45
- await updateNPMScripts(pkgJson, root, updateScripts);
66
+ await updateNPMScripts({ pkgJson, root, bundle, updateScriptsMode: updateScripts });
46
67
  log.info('Updated scripts at package.json');
47
68
  }
48
69
  catch (error) {
@@ -114,7 +135,7 @@ export class Init {
114
135
  const { configAbsolutePath } = await createConfig({
115
136
  root,
116
137
  log,
117
- options: { validationLibrary, channel, lang, dryRun },
138
+ options: { validationLibrary, channel, bundle, lang, dryRun },
118
139
  });
119
140
  log.info('Config created successfully at ' + chalkHighlightThing(configAbsolutePath));
120
141
  }
@@ -122,7 +143,7 @@ export class Init {
122
143
  log.error(`Failed to create config: ${error.message}`);
123
144
  }
124
145
  }
125
- async main({ prefix, yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, lang, dryRun, channel, }) {
146
+ async main({ prefix, yes, logLevel, useNpm, useYarn, usePnpm, useBun, skipInstall, updateTsConfig, updateScripts, validationLibrary, bundle, lang, dryRun, channel, }) {
126
147
  const cwd = process.cwd();
127
148
  const root = path.resolve(cwd, prefix ?? '.');
128
149
  const log = getLogger(logLevel ?? 'info');
@@ -141,6 +162,7 @@ export class Init {
141
162
  updateTsConfig: updateTsConfig ?? true,
142
163
  updateScripts: updateScripts ?? 'implicit',
143
164
  validationLibrary: validationLibrary?.toLocaleLowerCase() === 'none' ? null : (validationLibrary ?? 'zod'),
165
+ bundle: bundle ?? true,
144
166
  dryRun: dryRun ?? false,
145
167
  channel: channel ?? 'latest',
146
168
  lang: lang ?? [],
@@ -185,10 +207,29 @@ export class Init {
185
207
  { name: 'None', value: null, description: 'Install validation library later' },
186
208
  ],
187
209
  })));
210
+ if (typeof updateTsConfig === 'undefined' && pkgJson) {
211
+ let shouldAsk = false;
212
+ try {
213
+ shouldAsk = !(await checkTSConfigForExperimentalDecorators(root));
214
+ }
215
+ catch (error) {
216
+ log.error(`Failed to check tsconfig.json for "experimentalDecorators": ${error.message}`);
217
+ }
218
+ if (shouldAsk) {
219
+ const keys = ['experimentalDecorators'];
220
+ updateTsConfig = await confirm({
221
+ message: `Do you want to add ${keys.map((k) => `"${k}"`).join(' and ')} to tsconfig.json? (recommended)`,
222
+ });
223
+ }
224
+ }
225
+ bundle ??= await confirm({
226
+ message: 'Do you want to set up "tsdown" to bundle TypeScript client?',
227
+ default: true,
228
+ });
188
229
  updateScripts ??= !pkgJson
189
230
  ? undefined
190
231
  : await select({
191
- message: 'Do you want to update "dev" and add "prebuild" NPM scripts at package.json (recommended)?',
232
+ message: `Do you want to update "dev" and add "prebuild"${bundle ? ' and "bundle"' : ''} NPM scripts at package.json (recommended)?`,
192
233
  default: 'implicit',
193
234
  choices: [
194
235
  {
@@ -208,21 +249,6 @@ export class Init {
208
249
  },
209
250
  ],
210
251
  });
211
- if (typeof updateTsConfig === 'undefined' && pkgJson) {
212
- let shouldAsk = false;
213
- try {
214
- shouldAsk = !(await checkTSConfigForExperimentalDecorators(root));
215
- }
216
- catch (error) {
217
- log.error(`Failed to check tsconfig.json for "experimentalDecorators": ${error.message}`);
218
- }
219
- if (shouldAsk) {
220
- const keys = ['experimentalDecorators'];
221
- updateTsConfig = await confirm({
222
- message: `Do you want to add ${keys.map((k) => `"${k}"`).join(' and ')} to tsconfig.json? (recommended)`,
223
- });
224
- }
225
- }
226
252
  lang ??= await checkbox({
227
253
  message: 'Do you want to generate RPC client for other languages besides TypeScript (experimental)?',
228
254
  choices: [
@@ -239,6 +265,7 @@ export class Init {
239
265
  updateTsConfig,
240
266
  updateScripts,
241
267
  validationLibrary,
268
+ bundle,
242
269
  lang,
243
270
  dryRun,
244
271
  channel,
@@ -1,3 +1,8 @@
1
1
  import NPMCliPackageJson from '@npmcli/package-json';
2
2
  export declare function getDevScript(pkgJson: NPMCliPackageJson, updateScriptsMode: 'implicit' | 'explicit'): string;
3
- export declare function updateNPMScripts(pkgJson: NPMCliPackageJson, root: string, updateScriptsMode: 'implicit' | 'explicit'): Promise<void>;
3
+ export declare function updateNPMScripts({ pkgJson, bundle, updateScriptsMode, }: {
4
+ pkgJson: NPMCliPackageJson;
5
+ root: string;
6
+ bundle?: boolean;
7
+ updateScriptsMode: 'implicit' | 'explicit';
8
+ }): Promise<void>;
@@ -8,12 +8,13 @@ export function getDevScript(pkgJson, updateScriptsMode) {
8
8
  ? `PORT=3000 concurrently '${dev}' 'vovk dev' --kill-others`
9
9
  : `vovk dev --next-dev${nextDevFlags ? ` -- ${nextDevFlags}` : ''}`;
10
10
  }
11
- export async function updateNPMScripts(pkgJson, root, updateScriptsMode) {
11
+ export async function updateNPMScripts({ pkgJson, bundle, updateScriptsMode, }) {
12
12
  pkgJson.update({
13
13
  scripts: {
14
14
  ...pkgJson.content.scripts,
15
15
  dev: getDevScript(pkgJson, updateScriptsMode),
16
16
  prebuild: 'vovk generate',
17
+ ...(bundle ? { bundle: 'vovk bundle' } : {}),
17
18
  },
18
19
  });
19
20
  await pkgJson.save();
@@ -15,7 +15,7 @@ export async function newSegment({ projectInfo, segmentName, isStaticSegment, ov
15
15
  log.error(`Unable to create new segment. ${formatLoggedSegmentName(segmentName, { upperFirst: true })} already exists. You can use --overwrite flag to overwrite it.`);
16
16
  return process.exit(1);
17
17
  }
18
- const code = await prettify(`import { initSegment${isStaticSegment ? ', generateStaticAPI' : ''} } from 'vovk';
18
+ const code = await prettify(`import { initSegment${isStaticSegment ? ', controllersToStaticParams' : ''} } from 'vovk';
19
19
 
20
20
  const controllers = {};
21
21
 
@@ -23,7 +23,7 @@ export type Controllers = typeof controllers;
23
23
  ${isStaticSegment
24
24
  ? `
25
25
  export function generateStaticParams() {
26
- return generateStaticAPI(controllers);
26
+ return controllersToStaticParams(controllers);
27
27
  }
28
28
  `
29
29
  : ''}
package/dist/types.d.mts CHANGED
@@ -63,6 +63,7 @@ export interface InitOptions {
63
63
  skipInstall?: boolean;
64
64
  updateTsConfig?: boolean;
65
65
  updateScripts?: 'implicit' | 'explicit';
66
+ bundle?: boolean;
66
67
  validationLibrary?: 'zod' | 'valibot' | 'arktype' | null;
67
68
  dryRun?: boolean;
68
69
  lang?: string[];
@@ -0,0 +1,2 @@
1
+ export declare function updateConfigProperty(sourceCode: string, pathToProperty: string[], newValue: unknown): string;
2
+ export declare function updateConfigFileProperty(absolutePathToTheFile: string, pathToProperty: string[], newValue: unknown): string;
@@ -0,0 +1,131 @@
1
+ import { Project, QuoteKind, IndentationText, NewLineKind, SyntaxKind, Node } from 'ts-morph';
2
+ export function updateConfigProperty(sourceCode, pathToProperty, newValue) {
3
+ const project = createProject();
4
+ const sourceFile = project.createSourceFile('config-temp.mts', sourceCode, { overwrite: true });
5
+ return mutateConfig(sourceFile, pathToProperty, newValue);
6
+ }
7
+ export function updateConfigFileProperty(absolutePathToTheFile, pathToProperty, newValue) {
8
+ const project = createProject();
9
+ const sourceFile = project.addSourceFileAtPath(absolutePathToTheFile);
10
+ const updated = mutateConfig(sourceFile, pathToProperty, newValue);
11
+ sourceFile.saveSync();
12
+ return updated;
13
+ }
14
+ function createProject() {
15
+ return new Project({
16
+ manipulationSettings: {
17
+ quoteKind: QuoteKind.Single,
18
+ indentationText: IndentationText.TwoSpaces,
19
+ newLineKind: NewLineKind.LineFeed,
20
+ },
21
+ });
22
+ }
23
+ function mutateConfig(sourceFile, pathToProperty, newValue) {
24
+ const variableDeclaration = sourceFile.getVariableDeclaration('config');
25
+ if (!variableDeclaration) {
26
+ throw new Error('config variable not found in the file.');
27
+ }
28
+ const initializer = variableDeclaration.getInitializer();
29
+ if (!initializer || !Node.isObjectLiteralExpression(initializer)) {
30
+ throw new Error('config is not initialized as an object literal.');
31
+ }
32
+ let currentNode = initializer;
33
+ for (let i = 0; i < pathToProperty.length; i++) {
34
+ const key = pathToProperty[i];
35
+ if (!Node.isObjectLiteralExpression(currentNode)) {
36
+ throw new Error(`Property at path ${pathToProperty.slice(0, i).join('.')} is not an object.`);
37
+ }
38
+ const property = currentNode.getProperty(key);
39
+ if (!property) {
40
+ // Property does not exist
41
+ if (newValue === undefined) {
42
+ // Nothing to remove
43
+ return sourceFile.getFullText();
44
+ }
45
+ else {
46
+ // Create property
47
+ if (i === pathToProperty.length - 1) {
48
+ // Last key
49
+ currentNode.addPropertyAssignment({
50
+ name: key,
51
+ initializer: (writer) => writeInitializer(writer, newValue),
52
+ });
53
+ }
54
+ else {
55
+ // Need to create nested object
56
+ const newObjectAssignment = currentNode.addPropertyAssignment({
57
+ name: key,
58
+ initializer: '{}',
59
+ });
60
+ const init = newObjectAssignment.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
61
+ currentNode = init;
62
+ }
63
+ }
64
+ }
65
+ else {
66
+ // Property exists
67
+ if (Node.isPropertyAssignment(property)) {
68
+ const propInitializer = property.getInitializer();
69
+ if (i === pathToProperty.length - 1) {
70
+ // Last key
71
+ if (newValue === undefined) {
72
+ // Remove the property
73
+ property.remove();
74
+ }
75
+ else {
76
+ // Update the value
77
+ property.setInitializer((writer) => writeInitializer(writer, newValue));
78
+ }
79
+ }
80
+ else {
81
+ // Need to go deeper
82
+ if (propInitializer && Node.isObjectLiteralExpression(propInitializer)) {
83
+ currentNode = propInitializer;
84
+ }
85
+ else {
86
+ throw new Error(`Cannot traverse into non-object property at ${pathToProperty.slice(0, i + 1).join('.')}.`);
87
+ }
88
+ }
89
+ }
90
+ else {
91
+ throw new Error(`Unsupported property kind at path ${pathToProperty.slice(0, i + 1).join('.')}.`);
92
+ }
93
+ }
94
+ }
95
+ return sourceFile.getFullText();
96
+ }
97
+ function writeInitializer(writer, value) {
98
+ if (typeof value === 'string') {
99
+ writer.quote(value);
100
+ }
101
+ else if (typeof value === 'number' || typeof value === 'boolean') {
102
+ writer.write(value.toString());
103
+ }
104
+ else if (typeof value === 'function') {
105
+ writer.write(value.toString());
106
+ }
107
+ else if (Array.isArray(value)) {
108
+ writer.write('[');
109
+ value.forEach((item, index) => {
110
+ if (index > 0)
111
+ writer.write(', ');
112
+ writeInitializer(writer, item);
113
+ });
114
+ writer.write(']');
115
+ }
116
+ else if (typeof value === 'object' && value !== null) {
117
+ writer.write('{');
118
+ const entries = Object.entries(value);
119
+ entries.forEach(([k, v], index) => {
120
+ if (index > 0)
121
+ writer.write(', ');
122
+ writer.write(k);
123
+ writer.write(': ');
124
+ writeInitializer(writer, v);
125
+ });
126
+ writer.write('}');
127
+ }
128
+ else {
129
+ writer.write('null');
130
+ }
131
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vovk-cli",
3
- "version": "0.0.1-beta.57",
3
+ "version": "0.0.1-beta.59",
4
4
  "bin": {
5
5
  "vovk": "./dist/index.mjs"
6
6
  },
@@ -15,7 +15,7 @@
15
15
  "test-only": "npm run pre-test && node --experimental-transform-types --experimental-strip-types --test --test-only test/spec/**/*.mts",
16
16
  "test": "npm run pre-test && node --experimental-transform-types --experimental-strip-types --test --test-concurrency=1 test/spec/**/*.mts",
17
17
  "tsc": "tsc --noEmit",
18
- "ncu": "npm-check-updates -u -x commander",
18
+ "ncu": "npm-check-updates -u -x commander,node-pty",
19
19
  "npm-publish": "npm publish"
20
20
  },
21
21
  "repository": {
@@ -35,11 +35,11 @@
35
35
  },
36
36
  "homepage": "https://vovk.dev",
37
37
  "peerDependencies": {
38
- "vovk": "^3.0.0-beta.85",
38
+ "vovk": "^3.0.0-beta.87",
39
39
  "vovk-ajv": "^0.0.0-beta.4",
40
40
  "vovk-client": "^0.0.4-beta.4",
41
41
  "vovk-python": "^0.0.1-beta.4",
42
- "vovk-rust": "^0.0.1-beta.4"
42
+ "vovk-rust": "^0.0.1-beta.6"
43
43
  },
44
44
  "optionalDependencies": {
45
45
  "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.57"
@@ -83,7 +83,7 @@
83
83
  "concat-stream": "^2.0.0",
84
84
  "create-next-app": "^16.1.1",
85
85
  "http-server": "^14.1.1",
86
- "node-pty": "^1.1.0",
86
+ "node-pty": "0.10.1",
87
87
  "tsdown": "^0.18.3",
88
88
  "zod": "^4.2.1"
89
89
  }