Skip to content

Commit

Permalink
[Feature] 新增 less 等功能
Browse files Browse the repository at this point in the history
  • Loading branch information
VisualSJ committed Nov 21, 2023
1 parent 755cd78 commit 097326e
Show file tree
Hide file tree
Showing 19 changed files with 600 additions and 104 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"eslint": "^8.53.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-prettier": "^5.0.1",
"less": "^4.2.0",
"typescript": "^5.2.2"
},
"dependencies": {
Expand Down
125 changes: 125 additions & 0 deletions source/internal/file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import {
join,
isAbsolute,
relative,
} from 'node:path';
import {
existsSync,
statSync,
Stats,
copyFileSync,
} from 'node:fs';

import { green, italic } from 'chalk';

import { registerTask, Task, TaskState } from '../task';
import { forEachFiles, makeDir } from '../utils';

export type FileConfig = {
source: string;
dist: string;
filter(file: string, stat: Stats): boolean;
}[];

export class FileTask extends Task {
static getMaxConcurrent() {
return 1;
}

getTitle() {
return 'Copy files';
}

async execute(workspace: string, configArray: FileConfig): Promise<TaskState> {
let hasError = false;

for (const config of configArray) {
// 将相对路径转成绝对路径
const source = isAbsolute(config.source)
? config.source
: join(workspace, config.source);
const dist = isAbsolute(config.dist)
? config.dist
: join(workspace, config.dist);

const dataItem = this.getCache(source);

// 新的缓存数据
const newDataItem: {
[key: string]: number;
} = {};

// 编译的文件是否有变化
let changed = false;
const fileArray: {
source: string;
dist: string;
}[] = [];

// 获取编译的文件列表
try {
await forEachFiles(source, (file, stat) => {
const relativePath = relative(source, file);
const distFile = join(dist, relativePath);
if (config.filter && !config.filter(file, stat)) {
return;
}
if (stat.isDirectory()) {
return;
}
fileArray.push({
source: file,
dist: distFile,
});
});

this.print(`${italic(`${config.source} => ${config.dist}`)} Copy files: ${green(fileArray.length)}`);

fileArray.forEach((item) => {
const stat = statSync(item.source);
const mtime = stat.mtime.getTime();
if (
!dataItem[item.source]
|| mtime !== dataItem[item.source]
|| !existsSync(item.dist)
) {
changed = true;
}
newDataItem[item.source] = mtime;
});
} catch (error) {
const err = error as Error;
this.print(err.message);
hasError = true;
}

// 没有变化
if (changed === false) {
continue;
}

// 实际拷贝
try {
for (const item of fileArray) {
const baseDir = join(item.dist, '..');
if (!existsSync(baseDir)) {
await makeDir(baseDir);
}
if (existsSync(item.source) && !existsSync(item.dist)) {
copyFileSync(item.source, item.dist);
}
}

// 有变化的时候,更新缓存
this.setCache(source, newDataItem);
} catch (error) {
const err = error as Error;
this.print(err.message);
hasError = true;
}
}

return hasError ? TaskState.error : TaskState.success;
}
}
registerTask('file', FileTask);
24 changes: 24 additions & 0 deletions source/internal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { TscConfig, TscTask } from './tsc';
import { RepoConfig, RepoTask } from './repo';
import { LessConfig, LessTask } from './less';
import { FileConfig, FileTask } from './file';

/**
* 任务配置
*/
export type ConfigType = {
tsc: TscConfig,
repo: RepoConfig,
less: LessConfig,
file: FileConfig,
};

/**
* 任务列表
*/
export const Task = {
tsc: TscTask,
repo: RepoTask,
less: LessTask,
file: FileTask,
};
106 changes: 106 additions & 0 deletions source/internal/less.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
join, isAbsolute,
} from 'node:path';
import {
existsSync,
statSync,
} from 'node:fs';
import { cpus } from 'node:os';

import { green, italic } from 'chalk';

