@sap/generator-cap-project
Version:
Creates a new SAP Cloud Application Programming Model project.
160 lines (159 loc) • 5.01 kB
JavaScript
import { EventEmitter } from 'events';
import path from 'path';
import { vinylFileSync } from 'vinyl-file';
import File from 'vinyl';
import { Readable, Transform } from 'stream';
import { pipeline } from 'stream/promises';
import fs from 'node:fs';
export function isFileTransform(transform) {
return (typeof transform === 'function' ||
(typeof transform === 'object' &&
('readable' in transform || 'writable' in transform)));
}
export function loadFile(filepath) {
const stat = fs.statSync(filepath, { throwIfNoEntry: false });
if (stat?.isDirectory?.()) {
return new File({
cwd: process.cwd(),
base: process.cwd(),
path: filepath,
stat,
contents: null,
});
}
try {
return vinylFileSync(filepath);
}
catch {
return new File({
cwd: process.cwd(),
base: process.cwd(),
path: filepath,
contents: null,
});
}
}
export class Store extends EventEmitter {
loadFile;
store = new Map();
constructor(options) {
super();
this.loadFile =
options?.loadFile ?? loadFile;
}
load(filepath) {
const file = this.loadFile(filepath);
this.store.set(filepath, file);
return file;
}
get(filepath) {
filepath = path.resolve(filepath);
return this.store.get(filepath) || this.load(filepath);
}
existsInMemory(filepath) {
filepath = path.resolve(filepath);
return this.store.has(filepath);
}
add(file) {
this.store.set(file.path, file);
this.emit('change', file.path);
return this;
}
each(onEach) {
this.store.forEach((file) => {
onEach(file);
});
return this;
}
all() {
return Array.from(this.store.values());
}
stream({ filter = () => true } = {}) {
function* iterablefilter(iterable) {
for (const item of iterable) {
if (filter(item)) {
yield item;
}
}
}
return Readable.from(iterablefilter(this.store.values()));
}
async pipeline(options, ...transforms) {
let filter;
let resolveConflict;
let refresh = true;
if (isFileTransform(options)) {
transforms = [options, ...transforms];
}
else if (options) {
filter = options.filter;
if (options.refresh !== undefined) {
refresh = options.refresh;
}
if (options.resolveConflict !== undefined) {
resolveConflict = options.resolveConflict;
}
else if (options.allowOverride !== undefined) {
resolveConflict = (_current, newFile) => newFile;
}
}
const newStore = refresh ? new Map() : undefined;
const fileFilter = filter ?? (transforms.length === 0 ? () => false : () => true);
const addFile = newStore
? (file) => {
const currentFile = newStore.get(file.path);
if (currentFile) {
if (!resolveConflict) {
throw new Error(`Duplicated file ${file.path} was emitted.`);
}
file = resolveConflict(currentFile, file);
}
newStore.set(file.path, file);
}
: undefined;
function* iterablefilter(iterable) {
for (const item of iterable) {
if (fileFilter(item)) {
yield item;
}
else {
addFile?.(item);
}
}
}
await pipeline(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Readable.from(iterablefilter(this.store.values())),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...transforms, new Transform({
objectMode: true,
transform(file, _encoding, callback) {
addFile?.(file);
callback(null);
},
}));
if (newStore) {
const oldStore = this.store;
this.store = newStore;
for (const file of this.store.keys()) {
if (oldStore.has(file)) {
const newFile = this.store.get(file);
const oldFile = oldStore.get(file);
oldStore.delete(file);
if (newFile !== oldFile) {
this.emit('change', file);
}
}
else {
this.emit('change', file);
}
}
for (const oldFile of oldStore.keys()) {
this.emit('change', oldFile);
}
}
}
}
export function create() {
return new Store();
}