add yargs bun and pm2
This commit is contained in:
parent
2190ea3066
commit
806cfa0ff6
@ -6,7 +6,8 @@
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"cli": "./node_modules/bun/bin/bun --bun ./console/bin.ts",
|
||||
"cli": "bun --bun src/console/bin.ts",
|
||||
"dev": "cross-env NODE_ENV=development pnpm cli start -w",
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "cross-env NODE_ENV=production nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
|
@ -200,7 +200,7 @@ importers:
|
||||
version: 28.14.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(jest@30.0.0(@types/node@24.0.3)(ts-node@10.9.2(@swc/core@1.12.1)(@types/node@24.0.3)(typescript@5.8.3)))(typescript@5.8.3)
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^5.4.1
|
||||
version: 5.4.1(@types/eslint@9.6.1)(eslint-config-prettier@10.1.5(eslint@9.29.0))(eslint@9.29.0)(prettier@3.5.3)
|
||||
version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@10.1.5(eslint@9.29.0))(eslint@9.29.0)(prettier@3.5.3)
|
||||
eslint-plugin-unused-imports:
|
||||
specifier: ^4.1.4
|
||||
version: 4.1.4(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)
|
||||
@ -2752,8 +2752,8 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
hasBin: true
|
||||
|
||||
electron-to-chromium@1.5.168:
|
||||
resolution: {integrity: sha512-RUNQmFLNIWVW6+z32EJQ5+qx8ci6RGvdtDC0Ls+F89wz6I2AthpXF0w0DIrn2jpLX0/PU9ZCo+Qp7bg/EckJmA==}
|
||||
electron-to-chromium@1.5.169:
|
||||
resolution: {integrity: sha512-q7SQx6mkLy0GTJK9K9OiWeaBMV4XQtBSdf6MJUzDB/H/5tFXfIiX38Lci1Kl6SsgiEhz1SQI1ejEOU5asWEhwQ==}
|
||||
|
||||
emittery@0.13.1:
|
||||
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
|
||||
@ -2901,8 +2901,8 @@ packages:
|
||||
jest:
|
||||
optional: true
|
||||
|
||||
eslint-plugin-prettier@5.4.1:
|
||||
resolution: {integrity: sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg==}
|
||||
eslint-plugin-prettier@5.5.0:
|
||||
resolution: {integrity: sha512-8qsOYwkkGrahrgoUv76NZi23koqXOGiiEzXMrT8Q7VcYaUISR+5MorIUxfWqYXN0fN/31WbSrxCxFkVQ43wwrA==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
'@types/eslint': '>=8.0.0'
|
||||
@ -7815,7 +7815,7 @@ snapshots:
|
||||
browserslist@4.25.0:
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001723
|
||||
electron-to-chromium: 1.5.168
|
||||
electron-to-chromium: 1.5.169
|
||||
node-releases: 2.0.19
|
||||
update-browserslist-db: 1.1.3(browserslist@4.25.0)
|
||||
|
||||
@ -8190,7 +8190,7 @@ snapshots:
|
||||
dependencies:
|
||||
jake: 10.9.2
|
||||
|
||||
electron-to-chromium@1.5.168: {}
|
||||
electron-to-chromium@1.5.169: {}
|
||||
|
||||
emittery@0.13.1: {}
|
||||
|
||||
@ -8395,7 +8395,7 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
eslint-plugin-prettier@5.4.1(@types/eslint@9.6.1)(eslint-config-prettier@10.1.5(eslint@9.29.0))(eslint@9.29.0)(prettier@3.5.3):
|
||||
eslint-plugin-prettier@5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@10.1.5(eslint@9.29.0))(eslint@9.29.0)(prettier@3.5.3):
|
||||
dependencies:
|
||||
eslint: 9.29.0
|
||||
prettier: 3.5.3
|
||||
|
@ -5,12 +5,13 @@ import {
|
||||
Entity,
|
||||
OneToMany,
|
||||
PrimaryColumn,
|
||||
Relation,
|
||||
Tree,
|
||||
TreeChildren,
|
||||
TreeParent,
|
||||
} from 'typeorm';
|
||||
|
||||
import type { Relation } from 'typeorm';
|
||||
|
||||
import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||
|
||||
@Exclude()
|
||||
|
@ -6,12 +6,13 @@ import {
|
||||
Entity,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
Relation,
|
||||
Tree,
|
||||
TreeChildren,
|
||||
TreeParent,
|
||||
} from 'typeorm';
|
||||
|
||||
import type { Relation } from 'typeorm';
|
||||
|
||||
import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||
|
||||
@Exclude()
|
||||
|
@ -10,10 +10,11 @@ import {
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryColumn,
|
||||
Relation,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import type { Relation } from 'typeorm';
|
||||
|
||||
import { PostBodyType } from '@/modules/content/constants';
|
||||
import { CategoryEntity } from '@/modules/content/entities/category.entity';
|
||||
import { CommentEntity } from '@/modules/content/entities/comment.entity';
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Exclude, Expose } from 'class-transformer';
|
||||
import { Column, Entity, ManyToMany, PrimaryColumn, Relation } from 'typeorm';
|
||||
import { Column, Entity, ManyToMany, PrimaryColumn } from 'typeorm';
|
||||
import type { Relation } from 'typeorm';
|
||||
|
||||
import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||
|
||||
|
@ -10,7 +10,7 @@ import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||
import { CategoryRepository } from '@/modules/content/repositories';
|
||||
import { PostRepository } from '@/modules/content/repositories/post.repository';
|
||||
import { SearchService } from '@/modules/content/services/search.service';
|
||||
import { SearchType } from '@/modules/content/types';
|
||||
import type { SearchType } from '@/modules/content/types';
|
||||
import { BaseService } from '@/modules/database/base/service';
|
||||
import { SelectTrashMode } from '@/modules/database/constants';
|
||||
import { QueryHook } from '@/modules/database/types';
|
||||
|
52
src/modules/core/commands/build.command.ts
Normal file
52
src/modules/core/commands/build.command.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
import { exit } from 'process';
|
||||
|
||||
import { Arguments } from 'yargs';
|
||||
|
||||
import { getCLIConfig } from '@/modules/core/commands/helpers/config';
|
||||
import { BuildCommandArguments } from '@/modules/core/commands/types';
|
||||
import { CommandItem } from '@/modules/core/types';
|
||||
|
||||
export const createBuildCommand: CommandItem<any, BuildCommandArguments> = async (app) => ({
|
||||
command: ['build', 'b'],
|
||||
describe: 'Build application by nest cli.',
|
||||
builder: {
|
||||
nestConfig: {
|
||||
type: 'string',
|
||||
alias: 'n',
|
||||
describe: 'nest cli config file path.',
|
||||
default: 'nest-cli.json',
|
||||
},
|
||||
tsConfig: {
|
||||
type: 'string',
|
||||
alias: 't',
|
||||
describe: 'typescript config file path.',
|
||||
default: 'tsconfig.build.json',
|
||||
},
|
||||
watch: {
|
||||
type: 'boolean',
|
||||
alias: 'w',
|
||||
describe: ' Run in watch mode (live-reload).',
|
||||
default: false,
|
||||
},
|
||||
preserveWatchOutput: {
|
||||
type: 'boolean',
|
||||
alias: 'po',
|
||||
describe: 'Use "preserveWatchOutput" option when using tsc watch mode',
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
handler: async (args: Arguments<BuildCommandArguments>) => {
|
||||
const config = getCLIConfig(args.tsConfig, args.nestConfig);
|
||||
const params = ['build', '-c', args.nestConfig, '-p', args.tsConfig];
|
||||
if (args.watch) {
|
||||
params.push('-w');
|
||||
}
|
||||
if (args.preserveWatchOutput) {
|
||||
params.push('po');
|
||||
}
|
||||
const child = spawn(config.paths.nest, params, config.subprocess.node);
|
||||
child.on('exit', () => exit());
|
||||
},
|
||||
});
|
@ -1,4 +1,13 @@
|
||||
import { FSWatcher } from 'chokidar';
|
||||
import { join } from 'path';
|
||||
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { ActionOnFile, AssetEntry } from '@nestjs/cli/lib/configuration';
|
||||
import chokidar, { FSWatcher } from 'chokidar';
|
||||
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { CLIConfig } from '@/modules/core/commands/types';
|
||||
import { toBoolean } from '@/modules/core/helpers';
|
||||
|
||||
export class Asset {
|
||||
private watchAssetsKeyValue: { [key: string]: boolean } = {};
|
||||
@ -7,5 +16,82 @@ export class Asset {
|
||||
|
||||
private actionInProgress = false;
|
||||
|
||||
closeWatchers() {}
|
||||
closeWatchers() {
|
||||
const timeout = 500;
|
||||
const closeFn = () => {
|
||||
if (this.actionInProgress) {
|
||||
this.actionInProgress = false;
|
||||
setTimeout(closeFn, timeout);
|
||||
} else {
|
||||
this.watchers.forEach((watch) => watch.close());
|
||||
}
|
||||
};
|
||||
setTimeout(closeFn, timeout);
|
||||
}
|
||||
|
||||
watchAssets(config: CLIConfig, codePath: string, changer: () => void) {
|
||||
const assets = get(config.options.nest, 'compilerOptions.assets', []) as AssetEntry[];
|
||||
|
||||
if (assets.length <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const isWatchEnabled = toBoolean(get(config, 'watchAssets', 'src'));
|
||||
const filesToWatch = assets.map<AssetEntry>((item) => {
|
||||
if (typeof item === 'string') {
|
||||
return {
|
||||
glob: join(codePath, item),
|
||||
};
|
||||
}
|
||||
return {
|
||||
glob: join(codePath, item.include!),
|
||||
exclude: item.exclude ? join(codePath, item.exclude) : undefined,
|
||||
flat: item.flat,
|
||||
watchAssets: item.watchAssets,
|
||||
};
|
||||
});
|
||||
|
||||
for (const file of filesToWatch) {
|
||||
const option: ActionOnFile = {
|
||||
action: 'change',
|
||||
item: file,
|
||||
path: '',
|
||||
sourceRoot: codePath,
|
||||
watchAssetsMode: isWatchEnabled,
|
||||
};
|
||||
|
||||
const watcher = chokidar
|
||||
.watch(file.glob, { ignored: file.exclude })
|
||||
.on('add', (path) =>
|
||||
this.actionOnFIle({ ...option, path, action: 'change' }, changer),
|
||||
)
|
||||
.on('change', (path) =>
|
||||
this.actionOnFIle({ ...option, path, action: 'change' }, changer),
|
||||
)
|
||||
.on('unlink', (path) =>
|
||||
this.actionOnFIle({ ...option, path, action: 'unlink' }, changer),
|
||||
);
|
||||
this.watchers.push(watcher);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`An error occurred during the assets copying process. ${(e as any).message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected actionOnFIle(option: ActionOnFile, changer: () => void) {
|
||||
const { action, item, path, watchAssetsMode } = option;
|
||||
const isWatchEnabled = watchAssetsMode || item.watchAssets;
|
||||
|
||||
if (!isWatchEnabled && this.watchAssetsKeyValue[path]) {
|
||||
return;
|
||||
}
|
||||
this.watchAssetsKeyValue[path] = true;
|
||||
this.actionInProgress = true;
|
||||
if (action === 'change') {
|
||||
changer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ const cwdPath = resolve(__dirname, '../../../../..');
|
||||
export function getCLIConfig(
|
||||
tsConfigFile: string,
|
||||
nestConfigFile: string,
|
||||
tsEntryFile: string,
|
||||
tsEntryFile?: string,
|
||||
): CLIConfig {
|
||||
let tsConfig: ts.CompilerOptions = {};
|
||||
const tsConfigPath = join(cwdPath, tsConfigFile);
|
||||
@ -57,7 +57,7 @@ export function getCLIConfig(
|
||||
js: join(dist, nestConfig.entryFile ?? 'main.js'),
|
||||
ts: join(src, tsEntryFile ?? 'main.ts'),
|
||||
bun: './node_modules/bun/bin/bun',
|
||||
nest: './node_modules/@nestjs//cli/bin/nest.js',
|
||||
nest: './node_modules/@nestjs/cli/bin/nest.js',
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -2,16 +2,14 @@ import { isNil } from '@nestjs/common/utils/shared.utils';
|
||||
import { Subprocess } from 'bun';
|
||||
import chalk from 'chalk';
|
||||
import { pick } from 'lodash';
|
||||
import {
|
||||
connect as pm2Connect,
|
||||
disconnect as pm2Disconnect,
|
||||
restart as pm2Restart,
|
||||
start as pm2Start,
|
||||
} from 'pm2';
|
||||
import pm2 from 'pm2';
|
||||
|
||||
import { Arguments } from 'yargs';
|
||||
|
||||
import { Configure } from '@/modules/config/configure';
|
||||
import { Asset } from '@/modules/core/commands/helpers/asset';
|
||||
import { getPm2Config } from '@/modules/core/commands/helpers/config';
|
||||
import { generateSwaggerMetadata } from '@/modules/core/commands/helpers/swagger';
|
||||
import { CLIConfig, StartCommandArguments } from '@/modules/core/commands/types';
|
||||
import { AppConfig } from '@/modules/core/types';
|
||||
|
||||
@ -19,6 +17,9 @@ export async function start(
|
||||
args: Arguments<StartCommandArguments>,
|
||||
config: CLIConfig,
|
||||
): Promise<void> {
|
||||
console.log('command start...');
|
||||
console.log(args);
|
||||
console.log(config);
|
||||
const script = args.typescript ? config.paths.ts : config.paths.js;
|
||||
const params = [config.paths.bun, 'run'];
|
||||
if (args.watch) {
|
||||
@ -29,9 +30,13 @@ export async function start(
|
||||
typeof args.debug === 'string' ? `--inspect=${args.debug}` : '--inspect';
|
||||
params.push(inspectFlag);
|
||||
}
|
||||
if (args.typescript) {
|
||||
generateSwaggerMetadata(args, config, false);
|
||||
}
|
||||
params.push(script);
|
||||
let child: Subprocess;
|
||||
if (args.watch) {
|
||||
const asset = new Asset();
|
||||
const restart = () => {
|
||||
if (!isNil(child)) {
|
||||
child.kill();
|
||||
@ -39,6 +44,12 @@ export async function start(
|
||||
child = Bun.spawn(params, config.subprocess.bun);
|
||||
};
|
||||
restart();
|
||||
asset.watchAssets(config, config.paths.cwd, restart);
|
||||
process.on('exit', () => {
|
||||
child.kill();
|
||||
asset.closeWatchers();
|
||||
process.exit(0);
|
||||
});
|
||||
} else {
|
||||
Bun.spawn(params, {
|
||||
...config.subprocess.bun,
|
||||
@ -94,21 +105,22 @@ export async function startPM2(
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
pm2Disconnect();
|
||||
pm2.disconnect();
|
||||
};
|
||||
|
||||
const restartCallback = (error?: any) => {
|
||||
if (isNil(error)) {
|
||||
pm2Disconnect();
|
||||
pm2.disconnect();
|
||||
} else {
|
||||
pm2Start(pm2config, (err) => startCallback(err));
|
||||
pm2.start(pm2config, (err) => startCallback(err));
|
||||
}
|
||||
};
|
||||
|
||||
pm2Connect((err: any) => {
|
||||
pm2.connect((err: any) => {
|
||||
connectCallback(err);
|
||||
generateSwaggerMetadata(args, config, false);
|
||||
args.restart
|
||||
? pm2Restart(name, restartCallback)
|
||||
: pm2Start(pm2config, (e) => startCallback(e));
|
||||
? pm2.restart(name, restartCallback)
|
||||
: pm2.start(pm2config, (e) => startCallback(e));
|
||||
});
|
||||
}
|
||||
|
39
src/modules/core/commands/helpers/swagger.ts
Normal file
39
src/modules/core/commands/helpers/swagger.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { join } from 'path';
|
||||
|
||||
import { PluginMetadataGenerator } from '@nestjs/cli/lib/compiler/plugins/plugin-metadata-generator';
|
||||
import { PluginOptions } from '@nestjs/cli/lib/configuration';
|
||||
import { ReadonlyVisitor } from '@nestjs/swagger/dist/plugin';
|
||||
import { get, isNil } from 'lodash';
|
||||
import { Arguments } from 'yargs';
|
||||
|
||||
import { CLIConfig, StartCommandArguments } from '@/modules/core/commands/types';
|
||||
|
||||
export function generateSwaggerMetadata(
|
||||
args: Arguments<StartCommandArguments>,
|
||||
config: CLIConfig,
|
||||
watch: boolean,
|
||||
) {
|
||||
const cliPlugins = get(config.options.nest, 'compilerOptions.plugins', []) as (
|
||||
| string
|
||||
| RecordAny
|
||||
)[];
|
||||
const swaggerPlugin = cliPlugins.find(
|
||||
(item) => item === '@nest/swagger' || (item as any).name === '@nest/swagger',
|
||||
);
|
||||
if (!isNil(swaggerPlugin) && args.typescript) {
|
||||
const srcPath = join(config.paths.cwd, config.paths.src);
|
||||
const generator = new PluginMetadataGenerator();
|
||||
let swaggerPluginOption: PluginOptions;
|
||||
if (typeof swaggerPlugin !== 'string' && 'options' in swaggerPlugin) {
|
||||
swaggerPluginOption = swaggerPlugin.options;
|
||||
}
|
||||
generator.generate({
|
||||
visitors: [new ReadonlyVisitor({ ...swaggerPluginOption, pathToSource: srcPath })],
|
||||
outputDir: srcPath,
|
||||
watch,
|
||||
tsconfigPath: args.tsConfig,
|
||||
printDiagnostics: false,
|
||||
});
|
||||
}
|
||||
}
|
@ -1 +1,3 @@
|
||||
export * from './demo.command';
|
||||
export * from './build.command';
|
||||
export * from './start.command';
|
||||
|
@ -60,8 +60,11 @@ export const createStartCommand: CommandItem<any, StartCommandArguments> = async
|
||||
},
|
||||
},
|
||||
handler: async (args: Arguments<StartCommandArguments>) => {
|
||||
console.log('createStartCommand handler start');
|
||||
console.log(app);
|
||||
const { configure } = app;
|
||||
const config = getCLIConfig(args.tsConfig, args.nestConfig, args.entry);
|
||||
console.log(config);
|
||||
if (args.prod || args.restart) {
|
||||
await startPM2(configure, args, config);
|
||||
} else {
|
||||
|
@ -57,3 +57,8 @@ export type StartCommandArguments = {
|
||||
};
|
||||
|
||||
export type Pm2Option = Pick<StartCommandArguments, 'typescript' | 'watch'> & { command: string };
|
||||
|
||||
export type BuildCommandArguments = Pick<StartCommandArguments, 'tsConfig' | 'nestConfig'> & {
|
||||
watch?: string;
|
||||
preserveWatchOutput?: boolean;
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ import * as coreCommands from '../commands';
|
||||
import { App, CommandCollection } from '../types';
|
||||
|
||||
export async function buildCli(creator: () => Promise<App>) {
|
||||
console.log('buildCli start');
|
||||
const app = await creator();
|
||||
const bin = yargs(hideBin(process.argv));
|
||||
app.commands.forEach((cmd) => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ConfigureFactory, ConfigureRegister } from '../config/types';
|
||||
import { createConnectionOptions } from '../config/utils';
|
||||
|
||||
import { MeiliConfig } from './types';
|
||||
import type { MeiliConfig } from './types';
|
||||
|
||||
export const createMeiliConfig: (
|
||||
registre: ConfigureRegister<RePartial<MeiliConfig>>,
|
||||
|
@ -3,7 +3,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { isNil } from 'lodash';
|
||||
import { MeiliSearch } from 'meilisearch';
|
||||
|
||||
import { MeiliConfig } from '@/modules/meilisearch/types';
|
||||
import type { MeiliConfig } from '@/modules/meilisearch/types';
|
||||
|
||||
@Injectable()
|
||||
export class MeiliService {
|
||||
|
Loading…
Reference in New Issue
Block a user