//--------------------------------------------------------
//-- Node IoC - Foundation - Service Provider
//--------------------------------------------------------
import __ from '@absolunet/private-registry';
/**
* Base service provider class.
*
* @memberof foundation
* @abstract
* @hideconstructor
*/
class ServiceProvider {
/**
* Get all service providers that publish content.
*
* @returns {Array<foundation.ServiceProvider>} The publishable service provider instances.
*/
static publishableProviders() {
return [...__(ServiceProvider).get('publishable').entries()].filter(([, publishable]) => {
return publishable.length > 0;
}).map(([provider]) => {
return provider;
});
}
/**
* Get all tags for published content.
*
* @returns {Array<string>} The publishable tags.
*/
static publishableTags() {
return [...new Set([...__(ServiceProvider).get('publishable').values()].flat()
.filter(({ tag }) => {
return Boolean(tag);
})
.map(({ tag }) => {
return tag;
}))];
}
/**
* Get all publishable files and folders for a given provider.
*
* @param {foundation.ServiceProvider} provider - The service provider instance.
* @returns {object<string, string>} A dictionary associating the source paths to the destination paths.
*/
static getPublishableByProvider(provider) {
const publishable = __(ServiceProvider).get('publishable').get(provider) || [];
return Object.fromEntries(publishable.map(({ from, to }) => {
return [from, to];
}));
}
/**
* Get all publishable files and folders for a given tag.
*
* @param {string} tag - The tag to get pûblishables from.
* @returns {object<string, string>} A dictionary associating the source paths to the destination paths.
*/
static getPublishableByTag(tag) {
return Object.fromEntries([...__(ServiceProvider).get('publishable').values()].flat()
.filter(({ tag: publishableTag }) => {
return tag === publishableTag;
}).map(({ from, to }) => {
return [from, to];
}));
}
/**
* Get all publishable files and folders.
*
* @returns {object<string, string>} A dictionary associating the source paths to the destination paths.
*/
static getAllPublishable() {
return Object.fromEntries([...__(ServiceProvider).get('publishable').values()].flat()
.map(({ from, to }) => {
return [from, to];
}));
}
/**
* Name of the service provider.
* Will be use for display only.
*
* @type {string}
*/
get name() {
return this.constructor.name;
}
/**
* @inheritdoc
* @private
*/
init() {
__(ServiceProvider).get('publishable').set(this, []);
}
/**
* Load configuration file from given folder path.
*
* @param {string} configPath - The configuration folder path.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
loadConfig(configPath) {
if (this.app.isBound('config')) {
this.app.make('config').loadConfigFromFolder(configPath);
}
return this;
}
/**
* Load commands into the registrar.
*
* @param {Array<Function>} commands - List of all commands that must be registered.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
loadCommands(commands) {
if (this.app.isBound('command')) {
const commandRepository = this.app.make('command');
commands.forEach((command) => {
commandRepository.add(command);
});
}
return this;
}
/**
* Load translations into the translator.
*
* @param {string} translationsPath - The translations folder path.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
loadTranslations(translationsPath) {
if (this.app.isBound('file') && this.app.isBound('translator')) {
const file = this.app.make('file');
const translator = this.app.make('translator');
const translations = file.loadRecursivelyInFolder(translationsPath);
translator.addTranslations(translations);
}
return this;
}
/**
* Add views path in the resolver.
* If given, it will add the views path as a namespace.
*
* @param {string} viewsPath - The views path.
* @param {string} [namespace] - The views namespace.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
loadViews(viewsPath, namespace) {
if (this.app.isBound('view.resolver')) {
const resolver = this.app.make('view.resolver');
if (namespace) {
resolver.namespace(namespace, viewsPath);
} else {
resolver.addPath(viewsPath);
}
}
return this;
}
/**
* Publish paths, with an optional tag.
*
* @param {object<string, string>} publications - Dictionary representing the published paths, the "from" as keys and the "to" as values.
* @param {string} [tag] - The tag to give to the publications.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
publish(publications, tag) {
__(ServiceProvider).get('publishable').get(this).push(...Object.entries(publications).map(([from, to]) => {
return { from, to, tag };
}));
return this;
}
/**
* Publish static assets from given absolute folder path.
* Can store assets in a subfolder of the application's public directory if needed.
*
* @param {string} staticPath - The absolute assets folder path to publish.
* @param {string} [folder=""] - The folder to store the assets when publishing.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
publishAssets(staticPath, folder = '') {
return this.publish({ [staticPath]: this.app.publicPath(folder) }, 'assets');
}
/**
* Publish configuration files from given absolute folder path.
*
* @param {string} configPath - The absolute configuration folder path to publish.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
publishConfig(configPath) {
return this.publish({ [configPath]: this.app.configPath() }, 'config');
}
/**
* Publish migration stub files from given absolute folder path.
* Will only accept ".stub" files.
*
* @param {string} migrationsPath - The absolute migrations folder path to publish.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
publishMigrations(migrationsPath) {
const required = ['file', 'helper.date', 'helper.path', 'db.resolver'];
if (required.every((dependency) => { return this.app.isBound(dependency); })) {
this.app.make('db.resolver').bindPaths();
const file = this.app.make('file');
const pathHelper = this.app.make('helper.path');
const prefix = this.app.make('helper.date')().format('YYYYMMDDHHmmss');
const migrations = file.scandir(migrationsPath, 'file', { recursive: true, fullPath: true })
.filter((migration) => {
return migration.endsWith('.stub');
});
this.publish(Object.fromEntries(migrations.map((migration) => {
const fileName = pathHelper.relative(migrationsPath, migration)
.replace(/\d{14}/u, prefix)
.replace('.stub', '.js');
return [migration, this.app.sourcePath('migration', fileName)];
})), 'migrations');
}
return this;
}
/**
* Publish translations files from given absolute folder path.
*
* @param {string} translationsPath - The absolute translations folder path to publish.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
publishTranslations(translationsPath) {
return this.publish({ [translationsPath]: this.app.langPath() }, 'translations');
}
/**
* Publish views files from given absolute folder path.
* Can store views in a subfolder of the application's views directory if needed.
*
* @param {string} viewsPath - The absolute views folder path to publish.
* @param {string} [folder=""] - The folder to store the views when publishing.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
publishViews(viewsPath, folder = '') {
return this.publish({ [viewsPath]: this.app.viewPath(folder) }, 'views');
}
/**
* Load and publish configuration files.
*
* @param {string} configPath - The configuration folder path.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
loadAndPublishConfig(configPath) {
this.loadConfig(configPath);
this.publishConfig(configPath);
return this;
}
/**
* Load and publish translations from folder path.
*
* @param {string} translationsPath - The translations folder path.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
loadAndPublishTranslations(translationsPath) {
this.loadTranslations(translationsPath);
this.publishTranslations(translationsPath);
return this;
}
/**
* Load and publish views with namespace from folder path, with an optional folder destination name.
*
* @param {string} viewsPath - The absolute views folder path.
* @param {string} [namespace] - The views namespace for resolver and the folder to create for publishing.
* @returns {foundation.ServiceProvider} The current service provider instance.
*/
loadAndPublishViews(viewsPath, namespace) {
this.loadViews(viewsPath, namespace);
this.publishViews(viewsPath, namespace);
this.loadViews(this.app.viewPath(namespace), namespace);
return this;
}
}
__(ServiceProvider).set('publishable', new Map());
export default ServiceProvider;