UNPKG

@stackbit/sdk

Version:
278 lines 12 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileBrowser = exports.getFileBrowserFromOptions = exports.GitHubFileBrowserAdapter = exports.FileSystemFileBrowserAdapter = exports.EXCLUDED_LIST_FILES = void 0; const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); const lodash_1 = __importDefault(require("lodash")); const micromatch_1 = __importDefault(require("micromatch")); const rest_1 = require("@octokit/rest"); const utils_1 = require("@stackbit/utils"); const consts_1 = require("../consts"); exports.EXCLUDED_LIST_FILES = ['**/node_modules/**', '**/.git/**', '.idea/**']; class FileSystemFileBrowserAdapter { constructor({ dirPath }) { this.dirPath = dirPath; } async listFiles({ includePattern, excludePattern }) { const readDirResult = await (0, utils_1.readDirRecursively)(this.dirPath, { includeDirs: true, includeStats: true, filter: (filePath) => { const isIncluded = !includePattern || micromatch_1.default.isMatch(filePath, includePattern); excludePattern = excludePattern || exports.EXCLUDED_LIST_FILES; const isExcluded = micromatch_1.default.isMatch(filePath, excludePattern); return isIncluded && !isExcluded; } }); // TODO: order files alphabetically so both Git and FileSystem adapters will result same analyze results return readDirResult.map((fileResult) => ({ filePath: fileResult.filePath, isFile: fileResult.stats.isFile(), isDirectory: fileResult.stats.isDirectory() })); } async readFile(filePath) { const absPath = path_1.default.join(this.dirPath, filePath); return fs_extra_1.default.readFile(absPath, 'utf8'); } } exports.FileSystemFileBrowserAdapter = FileSystemFileBrowserAdapter; class GitHubFileBrowserAdapter { constructor(options) { if ('repoUrl' in options) { const parsedRepoUrl = this.parseGitHubUrl(options.repoUrl); if (!parsedRepoUrl) { throw new Error(`repository URL '${options.repoUrl}' cannot be parsed, please use standard github URL`); } this.owner = parsedRepoUrl.owner; this.repo = parsedRepoUrl.repo; } else { this.owner = options.owner; this.repo = options.repo; } this.branch = options.branch; this.octokit = new rest_1.Octokit({ auth: options.auth }); } async listFiles(listFilesOptions) { // const branchResponse = await this.octokit.repos.getBranch({ // owner: this.owner, // repo: this.repo, // branch: this.branch // }); // const treeSha = branchResponse.data.commit.commit.tree.sha; const branchResponse = await this.octokit.repos.listBranches({ owner: this.owner, repo: this.repo }); const branch = lodash_1.default.find(branchResponse.data, { name: this.branch }); if (!branch) { throw new Error(`branch ${this.branch} not found`); } const treeSha = branch.commit.sha; const treeResponse = await this.octokit.git.getTree({ owner: this.owner, repo: this.repo, tree_sha: treeSha, recursive: 'true' }); let tree; if (!treeResponse.data.truncated) { const { includePattern, excludePattern } = listFilesOptions; tree = treeResponse.data.tree; tree = tree.filter((node) => { if (!node.path || !(node.type === 'blob' || node.type === 'tree')) { return false; } const isIncluded = !includePattern || micromatch_1.default.isMatch(node.path, includePattern); const isExcluded = micromatch_1.default.isMatch(node.path, excludePattern || exports.EXCLUDED_LIST_FILES); return isIncluded && !isExcluded; }); } else { tree = await this.listFilesRecursively(treeResponse.data.sha, listFilesOptions, ''); } // TODO: order files alphabetically so both Git and FileSystem adapters will result same analyze results return tree.map((node) => ({ filePath: node.path, isFile: node.type === 'blob', isDirectory: node.type === 'tree' })); } async listFilesRecursively(treeSha, listFilesOptions, parentPath) { const treeResponse = await this.octokit.git.getTree({ owner: this.owner, repo: this.repo, tree_sha: treeSha }); const { includePattern, excludePattern } = listFilesOptions; const { blob: files, tree: folders } = lodash_1.default.groupBy(treeResponse.data.tree, 'type'); const filter = (fullPath) => { const isIncluded = !includePattern || micromatch_1.default.isMatch(fullPath, includePattern); const isExcluded = micromatch_1.default.isMatch(fullPath, excludePattern || exports.EXCLUDED_LIST_FILES); return isIncluded && !isExcluded; }; const filteredFolders = (folders || []).reduce((accum, treeNode) => { if (!treeNode.path || !treeNode.sha) { return accum; } const fullPath = (parentPath ? parentPath + '/' : '') + treeNode.path; if (!filter(fullPath)) { return accum; } return accum.concat(Object.assign(treeNode, { path: fullPath })); }, []); const filteredFiles = (files || []).reduce((accum, fileNode) => { if (!fileNode.path) { return accum; } const fullPath = (parentPath ? parentPath + '/' : '') + fileNode.path; if (!filter(fullPath)) { return accum; } return accum.concat(Object.assign(fileNode, { path: fullPath })); }, []); const folderResults = await (0, utils_1.reducePromise)(filteredFolders, async (accum, treeNode) => { const results = await this.listFilesRecursively(treeNode.sha, listFilesOptions, treeNode.path); return accum.concat(treeNode, results); }, []); return folderResults.concat(filteredFiles); } async readFile(filePath) { const contentResponse = await this.octokit.repos.getContent({ owner: this.owner, repo: this.repo, path: filePath }); if ('content' in contentResponse.data) { const base64Content = contentResponse.data.content; return Buffer.from(base64Content, 'base64').toString(); } return ''; } parseGitHubUrl(repoUrl) { const match = repoUrl.match(/github\.com[/:](.+?)\/(.+?)(\.git)?$/); if (!match) { return null; } const owner = match[1]; const repo = match[2]; return { owner, repo }; } } exports.GitHubFileBrowserAdapter = GitHubFileBrowserAdapter; function getFileBrowserFromOptions(options) { if ('fileBrowser' in options) { return options.fileBrowser; } if (!('fileBrowserAdapter' in options)) { throw new Error('either fileBrowser or fileBrowserAdapter must be provided to SSG matcher'); } return new FileBrowser({ fileBrowserAdapter: options.fileBrowserAdapter }); } exports.getFileBrowserFromOptions = getFileBrowserFromOptions; class FileBrowser { constructor({ fileBrowserAdapter }) { this.files = []; this.fileBrowserAdapter = fileBrowserAdapter; this.filePaths = []; this.filePathMap = {}; this.directoryPathsMap = {}; this.filePathsByFileName = {}; this.fileTree = {}; this.fileData = {}; } async listFiles({ includePattern, excludePattern } = {}) { if (this.files.length > 0) { return; } this.files = await this.fileBrowserAdapter.listFiles({ includePattern, excludePattern }); // create maps to find files by names or paths quickly lodash_1.default.forEach(this.files, (fileReadResult) => { const filePath = fileReadResult.filePath; const filePathArr = filePath.split(path_1.default.sep); if (fileReadResult.isFile) { lodash_1.default.set(this.fileTree, filePathArr, true); const pathObject = path_1.default.parse(filePath); const fileName = pathObject.base; if (!(fileName in this.filePathsByFileName)) { this.filePathsByFileName[fileName] = []; } this.filePathsByFileName[fileName].push(filePath); this.filePaths.push(filePath); this.filePathMap[filePath] = true; } else if (fileReadResult.isDirectory) { if (!lodash_1.default.has(this.fileTree, filePathArr)) { lodash_1.default.set(this.fileTree, filePathArr, {}); } this.directoryPathsMap[filePath] = true; } }); } filePathExists(filePath) { return lodash_1.default.has(this.filePathMap, filePath); } fileNameExists(fileName) { return lodash_1.default.has(this.filePathsByFileName, fileName); } getFilePathsForFileName(fileName) { return lodash_1.default.get(this.filePathsByFileName, fileName, []); } directoryPathExists(dirPath) { return lodash_1.default.has(this.directoryPathsMap, dirPath); } findFiles(pattern) { return (0, micromatch_1.default)(this.filePaths, pattern); } readFilesRecursively(dirPath, { filter, includeDirs }) { const reduceTreeNode = (treeNode, parentPath) => { return lodash_1.default.reduce(treeNode, (result, value, name) => { const filePath = path_1.default.join(parentPath, name); const isFile = value === true; if (filter && !filter({ filePath, isFile: isFile, isDirectory: !isFile })) { return result; } if (value !== true) { const childFilePaths = reduceTreeNode(value, filePath); result = includeDirs ? result.concat(filePath, childFilePaths) : result.concat(childFilePaths); } else { result = result.concat(filePath); } return result; }, []); }; const treeNode = dirPath === '' ? this.fileTree : lodash_1.default.get(this.fileTree, dirPath.split(path_1.default.sep)); return reduceTreeNode(treeNode, ''); } async getFileData(filePath) { if (!this.filePathExists(filePath)) { return null; } if (!(filePath in this.fileData)) { const extension = path_1.default.extname(filePath).substring(1); const data = await this.fileBrowserAdapter.readFile(filePath); if ([...consts_1.DATA_FILE_EXTENSIONS, ...consts_1.MARKDOWN_FILE_EXTENSIONS].includes(extension)) { try { this.fileData[filePath] = (0, utils_1.parseDataByFilePath)(data, filePath); } catch (error) { console.warn(`error parsing file: ${filePath}`); this.fileData[filePath] = null; } } else { this.fileData[filePath] = data; } } return this.fileData[filePath]; } } exports.FileBrowser = FileBrowser; //# sourceMappingURL=file-browser.js.map