import { registerTask, Task, TaskState } from '../task';
import { bash } from '../utils';

export type LessConfig = {
source: string;
dist: string;
compress: boolean;
}[];

export class LessTask extends Task {
static getMaxConcurrent() {
return cpus().length;
}

getTitle() {
return 'Compile with less';
}

async execute(workspace: string, configArray: LessConfig): Promise<TaskState> {
let hasError = false;

for (const config of configArray) {
// 将相对路径转成绝对路径
const path = isAbsolute(config.source) ? config.source : join(workspace, config.source);

const dataItem = this.getCache(path);

// 新的缓存数据
const newDataItem: {
[key: string]: number;
} = {};

// 编译的文件是否有变化
let changed = false;

// 获取编译的文件列表
try {
const fileArray: string[] = [
path,
];
const out = './.less.cache.json';
await bash('npx', ['lessc', '--depends', config.source, out], {
cwd: workspace,
}, (data) => {
let str = data.toString();
if (str.startsWith(out)) {
str = str.substring(out.length + 2);
}
str.split(/\.less /).forEach((fileWithoutExt) => {
const file = `${fileWithoutExt}.less`;
if (existsSync(file)) {
fileArray.push(file);
}
});
});
this.print(`${italic(config.source)} Compile files: ${green(fileArray.length)}`);

fileArray.forEach((file) => {
const stat = statSync(file);
const mtime = stat.mtime.getTime();
if (!dataItem[file] || mtime !== dataItem[file]) {
changed = true;
}
newDataItem[file] = mtime;
});
} catch (error) {
const err = error as Error;
this.print(err.message);
hasError = true;
}

// 没有变化
if (changed === false) {
continue;
}

// 实际编译
try {
await bash('npx', ['lessc', config.source, config.dist], {
cwd: workspace,
});

// 有变化的时候,更新缓存
this.setCache(path, newDataItem);
} catch (error) {
const err = error as Error;
this.print(err.message);
hasError = true;
}
}

return hasError ? TaskState.error : TaskState.success;
}
}
registerTask('less', LessTask);
83 changes: 83 additions & 0 deletions source/internal/npm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
join,
isAbsolute,
} from 'node:path';
import {
createWriteStream,
WriteStream,
} from 'node:fs';

import { italic } from 'chalk';

import { registerTask, Task, TaskState } from '../task';
import { bash } from '../utils';

export type NPMConfig = {
// 安装的时候输出的信息
message: string;
// 执行 NPM 命令的路径
path: string;
// 执行的参数
params: string[],
// 错误的时候输出的信息
detail: string;
// 存放日志的路径,当没有设置的时候,会将日志输出到控制台
logFile?: string,
}[];

export class FileTask extends Task {
static getMaxConcurrent() {
return 1;
}

getTitle() {
return 'Run npm command';
}

async execute(workspace: string, configArray: NPMConfig): Promise<TaskState> {
let hasError = false;

for (const config of configArray) {
// 将相对路径转成绝对路径
const source = isAbsolute(config.path)
? config.path
: join(workspace, config.path);

this.print(italic(`npm ${config.params.join(' ')}`));
if (config.message) {
this.print(italic(config.message));
}
this.print(`Execution Path: ${config.path}`);
if (config.logFile) {
this.print(`Log file: ${config.logFile}`);
}

// 执行命令
try {
let writeStream: WriteStream | undefined;
if (config.logFile) {
writeStream = createWriteStream(config.logFile, { flags: 'a' });
}
await bash('npm', config.params, {
cwd: source,
}, (data) => {
if (writeStream) {
writeStream.write(data.toString());
} else {
this.print(data.toString());
}
});
if (writeStream) {
writeStream.close();
}
} catch (error) {
const err = error as Error;
this.print(err.message);
hasError = true;
}
}

return hasError ? TaskState.error : TaskState.success;
}
}
registerTask('file', FileTask);
Loading

0 comments on commit 097326e

Please sign in to comment.