UNPKG

@solid/community-server

Version:

Community Solid Server: an open and modular implementation of the Solid specifications

131 lines 6.05 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ExtensionBasedMapperFactory = exports.ExtensionBasedMapper = void 0; const node_fs_1 = require("node:fs"); const mime = __importStar(require("mime-types")); const ContentTypes_1 = require("../../util/ContentTypes"); const NotImplementedHttpError_1 = require("../../util/errors/NotImplementedHttpError"); const PathUtil_1 = require("../../util/PathUtil"); const BaseFileIdentifierMapper_1 = require("./BaseFileIdentifierMapper"); /** * Supports the behaviour described in https://www.w3.org/DesignIssues/HTTPFilenameMapping.html * Determines content-type based on the file extension. * In case an identifier does not end on an extension matching its content-type, * the corresponding file will be appended with the correct extension, preceded by $. */ class ExtensionBasedMapper extends BaseFileIdentifierMapper_1.BaseFileIdentifierMapper { customTypes; customExtensions; constructor(base, rootFilepath, customTypes) { super(base, rootFilepath); // Workaround for https://github.com/LinkedSoftwareDependencies/Components.js/issues/20 if (!customTypes || Object.keys(customTypes).length === 0) { this.customTypes = ContentTypes_1.DEFAULT_CUSTOM_TYPES; } else { this.customTypes = customTypes; } this.customExtensions = {}; for (const [extension, contentType] of Object.entries(this.customTypes)) { this.customExtensions[contentType] = extension; } } async mapUrlToDocumentPath(identifier, filePath, contentType) { // Would conflict with how new extensions are stored if (/\$\.\w+$/u.test(filePath)) { this.logger.warn(`Identifier ${identifier.path} contains a dollar sign before its extension`); throw new NotImplementedHttpError_1.NotImplementedHttpError('Identifiers cannot contain a dollar sign before their extension'); } // Existing file if (!contentType) { // Find a matching file const [, folder, documentName] = /^(.*\/)([^/]*)$/u.exec(filePath); let fileName; try { const files = await node_fs_1.promises.readdir(folder); fileName = files.find((file) => file.startsWith(documentName) && /^(?:\$\..+)?$/u.test(file.slice(documentName.length))); } catch { // Parent folder does not exist (or is not a folder) } if (fileName) { filePath = (0, PathUtil_1.joinFilePath)(folder, fileName); } contentType = await this.getContentTypeFromPath(filePath); // If the extension of the identifier matches a different content-type than the one that is given, // we need to add a new extension to match the correct type. } else if (contentType !== await this.getContentTypeFromPath(filePath)) { let extension = mime.extension(contentType) || this.customExtensions[contentType]; if (!extension) { // When no extension is found for the provided content-type, use a fallback extension. extension = this.unknownMediaTypeExtension; // Signal the fallback by setting the content-type to undefined in the output link. contentType = undefined; } filePath += `$.${extension}`; } return super.mapUrlToDocumentPath(identifier, filePath, contentType); } async getDocumentUrl(relative) { return super.getDocumentUrl(this.stripExtension(relative)); } async getContentTypeFromPath(filePath) { const extension = (0, PathUtil_1.getExtension)(filePath).toLowerCase(); return mime.lookup(extension) || this.customTypes[extension] || await super.getContentTypeFromPath(filePath); } /** * Helper function that removes the internal extension, one starting with $., from the given path. * Nothing happens if no such extension is present. */ stripExtension(path) { const extension = (0, PathUtil_1.getExtension)(path); if (extension && path.endsWith(`$.${extension}`)) { path = path.slice(0, -(extension.length + 2)); } return path; } } exports.ExtensionBasedMapper = ExtensionBasedMapper; class ExtensionBasedMapperFactory { async create(base, rootFilePath) { return new ExtensionBasedMapper(base, rootFilePath); } } exports.ExtensionBasedMapperFactory = ExtensionBasedMapperFactory; //# sourceMappingURL=ExtensionBasedMapper.js.map