diff --git a/src/PipelineState.d.ts b/src/PipelineState.d.ts
index 5c5e6caa..47691575 100644
--- a/src/PipelineState.d.ts
+++ b/src/PipelineState.d.ts
@@ -11,7 +11,7 @@
*/
import {PathInfo, S3Loader, PipelineTimer } from "./index";
import {PipelineContent} from "./PipelineContent";
-import {PipelineSiteConfig} from "./site-config";
+import {ModifiersSheet, PipelineSiteConfig} from "./site-config";
declare enum PipelineType {
html = 'html',
@@ -117,5 +117,22 @@ declare class PipelineState {
*/
liveHost: string;
+ /**
+ * specifies if the content as folder mapped (note that it remains false for content that
+ * exists below a folder mapped path. in this case, the `mappedPath` would still be different
+ * from the `info.path`
+ */
+ mapped: boolean;
+
+ /**
+ * the mapped path (target) of a folder mapping. this is set irrespective of the existence of the
+ * resource, when the path is below a folder mapped path
+ */
+ mappedPath: string;
+
+ /**
+ * metadata from folder mapping
+ */
+ mappedMetadata: Modifiers
}
diff --git a/src/html-pipe.js b/src/html-pipe.js
index f6afb926..4ddee834 100644
--- a/src/html-pipe.js
+++ b/src/html-pipe.js
@@ -18,7 +18,7 @@ import fetchContent from './steps/fetch-content.js';
import fetch404 from './steps/fetch-404.js';
import initConfig from './steps/init-config.js';
import fixSections from './steps/fix-sections.js';
-import folderMapping from './steps/folder-mapping.js';
+import { calculateFolderMapping, applyFolderMapping } from './steps/folder-mapping.js';
import getMetadata from './steps/get-metadata.js';
import html from './steps/make-html.js';
import parseMarkdown from './steps/parse-markdown.js';
@@ -110,7 +110,7 @@ export async function htmlPipe(state, req) {
state.content.sourceBus = 'code';
}
- // ...and apply the folder mapping
+ calculateFolderMapping(state);
state.timer?.update('content-fetch');
let contentPromise = await fetchContent(state, req, res);
if (res.status === 404) {
@@ -119,7 +119,7 @@ export async function htmlPipe(state, req) {
contentPromise = fetchContentRedirectWith404Fallback(state, req, res);
} else {
// ...apply folder mapping if the current resource doesn't exist
- await folderMapping(state);
+ applyFolderMapping(state);
if (state.info.unmappedPath) {
contentPromise = fetchContentWith404Fallback(state, req, res);
} else {
diff --git a/src/steps/extract-metadata.js b/src/steps/extract-metadata.js
index 5be526d7..2e8c2651 100644
--- a/src/steps/extract-metadata.js
+++ b/src/steps/extract-metadata.js
@@ -174,7 +174,7 @@ export default function extractMetaData(state, req) {
// with local metadata from document
const metaConfig = Object.assign(
state.metadata.getModifiers(state.info.unmappedPath || state.info.path),
- state.mappedMetadata.getModifiers(state.info.unmappedPath),
+ state.mappedMetadata.getModifiers(state.info.unmappedPath || state.info.path),
);
// prune empty values and explicit "" strings from sheet based metadata
diff --git a/src/steps/fetch-mapped-metadata.js b/src/steps/fetch-mapped-metadata.js
index a3704e0f..97d4ce50 100644
--- a/src/steps/fetch-mapped-metadata.js
+++ b/src/steps/fetch-mapped-metadata.js
@@ -26,11 +26,12 @@ import { Modifiers } from '../utils/modifiers.js';
// eslint-disable-next-line no-unused-vars
export default async function fetchMappedMetadata(state, res) {
state.mappedMetadata = Modifiers.EMPTY;
- if (!state.mapped) {
+ const { mappedPath } = state;
+ if (!mappedPath) {
return;
}
const { contentBusId, partition } = state;
- const metadataPath = `${state.info.path}/metadata.json`;
+ const metadataPath = `${mappedPath}/metadata.json`;
const key = `${contentBusId}/${partition}${metadataPath}`;
const ret = await state.s3Loader.getObject('helix-content-bus', key);
if (ret.status === 200) {
diff --git a/src/steps/folder-mapping.js b/src/steps/folder-mapping.js
index 57d9286b..8c4c8adc 100644
--- a/src/steps/folder-mapping.js
+++ b/src/steps/folder-mapping.js
@@ -33,26 +33,35 @@ export function mapPath(folders, path) {
}
/**
- * Checks the fstab for folder mapping entries and then re-adjusts the path infos if needed.
- * if the remapped resource is *not* extensionless, it will be declared as code-bus resource.
+ * Checks if the resource path is below a folder-mapped configuration and updates `state.mappedPath`
+ * accordingly.
*
- * @type PipelineStep
- * @param {PipelineState} state
+ * @param state
*/
-export default function folderMapping(state) {
+export function calculateFolderMapping(state) {
const { folders } = state.config;
if (!folders) {
return;
}
const { path } = state.info;
- const mapped = mapPath(folders, path);
- if (mapped) {
- state.info = getPathInfo(mapped);
+ state.mappedPath = mapPath(folders, path);
+}
+
+/**
+ * Applies folder mapping if the resource is mapped (i.e. if `state.mappedPath` is {@code true}.
+ *
+ * @type PipelineStep
+ * @param {PipelineState} state
+ */
+export function applyFolderMapping(state) {
+ const { info: { path }, mappedPath } = state;
+ if (mappedPath) {
+ state.info = getPathInfo(mappedPath);
state.info.unmappedPath = path;
- if (getExtension(mapped)) {
+ if (getExtension(mappedPath)) {
// special case: use code-bus
state.content.sourceBus = 'code';
- state.info.resourcePath = mapped;
+ state.info.resourcePath = mappedPath;
state.log.info(`mapped ${path} to ${state.info.resourcePath} (code-bus)`);
} else {
state.mapped = true;
diff --git a/test/rendering.test.js b/test/rendering.test.js
index 7f8014ed..d1218e8e 100644
--- a/test/rendering.test.js
+++ b/test/rendering.test.js
@@ -643,6 +643,16 @@ describe('Rendering', () => {
loader.status('product1.md', 200);
resp = await render(new URL('https://helix-pipeline.com/products/product1'), '', 200);
assert.match(resp.body, //);
+ // folder mapped metadata is also applied.
+ assert.match(resp.body, //);
+
+ assert.deepStrictEqual(Object.fromEntries(resp.headers.entries()), {
+ 'access-control-allow-origin': '*',
+ 'content-type': 'text/html; charset=utf-8',
+ 'last-modified': 'Fri, 30 Apr 2021 03:47:18 GMT',
+ 'x-surrogate-key': 'AkcHu8fRFT7HarTR foo-id_metadata super-test--helix-pages--adobe_head foo-id AkcHu8fRFT7HarTR_metadata z8NGXvKB0X5Fzcnd',
+ link: '; rel=modulepreload; as=script; crossorigin=use-credentials',
+ });
});
it('renders 404', async () => {
diff --git a/test/steps/fetch-mapped-metadata.test.js b/test/steps/fetch-mapped-metadata.test.js
index 5fb28279..49f82783 100644
--- a/test/steps/fetch-mapped-metadata.test.js
+++ b/test/steps/fetch-mapped-metadata.test.js
@@ -24,6 +24,7 @@ describe('Fetch Mapped Metadata', () => {
contentBusId: 'foo-id',
partition: 'live',
mapped: true,
+ mappedPath: '/mapped',
info: {
path: '/mapped',
},
@@ -44,6 +45,7 @@ describe('Fetch Mapped Metadata', () => {
contentBusId: 'foo-id',
partition: 'live',
mapped: true,
+ mappedPath: '/mapped',
info: {
path: '/mapped',
},
@@ -63,6 +65,7 @@ describe('Fetch Mapped Metadata', () => {
contentBusId: 'foo-id',
partition: 'live',
mapped: true,
+ mappedPath: '/mapped',
info: {
path: '/mapped',
},
@@ -81,6 +84,7 @@ describe('Fetch Mapped Metadata', () => {
log: console,
contentBusId: 'foo-id',
partition: 'live',
+ mappedPath: '/mapped',
mapped: true,
info: {
path: '/mapped',
@@ -102,6 +106,7 @@ describe('Fetch Mapped Metadata', () => {
contentBusId: 'foo-id',
partition: 'live',
mapped: true,
+ mappedPath: '/mapped',
info: {
path: '/mapped',
},