@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
157 lines (155 loc) • 5.92 kB
JavaScript
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { augmentTranslations } from '../utils/i18n';
import { components, plugins } from '../utils/constants';
const DEFAULT_FILE_NAME = 'index.js';
function isPluginFileBuilder(target) {
return typeof target === 'object';
}
export function buildFileUrl(siteOrBuilder, type, name, file, id) {
let site = siteOrBuilder;
if (isPluginFileBuilder(siteOrBuilder)) {
const builder = siteOrBuilder;
site = builder.site;
type = builder.type;
name = builder.name;
file = builder.file;
id = builder.id;
}
let url = `/studio/1/plugin/file?siteId=${site}&type=${type}&name=${name}&filename=${
file !== null && file !== void 0 ? file : DEFAULT_FILE_NAME
}`;
if (id) {
url += `&pluginId=${id}`;
}
return url;
}
export function createFileBuilder(site, type, name, file = DEFAULT_FILE_NAME, id) {
return Object.assign({ site, type, name, file }, id ? { id } : {});
}
export function importFile(siteOrBuilder, type, name, file, id) {
// @ts-ignore — methods share same signature
const url = buildFileUrl(...arguments);
return import(/* webpackIgnore: true */ url);
}
export function importPlugin(siteOrBuilder, type, name, file, id) {
// @ts-ignore — methods share the same signature(s)
const args = arguments;
return importFile(...args).then((module) => {
var _a;
const plugin = (_a = module.plugin) !== null && _a !== void 0 ? _a : module.default;
if (plugin) {
// The file may have been previously loaded and hence the plugin registered previously.
// This may however cause silent skips of legitimate duplicate plugin id registrations.
// Perhaps we should consider keeping an internal registry of the plugin file URLs that
// have been loaded if this is an issue.
!isPluginRegistered(plugin) &&
registerPlugin(plugin, isPluginFileBuilder(siteOrBuilder) ? siteOrBuilder : createFileBuilder(...args));
}
return plugin;
});
}
export function isPluginRegistered(plugin) {
return plugins.has(plugin === null || plugin === void 0 ? void 0 : plugin.id);
}
export function registerPlugin(plugin, source) {
var _a, _b;
// Skip registration if plugin with same id already exists
if (!plugins.has(plugin.id)) {
const extendedDescriptor = Object.assign(Object.assign({}, plugin), { source });
plugins.set(plugin.id, extendedDescriptor);
registerComponents(plugin.widgets);
augmentTranslations(plugin.locales);
// TODO: Allow externals?
if (source) {
(_a = plugin.stylesheets) === null || _a === void 0
? void 0
: _a.forEach((href) =>
appendStylesheet(
typeof href === 'string'
? hasProtocol(href)
? href
: buildFileUrl(Object.assign(Object.assign({}, source), { file: href }))
: href
)
);
(_b = plugin.scripts) === null || _b === void 0
? void 0
: _b.forEach((src) =>
appendScript(
typeof src === 'string'
? hasProtocol(src)
? src
: buildFileUrl(Object.assign(Object.assign({}, source), { file: src }))
: src
)
);
} else {
console.error('Scripts & stylesheets not allowed for umd bundles');
}
return true;
} else {
console.error(`Attempt to register a duplicate plugin "${plugin.id}" skipped.`);
return false;
}
}
export function registerComponents(widgets) {
Object.entries(widgets).forEach(([id, widget]) => {
// Skip registration if component with same id already exists
if (!components.has(id)) {
components.set(id, widget);
} else {
console.error(`Attempt to register a duplicate component id "${id}" skipped.`);
}
});
}
export function appendStylesheet(href) {
return appendLoadable('link', Object.assign({ rel: 'stylesheet' }, typeof href === 'string' ? { href } : href));
}
export function appendScript(src) {
return appendLoadable('script', typeof src === 'string' ? { src } : src);
}
function appendLoadable(type, attributes) {
return new Promise((resolve, reject) => {
const element = document.createElement(type);
for (let attr in attributes) {
if (Object.prototype.hasOwnProperty.call(attributes, attr)) {
element.setAttribute(attr, attributes[attr]);
}
}
element.onload = resolve;
element.onerror = reject;
document.head.appendChild(element);
});
}
function hasProtocol(url) {
return /^(http)(s?):\/\//.test(url);
}