Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-11-19 11:27:35 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-11-19 11:27:35 +0300
commit7e9c479f7de77702622631cff2628a9c8dcbc627 (patch)
treec8f718a08e110ad7e1894510980d2155a6549197 /app/assets/javascripts/static_site_editor
parente852b0ae16db4052c1c567d9efa4facc81146e88 (diff)
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/static_site_editor')
-rw-r--r--app/assets/javascripts/static_site_editor/components/edit_area.vue50
-rw-r--r--app/assets/javascripts/static_site_editor/components/edit_meta_controls.vue100
-rw-r--r--app/assets/javascripts/static_site_editor/components/edit_meta_modal.vue45
-rw-r--r--app/assets/javascripts/static_site_editor/constants.js3
-rw-r--r--app/assets/javascripts/static_site_editor/graphql/index.js4
-rw-r--r--app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql7
-rw-r--r--app/assets/javascripts/static_site_editor/graphql/typedefs.graphql9
-rw-r--r--app/assets/javascripts/static_site_editor/image_repository.js4
-rw-r--r--app/assets/javascripts/static_site_editor/index.js9
-rw-r--r--app/assets/javascripts/static_site_editor/pages/home.vue10
-rw-r--r--app/assets/javascripts/static_site_editor/services/front_matterify.js2
-rw-r--r--app/assets/javascripts/static_site_editor/services/parse_source_file.js18
-rw-r--r--app/assets/javascripts/static_site_editor/services/renderers/render_image.js89
13 files changed, 291 insertions, 59 deletions
diff --git a/app/assets/javascripts/static_site_editor/components/edit_area.vue b/app/assets/javascripts/static_site_editor/components/edit_area.vue
index e602f26acdf..69eabfe5339 100644
--- a/app/assets/javascripts/static_site_editor/components/edit_area.vue
+++ b/app/assets/javascripts/static_site_editor/components/edit_area.vue
@@ -6,10 +6,10 @@ import EditDrawer from './edit_drawer.vue';
import UnsavedChangesConfirmDialog from './unsaved_changes_confirm_dialog.vue';
import parseSourceFile from '~/static_site_editor/services/parse_source_file';
import { EDITOR_TYPES } from '~/vue_shared/components/rich_content_editor/constants';
-import { DEFAULT_IMAGE_UPLOAD_PATH } from '../constants';
import imageRepository from '../image_repository';
import formatter from '../services/formatter';
import templater from '../services/templater';
+import renderImage from '../services/renderers/render_image';
export default {
components: {
@@ -37,21 +37,35 @@ export default {
required: false,
default: '',
},
+ branch: {
+ type: String,
+ required: true,
+ },
+ baseUrl: {
+ type: String,
+ required: true,
+ },
+ mounts: {
+ type: Array,
+ required: true,
+ },
+ project: {
+ type: String,
+ required: true,
+ },
imageRoot: {
type: String,
- required: false,
- default: DEFAULT_IMAGE_UPLOAD_PATH,
- validator: prop => prop.endsWith('/'),
+ required: true,
},
},
data() {
return {
- saveable: false,
parsedSource: parseSourceFile(this.preProcess(true, this.content)),
editorMode: EDITOR_TYPES.wysiwyg,
- isModified: false,
hasMatter: false,
isDrawerOpen: false,
+ isModified: false,
+ isSaveable: false,
};
},
imageRepository: imageRepository(),
@@ -68,6 +82,18 @@ export default {
isWysiwygMode() {
return this.editorMode === EDITOR_TYPES.wysiwyg;
},
+ customRenderers() {
+ const imageRenderer = renderImage.build(
+ this.mounts,
+ this.project,
+ this.branch,
+ this.baseUrl,
+ this.$options.imageRepository,
+ );
+ return {
+ image: [imageRenderer],
+ };
+ },
},
created() {
this.refreshEditHelpers();
@@ -81,8 +107,11 @@ export default {
return templatedContent;
},
refreshEditHelpers() {
- this.isModified = this.parsedSource.isModified();
- this.hasMatter = this.parsedSource.hasMatter();
+ const { isModified, hasMatter, isMatterValid } = this.parsedSource;
+ this.isModified = isModified();
+ this.hasMatter = hasMatter();
+ const hasValidMatter = this.hasMatter ? isMatterValid() : true;
+ this.isSaveable = this.isModified && hasValidMatter;
},
onDrawerOpen() {
this.isDrawerOpen = true;
@@ -133,17 +162,18 @@ export default {
:content="editableContent"
:initial-edit-type="editorMode"
:image-root="imageRoot"
+ :options="{ customRenderers }"
class="mb-9 pb-6 h-100"
@modeChange="onModeChange"
@input="onInputChange"
@uploadImage="onUploadImage"
/>
- <unsaved-changes-confirm-dialog :modified="isModified" />
+ <unsaved-changes-confirm-dialog :modified="isSaveable" />
<publish-toolbar
class="gl-fixed gl-left-0 gl-bottom-0 gl-w-full"
:has-settings="hasSettings"
:return-url="returnUrl"
- :saveable="isModified"
+ :saveable="isSaveable"
:saving-changes="savingChanges"
@editSettings="onDrawerOpen"
@submit="onSubmit"
diff --git a/app/assets/javascripts/static_site_editor/components/edit_meta_controls.vue b/app/assets/javascripts/static_site_editor/components/edit_meta_controls.vue
index 9f75c65a316..c6247632b6e 100644
--- a/app/assets/javascripts/static_site_editor/components/edit_meta_controls.vue
+++ b/app/assets/javascripts/static_site_editor/components/edit_meta_controls.vue
@@ -1,9 +1,21 @@
<script>
-import { GlForm, GlFormGroup, GlFormInput, GlFormTextarea } from '@gitlab/ui';
-import AccessorUtilities from '~/lib/utils/accessor';
+import {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlForm,
+ GlFormGroup,
+ GlFormInput,
+ GlFormTextarea,
+} from '@gitlab/ui';
+
+import { __ } from '~/locale';
export default {
components: {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownItem,
GlForm,
GlFormGroup,
GlFormInput,
@@ -18,56 +30,47 @@ export default {
type: String,
required: true,
},
- },
- data() {
- return {
- editable: {
- title: this.title,
- description: this.description,
- },
- };
+ templates: {
+ type: Array,
+ required: false,
+ default: null,
+ },
+ currentTemplate: {
+ type: Object,
+ required: false,
+ default: null,
+ },
},
computed: {
- editableStorageKey() {
- return this.getId('local-storage', 'editable');
+ dropdownLabel() {
+ return this.currentTemplate ? this.currentTemplate.name : __('None');
},
- hasLocalStorage() {
- return AccessorUtilities.isLocalStorageAccessSafe();
+ hasTemplates() {
+ return this.templates?.length > 0;
},
},
mounted() {
- this.initCachedEditable();
this.preSelect();
},
methods: {
getId(type, key) {
return `sse-merge-request-meta-${type}-${key}`;
},
- initCachedEditable() {
- if (this.hasLocalStorage) {
- const cachedEditable = JSON.parse(localStorage.getItem(this.editableStorageKey));
- if (cachedEditable) {
- this.editable = cachedEditable;
- }
- }
- },
preSelect() {
this.$nextTick(() => {
this.$refs.title.$el.select();
});
},
- resetCachedEditable() {
- if (this.hasLocalStorage) {
- window.localStorage.removeItem(this.editableStorageKey);
- }
+ onChangeTemplate(template) {
+ this.$emit('changeTemplate', template || null);
},
- onUpdate() {
- const payload = { ...this.editable };
+ onUpdate(field, value) {
+ const payload = {
+ title: this.title,
+ description: this.description,
+ [field]: value,
+ };
this.$emit('updateSettings', payload);
-
- if (this.hasLocalStorage) {
- window.localStorage.setItem(this.editableStorageKey, JSON.stringify(payload));
- }
},
},
};
@@ -83,21 +86,44 @@ export default {
<gl-form-input
:id="getId('control', 'title')"
ref="title"
- v-model.lazy="editable.title"
+ :value="title"
type="text"
- @input="onUpdate"
+ @input="onUpdate('title', $event)"
/>
</gl-form-group>
<gl-form-group
+ v-if="hasTemplates"
+ key="template"
+ :label="__('Description template')"
+ :label-for="getId('control', 'template')"
+ >
+ <gl-dropdown :text="dropdownLabel">
+ <gl-dropdown-item key="none" @click="onChangeTemplate(null)">
+ {{ __('None') }}
+ </gl-dropdown-item>
+
+ <gl-dropdown-divider />
+
+ <gl-dropdown-item
+ v-for="template in templates"
+ :key="template.key"
+ @click="onChangeTemplate(template)"
+ >
+ {{ template.name }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </gl-form-group>
+
+ <gl-form-group
key="description"
:label="__('Goal of the changes and what reviewers should be aware of')"
:label-for="getId('control', 'description')"
>
<gl-form-textarea
:id="getId('control', 'description')"
- v-model.lazy="editable.description"
- @input="onUpdate"
+ :value="description"
+ @input="onUpdate('description', $event)"
/>
</gl-form-group>
</gl-form>
diff --git a/app/assets/javascripts/static_site_editor/components/edit_meta_modal.vue b/app/assets/javascripts/static_site_editor/components/edit_meta_modal.vue
index 4e5245bd892..f583d2049af 100644
--- a/app/assets/javascripts/static_site_editor/components/edit_meta_modal.vue
+++ b/app/assets/javascripts/static_site_editor/components/edit_meta_modal.vue
@@ -1,22 +1,38 @@
<script>
import { GlModal } from '@gitlab/ui';
import { __, s__, sprintf } from '~/locale';
+import Api from '~/api';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import EditMetaControls from './edit_meta_controls.vue';
+import { ISSUABLE_TYPE, MR_META_LOCAL_STORAGE_KEY } from '../constants';
+
export default {
components: {
GlModal,
EditMetaControls,
+ LocalStorageSync,
},
props: {
sourcePath: {
type: String,
required: true,
},
+ namespace: {
+ type: String,
+ required: true,
+ },
+ project: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
+ clearStorage: false,
+ currentTemplate: null,
+ mergeRequestTemplates: null,
mergeRequestMeta: {
title: sprintf(s__(`StaticSiteEditor|Update %{sourcePath} file`), {
sourcePath: this.sourcePath,
@@ -42,24 +58,42 @@ export default {
};
},
},
+ mounted() {
+ this.initTemplates();
+ },
methods: {
hide() {
this.$refs.modal.hide();
},
+ initTemplates() {
+ const { namespace, project } = this;
+ Api.issueTemplates(namespace, project, ISSUABLE_TYPE, (err, templates) => {
+ if (err) return; // Error handled by global AJAX error handler
+ this.mergeRequestTemplates = templates;
+ });
+ },
show() {
this.$refs.modal.show();
},
onPrimary() {
this.$emit('primary', this.mergeRequestMeta);
- this.$refs.editMetaControls.resetCachedEditable();
+ this.clearStorage = true;
},
onSecondary() {
this.hide();
},
+ onChangeTemplate(template) {
+ this.currentTemplate = template;
+
+ const description = this.currentTemplate ? this.currentTemplate.content : '';
+ const mergeRequestMeta = { ...this.mergeRequestMeta, description };
+ this.onUpdateSettings(mergeRequestMeta);
+ },
onUpdateSettings(mergeRequestMeta) {
this.mergeRequestMeta = { ...mergeRequestMeta };
},
},
+ storageKey: MR_META_LOCAL_STORAGE_KEY,
};
</script>
@@ -75,11 +109,20 @@ export default {
@secondary="onSecondary"
@hide="() => $emit('hide')"
>
+ <local-storage-sync
+ v-model="mergeRequestMeta"
+ :storage-key="$options.storageKey"
+ :clear="clearStorage"
+ as-json
+ />
<edit-meta-controls
ref="editMetaControls"
:title="mergeRequestMeta.title"
:description="mergeRequestMeta.description"
+ :templates="mergeRequestTemplates"
+ :current-template="currentTemplate"
@updateSettings="onUpdateSettings"
+ @changeTemplate="onChangeTemplate"
/>
</gl-modal>
</template>
diff --git a/app/assets/javascripts/static_site_editor/constants.js b/app/assets/javascripts/static_site_editor/constants.js
index 49db9ab7ca5..faa4026c064 100644
--- a/app/assets/javascripts/static_site_editor/constants.js
+++ b/app/assets/javascripts/static_site_editor/constants.js
@@ -2,6 +2,7 @@ import { s__, __ } from '~/locale';
export const BRANCH_SUFFIX_COUNT = 8;
export const DEFAULT_TARGET_BRANCH = 'master';
+export const ISSUABLE_TYPE = 'merge_request';
export const SUBMIT_CHANGES_BRANCH_ERROR = s__('StaticSiteEditor|Branch could not be created.');
export const SUBMIT_CHANGES_COMMIT_ERROR = s__(
@@ -20,4 +21,4 @@ export const TRACKING_ACTION_CREATE_COMMIT = 'create_commit';
export const TRACKING_ACTION_CREATE_MERGE_REQUEST = 'create_merge_request';
export const TRACKING_ACTION_INITIALIZE_EDITOR = 'initialize_editor';
-export const DEFAULT_IMAGE_UPLOAD_PATH = 'source/images/uploads/';
+export const MR_META_LOCAL_STORAGE_KEY = 'sse-merge-request-meta-storage-key';
diff --git a/app/assets/javascripts/static_site_editor/graphql/index.js b/app/assets/javascripts/static_site_editor/graphql/index.js
index cc68bc57bb0..a13f7d3ad53 100644
--- a/app/assets/javascripts/static_site_editor/graphql/index.js
+++ b/app/assets/javascripts/static_site_editor/graphql/index.js
@@ -25,11 +25,15 @@ const createApolloProvider = appData => {
},
);
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ const mounts = appData.mounts.map(mount => ({ __typename: 'Mount', ...mount }));
+
defaultClient.cache.writeData({
data: {
appData: {
__typename: 'AppData',
...appData,
+ mounts,
},
},
});
diff --git a/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql b/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql
index 9f4b0afe55f..e422a4b6036 100644
--- a/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql
+++ b/app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql
@@ -6,5 +6,12 @@ query appData {
sourcePath
username
returnUrl
+ branch
+ baseUrl
+ mounts {
+ source
+ target
+ }
+ imageUploadPath
}
}
diff --git a/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql b/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql
index 0ded1722d26..00af6c10359 100644
--- a/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql
+++ b/app/assets/javascripts/static_site_editor/graphql/typedefs.graphql
@@ -14,6 +14,11 @@ type SavedContentMeta {
branch: SavedContentField!
}
+type Mount {
+ source: String!
+ target: String
+}
+
type AppData {
isSupportedContent: Boolean!
hasSubmittedChanges: Boolean!
@@ -21,6 +26,10 @@ type AppData {
returnUrl: String
sourcePath: String!
username: String!
+ branch: String!
+ baseUrl: String!
+ mounts: [Mount]!
+ imageUploadPath: String!
}
input HasSubmittedChangesInput {
diff --git a/app/assets/javascripts/static_site_editor/image_repository.js b/app/assets/javascripts/static_site_editor/image_repository.js
index 02285ccdba3..b5ff4385d3c 100644
--- a/app/assets/javascripts/static_site_editor/image_repository.js
+++ b/app/assets/javascripts/static_site_editor/image_repository.js
@@ -12,9 +12,11 @@ const imageRepository = () => {
.catch(() => flash(__('Something went wrong while inserting your image. Please try again.')));
};
+ const get = path => images.get(path);
+
const getAll = () => images;
- return { add, getAll };
+ return { add, get, getAll };
};
export default imageRepository;
diff --git a/app/assets/javascripts/static_site_editor/index.js b/app/assets/javascripts/static_site_editor/index.js
index fceef8f9084..b58564388de 100644
--- a/app/assets/javascripts/static_site_editor/index.js
+++ b/app/assets/javascripts/static_site_editor/index.js
@@ -9,6 +9,7 @@ const initStaticSiteEditor = el => {
isSupportedContent,
path: sourcePath,
baseUrl,
+ branch,
namespace,
project,
mergeRequestsIllustrationPath,
@@ -16,13 +17,9 @@ const initStaticSiteEditor = el => {
// so we are adding them here as a convenience for future use.
// eslint-disable-next-line no-unused-vars
staticSiteGenerator,
- // eslint-disable-next-line no-unused-vars
imageUploadPath,
mounts,
} = el.dataset;
- // NOTE that the object in 'mounts' is a JSON string from the data attribute, so it must be parsed into an object.
- // eslint-disable-next-line no-unused-vars
- const mountsObject = JSON.parse(mounts);
const { current_username: username } = window.gon;
const returnUrl = el.dataset.returnUrl || null;
const router = createRouter(baseUrl);
@@ -30,9 +27,13 @@ const initStaticSiteEditor = el => {
isSupportedContent: parseBoolean(isSupportedContent),
hasSubmittedChanges: false,
project: `${namespace}/${project}`,
+ mounts: JSON.parse(mounts), // NOTE that the object in 'mounts' is a JSON string from the data attribute, so it must be parsed into an object.
+ branch,
+ baseUrl,
returnUrl,
sourcePath,
username,
+ imageUploadPath,
});
return new Vue({
diff --git a/app/assets/javascripts/static_site_editor/pages/home.vue b/app/assets/javascripts/static_site_editor/pages/home.vue
index 27bd1c99ae2..68943113c14 100644
--- a/app/assets/javascripts/static_site_editor/pages/home.vue
+++ b/app/assets/javascripts/static_site_editor/pages/home.vue
@@ -64,6 +64,9 @@ export default {
isContentLoaded() {
return Boolean(this.sourceContent);
},
+ projectSplit() {
+ return this.appData.project.split('/'); // TODO: refactor so `namespace` and `project` remain distinct
+ },
},
mounted() {
Tracking.event(document.body.dataset.page, TRACKING_ACTION_INITIALIZE_EDITOR);
@@ -138,11 +141,18 @@ export default {
:content="sourceContent.content"
:saving-changes="isSavingChanges"
:return-url="appData.returnUrl"
+ :mounts="appData.mounts"
+ :branch="appData.branch"
+ :base-url="appData.baseUrl"
+ :project="appData.project"
+ :image-root="appData.imageUploadPath"
@submit="onPrepareSubmit"
/>
<edit-meta-modal
ref="editMetaModal"
:source-path="appData.sourcePath"
+ :namespace="projectSplit[0]"
+ :project="projectSplit[1]"
@primary="onSubmit"
@hide="onHideModal"
/>
diff --git a/app/assets/javascripts/static_site_editor/services/front_matterify.js b/app/assets/javascripts/static_site_editor/services/front_matterify.js
index cbf0fffd515..60a5d799d11 100644
--- a/app/assets/javascripts/static_site_editor/services/front_matterify.js
+++ b/app/assets/javascripts/static_site_editor/services/front_matterify.js
@@ -16,6 +16,7 @@ export const frontMatterify = source => {
const NO_FRONTMATTER = {
source,
matter: null,
+ hasMatter: false,
spacing: null,
content: source,
delimiter: null,
@@ -53,6 +54,7 @@ export const frontMatterify = source => {
return {
source,
matter,
+ hasMatter: true,
spacing,
content,
delimiter,
diff --git a/app/assets/javascripts/static_site_editor/services/parse_source_file.js b/app/assets/javascripts/static_site_editor/services/parse_source_file.js
index d4fc8b2edb6..39126eb7bcc 100644
--- a/app/assets/javascripts/static_site_editor/services/parse_source_file.js
+++ b/app/assets/javascripts/static_site_editor/services/parse_source_file.js
@@ -1,15 +1,18 @@
import { frontMatterify, stringify } from './front_matterify';
const parseSourceFile = raw => {
- const remake = source => frontMatterify(source);
-
- let editable = remake(raw);
+ let editable;
const syncContent = (newVal, isBody) => {
if (isBody) {
editable.content = newVal;
} else {
- editable = remake(newVal);
+ try {
+ editable = frontMatterify(newVal);
+ editable.isMatterValid = true;
+ } catch (e) {
+ editable.isMatterValid = false;
+ }
}
};
@@ -23,10 +26,15 @@ const parseSourceFile = raw => {
const isModified = () => stringify(editable) !== raw;
- const hasMatter = () => Boolean(editable.matter);
+ const hasMatter = () => editable.hasMatter;
+
+ const isMatterValid = () => editable.isMatterValid;
+
+ syncContent(raw);
return {
matter,
+ isMatterValid,
syncMatter,
content,
syncContent,
diff --git a/app/assets/javascripts/static_site_editor/services/renderers/render_image.js b/app/assets/javascripts/static_site_editor/services/renderers/render_image.js
new file mode 100644
index 00000000000..b0d863bdb5a
--- /dev/null
+++ b/app/assets/javascripts/static_site_editor/services/renderers/render_image.js
@@ -0,0 +1,89 @@
+import { isAbsolute, getBaseURL, joinPaths } from '~/lib/utils/url_utility';
+
+const canRender = ({ type }) => type === 'image';
+
+let metadata;
+
+const getCachedContent = basePath => metadata.imageRepository.get(basePath);
+
+const isRelativeToCurrentDirectory = basePath => !basePath.startsWith('/');
+
+const extractSourceDirectory = url => {
+ const sourceDir = /^(.+)\/([^/]+)$/.exec(url); // Extracts the base path and fileName from an image path
+ return sourceDir || [null, null, url]; // If no source directory was extracted it means only a fileName was specified (e.g. url='file.png')
+};
+
+const parseCurrentDirectory = basePath => {
+ const baseUrl = decodeURIComponent(metadata.baseUrl);
+ const sourceDirectory = extractSourceDirectory(baseUrl)[1];
+ const currentDirectory = sourceDirectory.split(`/-/sse/${metadata.branch}`)[1];
+
+ return joinPaths(currentDirectory, basePath);
+};
+
+// For more context around this logic, please see the following comment:
+// https://gitlab.com/gitlab-org/gitlab/-/issues/241166#note_409413500
+const generateSourceDirectory = basePath => {
+ let sourceDir = '';
+ let defaultSourceDir = '';
+
+ if (!basePath || isRelativeToCurrentDirectory(basePath)) {
+ return parseCurrentDirectory(basePath);
+ }
+
+ if (!metadata.mounts.length) {
+ return basePath;
+ }
+
+ metadata.mounts.forEach(({ source, target }) => {
+ const hasTarget = target !== '';
+
+ if (hasTarget && basePath.includes(target)) {
+ sourceDir = source;
+ } else if (!hasTarget) {
+ defaultSourceDir = joinPaths(source, basePath);
+ }
+ });
+
+ return sourceDir || defaultSourceDir;
+};
+
+const resolveFullPath = (originalSrc, cachedContent) => {
+ if (cachedContent) {
+ return `data:image;base64,${cachedContent}`;
+ }
+
+ if (isAbsolute(originalSrc)) {
+ return originalSrc;
+ }
+
+ const sourceDirectory = extractSourceDirectory(originalSrc);
+ const [, basePath, fileName] = sourceDirectory;
+ const sourceDir = generateSourceDirectory(basePath);
+
+ return joinPaths(getBaseURL(), metadata.project, '/-/raw/', metadata.branch, sourceDir, fileName);
+};
+
+const render = ({ destination: originalSrc, firstChild }, { skipChildren }) => {
+ skipChildren();
+
+ const cachedContent = getCachedContent(originalSrc);
+
+ return {
+ type: 'openTag',
+ tagName: 'img',
+ selfClose: true,
+ attributes: {
+ 'data-original-src': !isAbsolute(originalSrc) || cachedContent ? originalSrc : '',
+ src: resolveFullPath(originalSrc, cachedContent),
+ alt: firstChild.literal,
+ },
+ };
+};
+
+const build = (mounts = [], project, branch, baseUrl, imageRepository) => {
+ metadata = { mounts, project, branch, baseUrl, imageRepository };
+ return { canRender, render };
+};
+
+export default { build };