Skip to content

Commit

Permalink
feat: calculate template pages dir depth and track
Browse files Browse the repository at this point in the history
everything with one watcher
  • Loading branch information
phukon committed Apr 13, 2024
1 parent 06a9e5f commit 56e04e9
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 116 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fs from 'fs';
import path from 'path';
/**
* Recursively finds template pages in a given directory and saves their paths to a set.
* @param {string} directoryPath
* @returns {Promise<Set<string>>} Set containing paths to template pages
*/
export const findTemplatePagesPaths = async (directoryPath) => {
const templatePagePaths = new Set();
const files = await fs.promises.readdir(directoryPath);

for (const file of files) {
const filePath = path.join(directoryPath, file);
const fileStat = await fs.promises.stat(filePath);

if (fileStat.isDirectory()) {
const nestedTemplatePagePaths = await findTemplatePagesPaths(filePath);
nestedTemplatePagePaths.forEach((absolutePath) => templatePagePaths.add(absolutePath));
} else if (file.match(/^\[(.*?)\]\.md$/)) {
templatePagePaths.add(filePath);
}
}

return templatePagePaths;
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import { getPagesDir } from './get-pages.js';
import { findTemplatePagesPaths } from './findTemplatePagesPaths.js';
import { watchDirectory } from './watcher.js';

/** @type {import("vite").Plugin} */
/**
* Crawls the pages dir and checks for the total no. of md files.
* Then crawls the built pages in .evidence and checks for the dirs
* Compares and shows percentage complete logs.
* @type {import("vite").Plugin}
*/
export const verboseLogs = {
name: 'evidence:verbose-logs',

buildStart() {
const directoryPath = '.svelte-kit/output/prerendered/pages';
getPagesDir('../../pages')
.then((totalExpectedFiles) => {
console.log('Build started');
watchDirectory(
directoryPath,
totalExpectedFiles,
(/** @type {string} */ progress) => {
console.log(`Build Progress: ${progress}%`);
},
() => {
console.log('Build completed.');
}
);
findTemplatePagesPaths('../../pages')
.then((dirs) => {
watchDirectory(directoryPath, dirs);
})
.catch((error) => {
console.error('Error:', error);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
import fs from 'fs/promises'
import fs from 'fs/promises';

/**
* Checks if a directory exists.
* @param {string} directoryPath
* @returns {Promise<boolean>}
*/
async function directoryExists(directoryPath) {
try {
const stats = await fs.stat(directoryPath);
return stats.isDirectory();
} catch (error) {
// @ts-ignore
if (error.code === 'ENOENT') {
return false;
}
throw error;
}
}

/**
* Waits for the specified directory to be created.
Expand All @@ -7,23 +25,20 @@ import fs from 'fs/promises'
* @param {number} intervalMs
* @returns {Promise<void>}
*/
export async function waitForDirectoryCreation(directoryPath, maxAttempts = 30, intervalMs = 3000) {
let attempts = 0;
while (attempts < maxAttempts) {
try {
const dirExists = await (await fs.stat(directoryPath)).isDirectory()
if (dirExists) {
console.log(`Directory ${directoryPath} exists.`);
return;
} else {
console.log("dir not found")
}
} catch (error) {
console.log("waiting for the dir to be created")
}
attempts++;
await new Promise(resolve => setTimeout(resolve, intervalMs));
}
export async function waitForDirectoryCreation(directoryPath, maxAttempts = 60, intervalMs = 3000) {
let attempts = 0;
while (attempts < maxAttempts) {
const dirExists = await directoryExists(directoryPath);
if (dirExists) {
// console.log(`\nMonitoring pages build progress.`);
return;
}
// } else {
// console.log('\nDirectory not found, retrying...');
// }
attempts++;
await new Promise((resolve) => setTimeout(resolve, intervalMs));
}

throw new Error(`Directory ${directoryPath} not created within the specified time.`);
}
throw new Error(`\nDirectory ${directoryPath} not created within the specified time.`);
}
151 changes: 113 additions & 38 deletions packages/lib/plugin-connector/src/build-plugins/verbose-logs/watcher.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,120 @@
import path from 'path';
import chokidar from 'chokidar';
import { countFiles } from './fileCounter.js';
import { calculateProgress } from './progressCalculator.js';
import { waitForDirectoryCreation } from './wait-creation.js';

/**
* Calculates the depth of a directory path
* @param {string} dirPath - The directory path
* @returns {number}
*/
function calculateDepth(dirPath) {
const segments = dirPath.split(path.sep).filter((segment) => segment !== '');
if (segments.length === 0) {
return 0;
}
return segments.length - 1;
}

/**
* Calculate the max depth among an array of directory paths
* @param {string[]} dirPaths
* @returns {number} - The highest depth
*/
function getMaxDepth(dirPaths) {
let maxDepth = -1;

for (const dirPath of dirPaths) {
const depth = calculateDepth(dirPath);
if (depth > maxDepth) {
maxDepth = depth;
}
}

return maxDepth;
}

/**
* Watches a directory and calculates the progress of files being added.
* @param {string} directoryPath
* @param {number} totalFiles
* @param {Function} onProgressUpdate
* @param {Function} onStopCallback
* @param {string} directoryPath - The path to the directory to watch.
* @param {Set<string>} dirs - The set of directories to watch.
*/
export async function watchDirectory(directoryPath, totalFiles, onProgressUpdate, onStopCallback) {
await waitForDirectoryCreation(directoryPath);

const currentFiles = await countFiles(directoryPath);
const initialProgress = calculateProgress(currentFiles, totalFiles);

if (initialProgress >= 100) {
console.log('Progress already at 100% or greater. Exiting watcher.');
if (typeof onStopCallback === 'function') {
onStopCallback();
}
return;
}

const watcher = chokidar.watch(directoryPath, { ignoreInitial: true });

async function updateProgress() {
const currentFiles = await countFiles(directoryPath);
const progress = calculateProgress(currentFiles, totalFiles);
onProgressUpdate(progress);

if (progress >= 100) {
watcher.close();

if (typeof onStopCallback === 'function') {
onStopCallback();
}
}
}

watcher.on('add', updateProgress);
watcher.on('addDir', updateProgress);
export async function watchDirectory(directoryPath, dirs) {
/**
* @type {string | number | NodeJS.Timeout | undefined}
*/
let timeout;
/**
* @type {string | number | NodeJS.Timeout | undefined}
*/
let masterTimeout;

/**
* @type {number}
*/
let totalCount = 0;

/**
* @type {chokidar.FSWatcher | undefined}
*/
let watcher;

let done = false;
await waitForDirectoryCreation(directoryPath);
try {
const pathArrays = Array.from(dirs).map((p) => {
const pathArray = p.split(path.sep);
pathArray.pop(); // removing the template page name
return pathArray.slice(3); // removing target dir top paths
});
const joinedPaths = pathArrays.map((p) => path.sep + path.join(...p));
const depth = getMaxDepth(joinedPaths);
watcher = chokidar.watch(directoryPath, { ignoreInitial: true, depth: depth + 1 });
/**
* Resets the timeout to 15 seconds.
* The timer will only start after the target directory has been
* created, therefore the process will not be killed prematurely.
*/
const resetTimeout = () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
watcher && watcher.close();
done = true;
}, 10_000);
};

/**
* Resets the master timeout to ensure the plugin's timeout mechanism functions as expected.
*
* Sometimes, plugins may run again, after the pages have been built.
* If this happens, the watcher may not register any changes, and the "done" state will never be true,
* even after the pages have been built. This function is called as a redundancy measure to return control
* and not be stuck forever.
*/
const resetMasterTimeout = () => {
clearTimeout(masterTimeout);
masterTimeout = setTimeout(() => {
watcher && watcher.close();
done = true;
}, 65_000);
};

resetMasterTimeout();
watcher.on('addDir', async () => {
resetTimeout();
resetMasterTimeout();
totalCount++;
console.clear(); // to clear the terminal and not fill the terminal with too many logs
console.log('\u001b[1m\u001b[34m Building template pages: %d+\u001b[0m', totalCount);
console.log('Please wait...');

if (done && watcher) {
watcher.close();
console.log('closing watcher', done);
return;
}
});
} catch (err) {
console.error('Error:', err);
return;
}
}

0 comments on commit 56e04e9

Please sign in to comment.