之前,我问了一个问题,以找到递归运行函数以检索NodeJs 这里中文件的所有导入/导出语句的最佳实践。
经过一些讨论后,我还不满意,所以我将把我在前面的讨论中学到的代码重新写出来,希望能找到一种更好的方法,或者简单地对我的代码进行审查。
我调用preprocessTSFiles函数,它使用入口点运行getFiles,并返回它的所有导入/导出语句加上自身,这将导致从VinylFile类递归调用getImporters。
import * as path from 'path';
import * as vinyl from 'vinyl';
import { VinylFile } from './file';
import {
ensureMakeDir,
isArray,
isDirectory,
isEmpty,
isFile,
isNil,
readFile,
writeFile,
} from './utils';
import { InlineStyles } from './plugins/inline-styles';
import { InlineTemplate } from './plugins/inline-template';
export interface HandlerPlugin {
(file: VinylFile): Promise<string>;
}
export async function preprocessTSFiles(entryFilePath: string, destDir: string, baseDir?: string): Promise<void> {
const fileList: string[] = [];
const plugins = [InlineTemplate, InlineStyles];
if (isNil(baseDir)) {
baseDir = path.dirname(entryFilePath);
}
const allFiles = await getFiles(entryFilePath, []);
for (const file of allFiles) {
for (const plugin of plugins) {
const newContent = await plugin(file);
if (newContent != file.content) {
file.setContent(newContent);
}
}
const currentPath = file.filePath;
const destPath = currentPath.replace(baseDir + path.sep, '');
const absDestPath = path.resolve(destDir, destPath);
file.copyTo(absDestPath);
}
}
async function getFiles(entryFilePath: string, excludeList: string[]): Promise<VinylFile[]> {
const entryFile = new VinylFile(entryFilePath);
const excludeFromList: string[] = [entryFilePath];
return [entryFile, ...await entryFile.getImporters(excludeFromList)];
}import * as path from 'path';
import {
ensureMakeDir,
isDirectory,
isFile,
isNil,
readFile,
writeFile,
} from './utils';
export class VinylFile {
private _content: string;
private importStatementRE = /(?:import|export)\s*(?:(?:\{[^}]*\}|\*|\w+)(?:\s*as\s+\w+)?(?:\s+from)?\s*)?([`'"])((?:\\[\s\S]|(?!\1)[^\\])*?)\1/ig;
public dirPath: string;
constructor(public filePath: string) {
this.dirPath = path.dirname(filePath);
}
get content(): string {
if (isNil(this._content)) {
this._content = readFile(this.filePath);
}
return this._content;
}
public setContent(content: string): void {
this._content = content;
}
public copyTo(destFilePath: string): void {
const dirPath = path.dirname(destFilePath);
ensureMakeDir(dirPath);
writeFile(destFilePath, this.content);
}
public hasImporters(): boolean {
this.importStatementRE.lastIndex = 0;
return this.importStatementRE.test(this.content);
}
public async getImporters(excludeFrom?: string[]): Promise<VinylFile[]> {
const importees: VinylFile[] = [];
const importStatementMatchIndex = 2;
let importersMatch: RegExpMatchArray | null;
if (isNil(excludeFrom)) {
excludeFrom = [];
}
// Reset current index to 0
// RegEx.test moves this into 1
this.importStatementRE.lastIndex = 0;
while ((importersMatch = this.importStatementRE.exec(this.content)) && !isNil(importersMatch)) {
const importee = importersMatch[importStatementMatchIndex];
const resolvedPath = path.resolve(this.dirPath, importee);
if (isDirectory(resolvedPath)) {
const resolvedIndexFilePath = path.resolve(resolvedPath, 'index.ts');
if (isFile(resolvedIndexFilePath) && excludeFrom.indexOf(resolvedIndexFilePath) < 0) {
const resolvedIndexFile = new VinylFile(resolvedIndexFilePath);
excludeFrom.push(resolvedIndexFilePath);
importees.push(resolvedIndexFile);
if (resolvedIndexFile.hasImporters()) {
importees.push(...await resolvedIndexFile.getImporters(excludeFrom));
}
}
} else {
let resolvedFilePath = resolvedPath;
if (path.extname(resolvedFilePath) !== '.ts') {
resolvedFilePath += '.ts';
}
if (isFile(resolvedFilePath) && excludeFrom.indexOf(resolvedFilePath) < 0) {
const resolvedFile = new VinylFile(resolvedFilePath);
excludeFrom.push(resolvedFilePath);
importees.push(resolvedFile);
if (resolvedFile.hasImporters()) {
importees.push(...await resolvedFile.getImporters(excludeFrom));
}
}
}
}
return importees;
}
}发布于 2017-08-23 19:32:01
我看到在重构之后,您的代码更易读。
你选择了更好的名字来澄清意图。这是一个很好的部分。
我认为思考的重点是时间的部分。
以功能的方式,您可以改进代码,将匹配的部分放入方法中:
getImportStatements() {
const importStatementMatchIndex = 2;
const importStatements = [];
let importersMatch = this.importStatementRE.exec(this.content);
while(!isNil(impotersMatch)) {
importStatements.push(impotersMatch[importStatementMatchIndex]);
importersMatch = this.importStatementRE.exec(this.content);
}
return importStatements;
}这将只返回一个具有匹配字符串的列表。进行此更改的原因是试图以一种代码易于阅读和更改的方式来划分响应。
此方法隔离了如何获取源文件上的导入列表的逻辑。
然后可以重写main方法,删除while循环并添加以下内容:
this.getImportStatements()
.map((importee) => (path.resolve(this.dirPath, importee)))
.map((resolvedPath) => {
if (isDirectory(resolvedPath)) {
return path.resolve(resolvedPath, 'index.ts');
}
return (path.extname(resolvedPath) !== '.ts') ? resolvedPath += '.ts' : resolvedPath;
})
.filter((resolvedFilePath) => (!isFile(resolvedFilePath) || excludeFrom.indexOf(resolvedFilePath) >= 0))
.forEach((resolvedFilePath) => {
const resolvedFile = new VinylFile(resolvedFilePath);
excludeFrom.push(resolvedFilePath);
importees.push(resolvedFile);
if (resolvedFile.hasImporters()) {
importees.push(...await resolvedFile.getImporters(excludeFrom));
}
});其原则是附加从getImportStatements()返回的数组,以保证至少返回一个空数组。
然后使用javascript数组方法将字符串映射到完整路径,然后过滤,然后收集。
可以随意更改方法中的箭头函数,以增加代码的可读性。
https://codereview.stackexchange.com/questions/173747
复制相似问题