feat: add pack module
This commit is contained in:
@@ -8,7 +8,7 @@ import { getConfig } from '@/module/index.ts';
|
||||
import inquirer from 'inquirer';
|
||||
|
||||
const command = new Command('deploy')
|
||||
.description('deploy to server')
|
||||
.description('把前端文件传到服务器')
|
||||
.argument('<filePath>', 'Path to the file to be uploaded') // 定义文件路径参数
|
||||
.option('-v, --version <version>', 'verbose')
|
||||
.option('-k, --key <key>', 'key')
|
||||
|
||||
225
src/command/publish.ts
Normal file
225
src/command/publish.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import * as tar from 'tar';
|
||||
import glob from 'fast-glob';
|
||||
import { program, Command } from '@/program.ts';
|
||||
import { getConfig } from '@/module/index.ts';
|
||||
import { fileIsExist } from '@/uitls/file.ts';
|
||||
import ignore from 'ignore';
|
||||
|
||||
// 查找文件(忽略大小写)
|
||||
async function findFileInsensitive(targetFile: string): Promise<string | null> {
|
||||
const files = await fs.readdir('.');
|
||||
const matchedFile = files.find((file) => file.toLowerCase() === targetFile.toLowerCase());
|
||||
return matchedFile || null;
|
||||
}
|
||||
|
||||
// 递归收集文件信息
|
||||
async function collectFileInfo(filePath: string, baseDir = '.'): Promise<any[]> {
|
||||
const stats = await fs.stat(filePath);
|
||||
const relativePath = path.relative(baseDir, filePath);
|
||||
|
||||
if (stats.isFile()) {
|
||||
return [{ path: relativePath, size: stats.size }];
|
||||
}
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
const files = await fs.readdir(filePath);
|
||||
const results = await Promise.all(files.map((file) => collectFileInfo(path.join(filePath, file), baseDir)));
|
||||
return results.flat();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
// 解析 .npmignore 文件
|
||||
async function loadNpmIgnore(cwd: string): Promise<ignore.Ignore> {
|
||||
const npmIgnorePath = path.join(cwd, '.npmignore');
|
||||
const ig = ignore.default();
|
||||
|
||||
try {
|
||||
const content = await fs.readFile(npmIgnorePath, 'utf-8');
|
||||
ig.add(content);
|
||||
} catch (err) {
|
||||
console.warn('.npmignore not found, using default ignore rules');
|
||||
// 如果没有 .npmignore 文件,使用默认规则
|
||||
ig.add(['node_modules', '.git']);
|
||||
}
|
||||
|
||||
return ig;
|
||||
}
|
||||
// 获取文件列表,兼容 .npmignore
|
||||
async function getFiles(cwd: string, patterns: string[]): Promise<string[]> {
|
||||
const ig = await loadNpmIgnore(cwd);
|
||||
|
||||
// 使用 fast-glob 匹配文件
|
||||
const allFiles = await glob(patterns, {
|
||||
cwd,
|
||||
dot: true, // 包括隐藏文件
|
||||
onlyFiles: false, // 包括目录
|
||||
followSymbolicLinks: true,
|
||||
});
|
||||
|
||||
// 过滤忽略的文件
|
||||
const filteredFiles = allFiles.filter((file) => !ig.ignores(file));
|
||||
return filteredFiles;
|
||||
}
|
||||
|
||||
const pack = async () => {
|
||||
const cwd = process.cwd();
|
||||
const collection: Record<string, any> = {};
|
||||
const packageJsonPath = path.join(cwd, 'package.json');
|
||||
if (!(await fileIsExist(packageJsonPath))) {
|
||||
console.error('package.json not found');
|
||||
return;
|
||||
}
|
||||
|
||||
let packageJson;
|
||||
try {
|
||||
const packageContent = await fs.readFile(packageJsonPath, 'utf-8');
|
||||
packageJson = JSON.parse(packageContent);
|
||||
} catch (error) {
|
||||
console.error('Invalid package.json:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
let outputFileName = `${packageJson.name}-${packageJson.version}.tgz`
|
||||
.replace('@', '') // 替换特殊字符 @
|
||||
.replace(/[\/\\:*?"<>|]/g, '-'); // 替换特殊字符
|
||||
|
||||
const outputFilePath = path.join(cwd, outputFileName);
|
||||
|
||||
// 从 package.json 的 files 字段收集文件
|
||||
const filesToInclude = packageJson.files
|
||||
? await glob(packageJson.files, {
|
||||
cwd: cwd,
|
||||
dot: true, // 包括隐藏文件
|
||||
onlyFiles: false, // 包括目录
|
||||
followSymbolicLinks: true, // 处理符号链接
|
||||
})
|
||||
: [];
|
||||
// 确保 README.md 和 dist 存在(忽略大小写检测 README.md)
|
||||
const readmeFile = await findFileInsensitive('README.md');
|
||||
if (readmeFile && !filesToInclude.includes(readmeFile)) {
|
||||
filesToInclude.push(readmeFile);
|
||||
}
|
||||
const packageFile = await findFileInsensitive('package.json');
|
||||
if (packageFile && !filesToInclude.includes(packageFile)) {
|
||||
filesToInclude.push(packageFile);
|
||||
}
|
||||
const allFiles = (await Promise.all(filesToInclude.map((file) => collectFileInfo(file)))).flat();
|
||||
|
||||
// 输出文件详细信息
|
||||
console.log('Tarball Contents:');
|
||||
allFiles.forEach((file) => {
|
||||
console.log(`${file.size}B ${file.path}`);
|
||||
});
|
||||
const totalSize = allFiles.reduce((sum, file) => sum + file.size, 0);
|
||||
const packageSize = (totalSize / 1024).toFixed(2) + ' kB';
|
||||
|
||||
collection.files = allFiles;
|
||||
collection.packageJson = packageJson;
|
||||
collection.totalSize = totalSize;
|
||||
|
||||
console.log('\nTarball Details');
|
||||
console.log(`name: ${packageJson.name}`);
|
||||
console.log(`version: ${packageJson.version}`);
|
||||
console.log(`filename: ${outputFileName}`);
|
||||
console.log(`package size: ${packageSize}`);
|
||||
console.log(`total files: ${allFiles.length}`);
|
||||
console.log(`Created package: ${outputFileName}`);
|
||||
try {
|
||||
await tar.c(
|
||||
{
|
||||
gzip: true,
|
||||
file: outputFilePath,
|
||||
cwd: cwd,
|
||||
},
|
||||
filesToInclude,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error creating tarball:', error);
|
||||
}
|
||||
return { collection, outputFilePath };
|
||||
};
|
||||
const packByIgnore = async () => {
|
||||
let collection: Record<string, any> = {};
|
||||
const cwd = process.cwd();
|
||||
const patterns = ['**/*']; // 匹配所有文件
|
||||
const packageJsonPath = path.join(cwd, 'package.json');
|
||||
let packageJson;
|
||||
try {
|
||||
const packageContent = await fs.readFile(packageJsonPath, 'utf-8');
|
||||
packageJson = JSON.parse(packageContent);
|
||||
} catch (error) {
|
||||
console.error('Invalid package.json:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
let outputFileName = `${packageJson.name}-${packageJson.version}.tgz`
|
||||
.replace('@', '') // 替换特殊字符 @
|
||||
.replace(/[\/\\:*?"<>|]/g, '-'); // 替换特殊字符
|
||||
|
||||
const outputFilePath = path.join(cwd, outputFileName);
|
||||
|
||||
// 获取符合条件的文件列表
|
||||
const files = await getFiles(cwd, patterns);
|
||||
|
||||
console.log('Files to include in the package:');
|
||||
// files 获取 size 和 path
|
||||
const filesInfo = await Promise.all(files.map((file) => collectFileInfo(file)));
|
||||
const allFiles = filesInfo.flat();
|
||||
allFiles.forEach((file) => {
|
||||
console.log(`${file.size}B ${file.path}`);
|
||||
});
|
||||
|
||||
const totalSize = allFiles.reduce((sum, file) => sum + file.size, 0);
|
||||
const packageSize = (totalSize / 1024).toFixed(2) + ' kB';
|
||||
|
||||
collection.files = allFiles;
|
||||
collection.packageJson = packageJson;
|
||||
collection.totalSize = totalSize;
|
||||
|
||||
console.log('\nTarball Details');
|
||||
console.log(`package size: ${packageSize}`);
|
||||
console.log(`total files: ${allFiles.length}`);
|
||||
const filesToInclude = files.map((file) => path.relative(cwd, file));
|
||||
try {
|
||||
await tar.c(
|
||||
{
|
||||
gzip: true,
|
||||
file: outputFilePath,
|
||||
cwd: cwd,
|
||||
},
|
||||
filesToInclude,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error creating tarball:', error);
|
||||
}
|
||||
return { collection, outputFilePath };
|
||||
};
|
||||
const publishCommand = new Command('publish')
|
||||
.description('发布应用')
|
||||
.option('-k, --key <key>', '应用 key')
|
||||
.option('-v, --version <version>', '应用版本')
|
||||
.action(async (options) => {
|
||||
const { key, version } = options;
|
||||
const config = await getConfig();
|
||||
const form = new FormData();
|
||||
console.log('发布逻辑实现', { key, version, config });
|
||||
});
|
||||
|
||||
const packCommand = new Command('pack')
|
||||
.description('打包应用, 默认使用 package.json 中的 files 字段')
|
||||
.option('-i, --ignore', '使用 .npmignore 文件模式去忽略文件进行打包, 不需要package.json中的files字段')
|
||||
.action(async (opts) => {
|
||||
let value: { collection: Record<string, any>; outputFilePath: string };
|
||||
if (opts.ignore) {
|
||||
value = await packByIgnore();
|
||||
} else {
|
||||
value = await pack();
|
||||
}
|
||||
//
|
||||
});
|
||||
|
||||
program.addCommand(publishCommand);
|
||||
program.addCommand(packCommand);
|
||||
@@ -9,6 +9,7 @@ import './command/config.ts';
|
||||
import './command/web.ts';
|
||||
import './command/router.ts';
|
||||
import './command/npm.ts';
|
||||
import './command/publish.ts';
|
||||
|
||||
// program.parse(process.argv);
|
||||
|
||||
|
||||
0
src/module/pino.ts
Normal file
0
src/module/pino.ts
Normal file
Reference in New Issue
Block a user