Skip to content

Commit 10f9182

Browse files
committed
Make this pipeline the basis for other file processors
1 parent 0521023 commit 10f9182

File tree

3 files changed

+106
-87
lines changed

3 files changed

+106
-87
lines changed

lib/index.js

Lines changed: 39 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,67 @@
1-
import { FileFinder } from "./util.js";
2-
import { readFile, stat } from "node:fs/promises";
1+
import { buildProcessPipeline } from "./util.js";
2+
import { readFile } from "node:fs/promises";
33
import * as path from "node:path";
44

55
export const key = "files";
66
export const bucket = "static";
77

88
/** @type FaucetPlugin<Config> */
9-
export function plugin(config, assetManager, { compact } = {}) {
10-
let copiers = config.map(copyConfig =>
11-
makeCopier(copyConfig, assetManager, { compact }));
9+
export function plugin(config, assetManager, options) {
10+
let pipeline = config.map(copyConfig => {
11+
let processFile = buildProcessFile(copyConfig, options);
12+
let { source, target, filter } = copyConfig;
13+
return buildProcessPipeline(source, target, processFile, assetManager, filter);
14+
});
1215

13-
return filepaths => Promise.all(copiers.map(copy => copy(filepaths)));
16+
return filepaths => Promise.all(pipeline.map(copy => copy(filepaths)));
1417
}
1518

1619
/**
17-
* Create a copier for a single configuration
20+
* Returns a function that copies a single file with optional compactor
1821
*
1922
* @param {Config} copyConfig
20-
* @param {AssetManager} assetManager
2123
* @param {FaucetPluginOptions} options
22-
* @returns {FaucetPluginFunc}
24+
* @returns {ProcessFile}
2325
*/
24-
function makeCopier(copyConfig, assetManager, { compact } = {}) {
25-
let source = assetManager.resolvePath(copyConfig.source);
26-
let target = assetManager.resolvePath(copyConfig.target, {
27-
enforceRelative: true
28-
});
29-
let fileFinder = new FileFinder(source, {
30-
skipDotfiles: true,
31-
filter: copyConfig.filter
32-
});
33-
let { fingerprint } = copyConfig;
34-
let compactors = (compact && copyConfig.compact) || {};
26+
function buildProcessFile(copyConfig, options) {
27+
let compactors = (options.compact && copyConfig.compact) || {};
3528

36-
return async filepaths => {
37-
let [filenames, targetDir] = await Promise.all([
38-
(filepaths ? fileFinder.match(filepaths) : fileFinder.all()),
39-
determineTargetDir(source, target)
40-
]);
29+
return async function(filename,
30+
{ source, target, targetDir, assetManager }) {
31+
let sourcePath = path.join(source, filename);
32+
let targetPath = path.join(target, filename);
33+
let content;
4134

42-
return Promise.all(filenames.map(filename => processFile(filename, {
43-
assetManager, source, target, targetDir, compactors, fingerprint
44-
})));
45-
};
46-
}
47-
48-
/**
49-
* If `source` is a directory, `target` is used as target directory -
50-
* otherwise, `target`'s parent directory is used
51-
*
52-
* @param {string} source
53-
* @param {string} target
54-
* @returns {Promise<string>}
55-
*/
56-
async function determineTargetDir(source, target) {
57-
let results = await stat(source);
58-
return results.isDirectory() ? target : path.dirname(target);
59-
}
60-
61-
/**
62-
* @param {string} filename
63-
* @param {ProcessFile} opts
64-
* @returns {Promise<unknown>}
65-
*/
66-
async function processFile(filename,
67-
{ source, target, targetDir, fingerprint, assetManager, compactors }) {
68-
let sourcePath = path.join(source, filename);
69-
let targetPath = path.join(target, filename);
70-
71-
try {
72-
var content = await readFile(sourcePath); // eslint-disable-line no-var
73-
} catch(err) {
74-
// @ts-expect-error TS2345
75-
if(err.code !== "ENOENT") {
76-
throw err;
35+
try {
36+
content = await readFile(sourcePath);
37+
} catch(err) {
38+
// @ts-expect-error TS2345
39+
if(err.code !== "ENOENT") {
40+
throw err;
41+
}
42+
console.error(`WARNING: \`${sourcePath}\` no longer exists`);
43+
return;
7744
}
78-
console.error(`WARNING: \`${sourcePath}\` no longer exists`);
79-
return;
80-
}
8145

82-
let fileExtension = path.extname(sourcePath).substr(1).toLowerCase();
83-
if(fileExtension && compactors[fileExtension]) {
84-
let compactor = compactors[fileExtension];
85-
content = await compactor(content);
86-
}
46+
let fileExtension = path.extname(sourcePath).substr(1).toLowerCase();
47+
if(fileExtension && compactors[fileExtension]) {
48+
let compactor = compactors[fileExtension];
49+
content = await compactor(content);
50+
}
8751

88-
/** @type WriteFileOpts */
89-
let options = { targetDir };
90-
if(fingerprint !== undefined) {
91-
options.fingerprint = fingerprint;
92-
}
93-
return assetManager.writeFile(targetPath, content, options);
52+
/** @type WriteFileOpts */
53+
let options = { targetDir };
54+
if(copyConfig.fingerprint !== undefined) {
55+
options.fingerprint = copyConfig.fingerprint;
56+
}
57+
return assetManager.writeFile(targetPath, content, options);
58+
};
9459
}
9560

9661
/** @import {
9762
* Config,
98-
* AssetManager,
9963
* FaucetPlugin,
10064
* FaucetPluginOptions,
101-
* FaucetPluginFunc,
102-
* CompactorMap,
10365
* WriteFileOpts,
10466
* ProcessFile
10567
* } from "./types.ts"

lib/types.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export interface Config {
3535
fingerprint?: boolean,
3636
compact?: CompactorMap,
3737
assetManager: AssetManager,
38-
filter?: (filename: string) => boolean
38+
filter?: Filter
3939
}
4040

4141
export interface CompactorMap {
@@ -47,16 +47,21 @@ export interface Compactor {
4747
}
4848

4949
export interface ProcessFile {
50+
(filename: string, opts: ProcessFileOptions): Promise<unknown>
51+
}
52+
53+
export interface ProcessFileOptions {
5054
source: string,
5155
target: string,
5256
targetDir: string,
53-
fingerprint?: boolean,
54-
compactors: CompactorMap,
5557
assetManager: AssetManager,
56-
filter?: (filename: string) => boolean
5758
}
5859

5960
export interface FileFinderOptions {
6061
skipDotfiles: boolean,
61-
filter?: (filename: string) => boolean
62+
filter?: Filter
63+
}
64+
65+
export interface Filter {
66+
(filename: string): boolean
6267
}

lib/util.js

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,52 @@
11
import { readdir, stat } from "node:fs/promises";
22
import * as path from "node:path";
33

4-
export class FileFinder {
4+
/**
5+
* Creates a processor for a single configuration
6+
*
7+
* @param {string} source - the source folder or file for this pipeline
8+
* @param {string} target - the target folder or file for this pipeline
9+
* @param {ProcessFile} processFile - process a single file
10+
* @param {AssetManager} assetManager
11+
* @param {Filter} [filter] - optional filter based on filenames
12+
* @returns {FaucetPluginFunc}
13+
*/
14+
export function buildProcessPipeline(source, target, processFile, assetManager, filter) {
15+
source = assetManager.resolvePath(source);
16+
target = assetManager.resolvePath(target, {
17+
enforceRelative: true
18+
});
19+
let fileFinder = new FileFinder(source, {
20+
skipDotfiles: true,
21+
filter
22+
});
23+
24+
return async filepaths => {
25+
let [filenames, targetDir] = await Promise.all([
26+
(filepaths ? fileFinder.match(filepaths) : fileFinder.all()),
27+
determineTargetDir(source, target)
28+
]);
29+
30+
return Promise.all(filenames.map(filename => processFile(filename, {
31+
assetManager, source, target, targetDir
32+
})));
33+
};
34+
}
35+
36+
/**
37+
* If `source` is a directory, `target` is used as target directory -
38+
* otherwise, `target`'s parent directory is used
39+
*
40+
* @param {string} source
41+
* @param {string} target
42+
* @returns {Promise<string>}
43+
*/
44+
async function determineTargetDir(source, target) {
45+
let results = await stat(source);
46+
return results.isDirectory() ? target : path.dirname(target);
47+
}
48+
49+
class FileFinder {
550
/**
651
* @param {string} root
752
* @param {FileFinderOptions} options
@@ -22,7 +67,7 @@ export class FileFinder {
2267
}
2368

2469
/**
25-
* a list of relative file paths within the respective directory
70+
* A list of relative file paths within the respective directory
2671
*
2772
* @returns {Promise<string[]>}
2873
*/
@@ -32,7 +77,7 @@ export class FileFinder {
3277
}
3378

3479
/**
35-
* all file paths that match the filter function
80+
* All file paths that match the filter function
3681
*
3782
* @param {string[]} filepaths
3883
* @returns {Promise<string[]>}
@@ -45,7 +90,7 @@ export class FileFinder {
4590
}
4691

4792
/**
48-
* flat list of all files of a directory tree
93+
* Flat list of all files of a directory tree
4994
*
5095
* @param {string} filepath
5196
* @param {string} referenceDir
@@ -64,4 +109,11 @@ async function tree(filepath, referenceDir = filepath) {
64109
return entries.flat();
65110
}
66111

67-
/** @import { FileFinderOptions } from "./types.ts" */
112+
/** @import {
113+
* AssetManager,
114+
* FaucetPluginFunc,
115+
* Filter,
116+
* FileFinderOptions,
117+
* ProcessFile
118+
* } from "./types.ts"
119+
**/

0 commit comments

Comments
 (0)