Skip to content

Commit

Permalink
Optimise plugin application rounds
Browse files Browse the repository at this point in the history
  • Loading branch information
t-ski committed Sep 27, 2024
1 parent ebdda48 commit da5a95d
Show file tree
Hide file tree
Showing 50 changed files with 1,923 additions and 1,847 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ jobs:
with:
node-version: 21
- uses: actions/checkout@v4
# - run: npm ci
# - run: npm run build
# - run: npm run test
- run: npm ci
- run: npm run build
- run: npm run test
18 changes: 9 additions & 9 deletions packages/rjs-build/src/AFilesystemNode.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { normalize, basename } from "path";

export abstract class AFilesystemNode {
public readonly absolutePath: string;
public readonly relativePath: string;
public readonly name: string;
public readonly extension: string;
public readonly absolutePath: string;
public readonly relativePath: string;
public readonly name: string;
public readonly extension: string;

constructor(relativePath: string) {
this.relativePath = normalize(`./${relativePath}`);
this.name = basename(relativePath).replace(/\.[^.]+$/, "");
this.extension = (relativePath.match(/\.([^.]+)$/) ?? [])[1];
}
constructor(relativePath: string) {
this.relativePath = normalize(`./${relativePath}`);
this.name = basename(relativePath).replace(/\.[^.]+$/, "");
this.extension = (relativePath.match(/\.([^.]+)$/) ?? [])[1];
}
}
195 changes: 101 additions & 94 deletions packages/rjs-build/src/Build.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Dirent, readdirSync } from "fs";
import { join, resolve } from "path";
import { join, normalize, resolve } from "path";

import { AFilesystemNode } from "./AFilesystemNode";
import { Directory } from "./Directory";
import { File } from "./File";
import { Filemap } from "./Filemap";
Expand All @@ -14,104 +13,112 @@ export enum EBuildFilter {
}

