// @ts-check import { readFileSync } from 'fs'; import { globSync } from 'glob'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; import { NOTEBOOKS_DIRECTORY_PATH } from './notebook-content-reader.js'; import { NotebookMetadataCollector } from './notebook-metadata-collector.js'; import { toMarkdown } from './notebook-metadata-formatter.js'; import { NotebookMetadataValidationError, validateNotebookMetadata } from './notebook-metadata-validator.js'; /** @typedef {import('../shared/notebook-metadata.ts').INotebookMetadata} INotebookMetadata */ export class NotebookMetadataHandler { /** * @param {string} notebookFilePath */ constructor(notebookFilePath) { /** @private */ this._notebookFilePath = notebookFilePath; /** @private */ this._metadata = new NotebookMetadataCollector(this._notebookFilePath).getMetadata(); } /** @private @const */ static _skippedNotebooks = this._getSkippedNotebooks(); get metadata() { return this._metadata; } /** * @returns {string | null} Validation error */ validateMetadata() { try { validateNotebookMetadata(this._metadata); } catch (error) { if (error instanceof NotebookMetadataValidationError) { error.message = `Invalid metadata for notebook "${this._notebookFilePath}".\n${error.message}`; return error.message; } throw error; } return null; } /** * @param {boolean} hasError */ toMarkdown(hasError) { return toMarkdown(this._metadata, hasError); } static getNotebooksPaths() { return globSync('**/*.ipynb', { ignore: ['**/.ipynb_checkpoints/*', '**/notebook_utils.ipynb', ...this._skippedNotebooks], cwd: NOTEBOOKS_DIRECTORY_PATH, }); } /** * @param {string[]} notebooksPaths * @return {[string | null, string[]]} Tuple of validation error (if exist) and array of metadata markdown representations */ static validateNotebooks(notebooksPaths) { const errors = []; const metadataMarkdowns = []; for (const notebookPath of notebooksPaths) { if (NotebookMetadataHandler._skippedNotebooks.includes(notebookPath)) { continue; } const notebookMetadataHandler = new NotebookMetadataHandler(notebookPath); const error = notebookMetadataHandler.validateMetadata(); if (error) { errors.push(error); } const metadataMarkdown = notebookMetadataHandler.toMarkdown(Boolean(error)); metadataMarkdowns.push(metadataMarkdown); } return [errors.length ? errors.join('\n\n') : null, metadataMarkdowns]; } static validateAll() { const notebooksPaths = NotebookMetadataHandler.getNotebooksPaths(); return NotebookMetadataHandler.validateNotebooks(notebooksPaths); } /** @private */ static _getSkippedNotebooks() { const currentDirectory = dirname(fileURLToPath(import.meta.url)); const skippedNotebooksFilePath = join(currentDirectory, 'skipped-notebooks.txt'); const skippedNotebooksFileContent = readFileSync(skippedNotebooksFilePath, { encoding: 'utf8' }); return skippedNotebooksFileContent .trim() .split('\n') .map((v) => v.trim()); } }