123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- const fs = require('fs')
- const path = require('path')
- // Cache for fs.lstatSync lookup.
- // Prevent multiple blocking IO requests that have already been calculated.
- const fsLstatSyncCache = {}
- const fsLstatSync = (source) => {
- fsLstatSyncCache[source] = fsLstatSyncCache[source] || fs.lstatSync(source)
- return fsLstatSyncCache[source]
- }
- /**
- * Checks if the source is a directory.
- * @param {string} source
- */
- function isDirectory(source) {
- return fsLstatSync(source).isDirectory()
- }
- /**
- * Checks if the source is a directory.
- * @param {string} source
- */
- function isSymlink(source) {
- return fsLstatSync(source).isSymbolicLink()
- }
- /**
- * Gets the possible URLs from a directory.
- * @param {string} urlprefix
- * @param {string[]} directories
- */
- function getUrlFromPagesDirectories(urlPrefix, directories) {
- return Array.from(
- // De-duplicate similar pages across multiple directories.
- new Set(
- directories
- .map((directory) => parseUrlForPages(urlPrefix, directory))
- .flat()
- .map(
- // Since the URLs are normalized we add `^` and `$` to the RegExp to make sure they match exactly.
- (url) => `^${normalizeURL(url)}$`
- )
- )
- ).map((urlReg) => {
- urlReg = urlReg.replace(/\[.*\]/g, '((?!.+?\\..+?).*?)')
- return new RegExp(urlReg)
- })
- }
- // Cache for fs.readdirSync lookup.
- // Prevent multiple blocking IO requests that have already been calculated.
- const fsReadDirSyncCache = {}
- /**
- * Recursively parse directory for page URLs.
- * @param {string} urlprefix
- * @param {string} directory
- */
- function parseUrlForPages(urlprefix, directory) {
- fsReadDirSyncCache[directory] =
- fsReadDirSyncCache[directory] || fs.readdirSync(directory)
- const res = []
- fsReadDirSyncCache[directory].forEach((fname) => {
- // TODO: this should account for all page extensions
- // not just js(x) and ts(x)
- if (/(\.(j|t)sx?)$/.test(fname)) {
- if (/^index(\.(j|t)sx?)$/.test(fname)) {
- res.push(`${urlprefix}${fname.replace(/^index(\.(j|t)sx?)$/, '')}`)
- }
- res.push(`${urlprefix}${fname.replace(/(\.(j|t)sx?)$/, '')}`)
- } else {
- const dirPath = path.join(directory, fname)
- if (isDirectory(dirPath) && !isSymlink(dirPath)) {
- res.push(...parseUrlForPages(urlprefix + fname + '/', dirPath))
- }
- }
- })
- return res
- }
- /**
- * Takes a URL and does the following things.
- * - Replaces `index.html` with `/`
- * - Makes sure all URLs are have a trailing `/`
- * - Removes query string
- * @param {string} url
- */
- function normalizeURL(url) {
- if (!url) {
- return
- }
- url = url.split('?')[0]
- url = url.split('#')[0]
- url = url = url.replace(/(\/index\.html)$/, '/')
- // Empty URLs should not be trailed with `/`, e.g. `#heading`
- if (url === '') {
- return url
- }
- url = url.endsWith('/') ? url : url + '/'
- return url
- }
- function execOnce(fn) {
- let used = false
- let result
- return (...args) => {
- if (!used) {
- used = true
- result = fn(...args)
- }
- return result
- }
- }
- module.exports = {
- getUrlFromPagesDirectories,
- normalizeURL,
- execOnce,
- }
|