UNPKG

@sap/generator-cap-project

Version:

Creates a new SAP Cloud Application Programming Model project.

160 lines (159 loc) 5.01 kB
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(); }