Files
overleaf-cep/services/web/app/src/Features/Metadata/MetaHandler.js
T
Alf Eaton a73853711d Promisify Metadata feature (#19361)
GitOrigin-RevId: 962aa9dbbc41a49c2c3120af9a1254a4db85387b
2024-07-24 08:05:19 +00:00

128 lines
3.0 KiB
JavaScript

const ProjectEntityHandler = require('../Project/ProjectEntityHandler')
const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
const packageMapping = require('./packageMapping')
const { callbackify } = require('@overleaf/promise-utils')
/** @typedef {{
* labels: string[]
* packages: Record<string, Record<string, any>>,
* packageNames: string[],
* }} DocMeta
*/
/**
* @param {string[]} lines
* @return {Promise<DocMeta>}
*/
async function extractMetaFromDoc(lines) {
/** @type {DocMeta} */
const docMeta = {
labels: [],
packages: {},
packageNames: [],
}
const labelRe = /\\label{(.{0,80}?)}/g
const packageRe = /^\\usepackage(?:\[.{0,80}?])?{(.{0,80}?)}/g
const reqPackageRe = /^\\RequirePackage(?:\[.{0,80}?])?{(.{0,80}?)}/g
for (const rawLine of lines) {
const line = getNonCommentedContent(rawLine)
for (const pkg of lineMatches(labelRe, line)) {
docMeta.labels.push(pkg)
}
for (const pkg of lineMatches(packageRe, line, ',')) {
docMeta.packageNames.push(pkg)
}
for (const pkg of lineMatches(reqPackageRe, line, ',')) {
docMeta.packageNames.push(pkg)
}
}
for (const packageName of docMeta.packageNames) {
if (packageMapping[packageName]) {
docMeta.packages[packageName] = packageMapping[packageName]
}
}
return docMeta
}
/**
*
* @param {RegExp} matchRe
* @param {string} line
* @param {string} [separator]
* @return {Generator<string>}
*/
function* lineMatches(matchRe, line, separator) {
let match
while ((match = matchRe.exec(line))) {
const matched = match[1].trim()
if (matched) {
if (separator) {
const items = matched
.split(',')
.map(item => item.trim())
.filter(Boolean)
for (const item of items) {
yield item
}
} else {
yield matched
}
}
}
}
/**
* @param {Record<{ lines: string[] }, any>} projectDocs
* @return {Promise<{}>}
*/
async function extractMetaFromProjectDocs(projectDocs) {
const projectMeta = {}
for (const doc of Object.values(projectDocs)) {
projectMeta[doc._id] = await extractMetaFromDoc(doc.lines)
}
return projectMeta
}
/**
* Trims comment content from line
* @param {string} rawLine
* @returns {string}
*/
function getNonCommentedContent(rawLine) {
return rawLine.replace(/(^|[^\\])%.*/, '$1')
}
async function getAllMetaForProject(projectId) {
await DocumentUpdaterHandler.promises.flushProjectToMongo(projectId)
const docs = await ProjectEntityHandler.promises.getAllDocs(projectId)
return await extractMetaFromProjectDocs(docs)
}
async function getMetaForDoc(projectId, docId) {
await DocumentUpdaterHandler.promises.flushDocToMongo(projectId, docId)
const { lines } = await ProjectEntityHandler.promises.getDoc(projectId, docId)
return await extractMetaFromDoc(lines)
}
module.exports = {
promises: {
getAllMetaForProject,
getMetaForDoc,
},
getAllMetaForProject: callbackify(getAllMetaForProject),
getMetaForDoc: callbackify(getMetaForDoc),
}