export class Build<O extends { [key: string]: unknown }> {
private readonly dev: boolean;
private readonly outpathMap: Map<string, Plugin<O>> = new Map();
private readonly pluginDirectoryPath: string;
private readonly dev: boolean;
private readonly outpathMap: Map<string, Plugin<O>> = new Map();
private readonly pluginDirectoryPath: string;
private readonly plugins: Map<string, Plugin<O>> = new Map();

private plugins: Plugin<O>[] = [];
constructor(pluginDirectoryPath: string, dev?: boolean) {
this.dev = !!dev;
this.pluginDirectoryPath = pluginDirectoryPath;
}

constructor(pluginDirectoryPath: string, dev?: boolean) {
this.dev = !!dev;
this.pluginDirectoryPath = pluginDirectoryPath;
}
private normalizeRelativePath(relativePath: string): string {
return resolve(`./${relativePath}`);
}

private normalizeRelativePath(relativePath: string): string {
return resolve(`./${relativePath}`);
}
private fetchPlugins() {
const pluginDirectoryPaths: string[] = [];

private fetchPlugins() {
this.plugins = readdirSync(this.pluginDirectoryPath, {
withFileTypes: true
})
readdirSync(this.pluginDirectoryPath, {
withFileTypes: true
})
.filter((dirent: Dirent) => dirent.isDirectory())
.reduce((acc: Plugin<O>[], dirent: Dirent) => {
return Plugin.isPluginDirectory(
join(this.pluginDirectoryPath, dirent.name)
)
? [
...acc,
new Plugin(
join(this.pluginDirectoryPath, dirent.name),
this.dev
)
]
: acc;
}, []);
}

public async retrieveAll(
options?: O,
filter: EBuildFilter = EBuildFilter.ALL
): Promise<Filemap> {
this.fetchPlugins(); // TODO: skip already loaded ones in prod?

return new Filemap(
await this.plugins.reduce(
async (acc: Promise<AFilesystemNode[]>, plugin: Plugin<O>) => {
const fileNodes: AFilesystemNode[] =
await plugin.apply(options);

fileNodes.forEach((fileNode: AFilesystemNode) => {
this.outpathMap.set(
this.normalizeRelativePath(fileNode.relativePath),
plugin
);
});

return [
...(await acc),
...fileNodes.filter((fileNode: AFilesystemNode) => {
switch (filter) {
case EBuildFilter.DIRECTORY:
return fileNode instanceof Directory;
case EBuildFilter.FILE:
return fileNode instanceof File;
}
return true;
})
];
},
Promise.resolve([] as AFilesystemNode[])
)
);
}

public async retrieve(
relativePath: string,
options?: O
): Promise<AFilesystemNode | null> {
const filterFilesystemNode = (
fileNodes: AFilesystemNode[]
): AFilesystemNode => {
return fileNodes
.filter((fileNode: AFilesystemNode) => {
return (
this.normalizeRelativePath(fileNode.relativePath) ===
.forEach((dirent: Dirent) => {
const pluginDirectoryPath: string = normalize(
join(this.pluginDirectoryPath, dirent.name)
);
if (!Plugin.isPluginDirectory(pluginDirectoryPath)) return;
pluginDirectoryPaths.push(pluginDirectoryPath);

if (this.plugins.has(pluginDirectoryPath)) return;

this.plugins.set(
pluginDirectoryPath,
new Plugin(
join(this.pluginDirectoryPath, dirent.name),
this.dev
)
);
});

Array.from(this.plugins.keys()).forEach(
(pluginDirectoryPath: string) => {
if (pluginDirectoryPaths.includes(pluginDirectoryPath)) return;

this.plugins.delete(pluginDirectoryPath);
}
);
}

public async retrieveAll(
options?: O,
filter: EBuildFilter = EBuildFilter.ALL
): Promise<Filemap> {
this.fetchPlugins();

return new Filemap(
await Array.from(this.plugins.values()).reduce(
async (acc: Promise<File[]>, plugin: Plugin<O>) => {
const files: File[] = await plugin.apply(options);
files.forEach((fileNode: File) => {
this.outpathMap.set(
this.normalizeRelativePath(fileNode.relativePath),
plugin
);
});

return [
...(await acc),
...files.filter((file: File) => {
switch (filter) {
case EBuildFilter.DIRECTORY:
return file instanceof Directory;
case EBuildFilter.FILE:
return file instanceof File;
}
return true;
})
];
},
Promise.resolve([] as File[])
)
);
}

public async retrieve(
relativePath: string,
options?: O
): Promise<File | null> {
const filterFilesystemNode = (files: File[]): File => {
return files
.filter((fileNode: File) => {
return (
this.normalizeRelativePath(fileNode.relativePath) ===
this.normalizeRelativePath(relativePath)
);
);
})
.pop();
};

if (!this.outpathMap.has(this.normalizeRelativePath(relativePath))) {
return filterFilesystemNode((await this.retrieveAll()).fileNodes);
}

const relatedPlugin: Plugin<O> = this.outpathMap.get(
this.normalizeRelativePath(relativePath)
);
return relatedPlugin
? filterFilesystemNode(await relatedPlugin.apply(options))
: null;
}
};

!this.outpathMap.has(this.normalizeRelativePath(relativePath)) &&
(await this.retrieveAll()); // TODO: Always retrieve all for presuming new file?

const relatedPlugin: Plugin<O> = this.outpathMap.get(
this.normalizeRelativePath(relativePath)
);
return relatedPlugin
? filterFilesystemNode(await relatedPlugin.apply(options))
: null;
}
}
30 changes: 15 additions & 15 deletions packages/rjs-build/src/Directory.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { AFilesystemNode } from "./AFilesystemNode";

export class Directory extends AFilesystemNode {
public readonly fileNodes: AFilesystemNode[];
public readonly fileNodes: AFilesystemNode[];

constructor(relativePath: string, fileNodes: AFilesystemNode[] = []) {
super(relativePath);
constructor(relativePath: string, fileNodes: AFilesystemNode[] = []) {
super(relativePath);

this.fileNodes = fileNodes;
}
this.fileNodes = fileNodes;
}

public traverse(
itemCb: (fileNode: AFilesystemNode) => void,
recursive: boolean = false
) {
this.fileNodes.forEach((fileNode: AFilesystemNode) => {
fileNode instanceof Directory
? recursive && fileNode.traverse(itemCb, recursive)
: itemCb(fileNode);
});
}
public traverse(
itemCb: (fileNode: AFilesystemNode) => void,
recursive: boolean = false
) {
this.fileNodes.forEach((fileNode: AFilesystemNode) => {
fileNode instanceof Directory
? recursive && fileNode.traverse(itemCb, recursive)
: itemCb(fileNode);
});
}
}
10 changes: 5 additions & 5 deletions packages/rjs-build/src/File.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { AFilesystemNode } from "./AFilesystemNode";

export class File extends AFilesystemNode {
public readonly contents?: Buffer | string;
public readonly contents?: Buffer | string;

constructor(relativePath: string, contents?: Buffer | string) {
super(relativePath);
constructor(relativePath: string, contents?: Buffer | string) {
super(relativePath);

this.contents = contents;
}
this.contents = contents;
}
}
34 changes: 17 additions & 17 deletions packages/rjs-build/src/Filemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@ import { resolve } from "path";
import { AFilesystemNode } from "./AFilesystemNode";

export class Filemap {
private readonly pathMap: Map<string, AFilesystemNode> = new Map();
private readonly pathMap: Map<string, AFilesystemNode> = new Map();

public readonly fileNodes: AFilesystemNode[];
public readonly fileNodes: AFilesystemNode[];

constructor(fileNodes: AFilesystemNode[] = []) {
this.fileNodes = fileNodes;
constructor(fileNodes: AFilesystemNode[] = []) {
this.fileNodes = fileNodes;

fileNodes.forEach((fileNode: AFilesystemNode) => {
this.pathMap.set(
this.normalizeRelativePath(fileNode.relativePath),
fileNode
);
});
}
fileNodes.forEach((fileNode: AFilesystemNode) => {
this.pathMap.set(
this.normalizeRelativePath(fileNode.relativePath),
fileNode
);
});
}

private normalizeRelativePath(relativePath: string): string {
return resolve(`./${relativePath}`);
}
private normalizeRelativePath(relativePath: string): string {
return resolve(`./${relativePath}`);
}

public lookup(relativePath: string): AFilesystemNode {
return this.pathMap.get(this.normalizeRelativePath(relativePath));
}
public lookup(relativePath: string): AFilesystemNode {
return this.pathMap.get(this.normalizeRelativePath(relativePath));
}
}
20 changes: 10 additions & 10 deletions packages/rjs-build/src/Filesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import { Directory } from "./Directory";
import { Filemap } from "./Filemap";

export class Filesystem extends Directory {
private readonly filemap: Filemap;
private readonly filemap: Filemap;

public readonly rootPath: string;
public readonly rootPath: string;

constructor(rootPath: string, fileNodes: AFilesystemNode[] = []) {
super(".", fileNodes);
constructor(rootPath: string, fileNodes: AFilesystemNode[] = []) {
super(".", fileNodes);

this.filemap = new Filemap(fileNodes);
this.rootPath = rootPath;
}
this.filemap = new Filemap(fileNodes);
this.rootPath = rootPath;
}

public get(relativePath: string): AFilesystemNode {
return this.filemap.lookup(relativePath);
}
public get(relativePath: string): AFilesystemNode {
return this.filemap.lookup(relativePath);
}
}
Loading

0 comments on commit da5a95d

Please sign in to comment.