cdcm
Version:
Community driven content management. Content stored in a directories composed of json, markdown, and images.
285 lines (192 loc) • 8.06 kB
JavaScript
;
const request = require( 'request' );
const fsx = require( 'fs-extra' ) || {};
const AdmZip = require( 'adm-zip' ) || {};
const marked = require( 'marked' );
const path = require( 'path' );
module.exports = function (config) {
config = Object.assign( {
zipUrl : false,
cdnUrl : false,
localPath : false,
tempDir : '/.tmp-cdcm',
verbose : 1000,
linkFileExt : []
}, config );
config.tempDir = process.cwd() + config.tempDir;
config.zipFilePath = config.tempDir + '/tmp.zip';
let timeout = false;
return {
getData : function getData () {
if ( config.verbose )
timeout = setTimeout( () => console.log( 'Getting data...' ), config.verbose );
return getContent()
.then( () => {
return generateData();
} )
.then( data => {
if ( timeout ) clearTimeout( timeout );
return Promise.resolve( data );
} );
}
};
function getContent () {
return fsx.emptyDir( config.tempDir )
.then( () => {
if ( config.localPath && fsx.pathExistsSync( config.localPath ) && isDirectory( config.localPath ) ) {
return fsx.copy( config.localPath, config.tempDir );
}
else if ( config.zipUrl ) {
return getFromUrl();
}
return Promise.reject( 'Enter a valid localPath or config.zipUrl.' );
} );
function getFromUrl () {
return downloadZip()
.then( () => {
return extractZip();
} )
.then( () => {
return copyExtractedFiles();
} )
}
function downloadZip () {
return new Promise( (resolve, reject) => {
return request( { url : config.zipUrl, encoding : null }, (err, resp, zipFile) => {
if ( err ) {
reject( err );
}
fsx.removeSync( config.zipFilePath );
fsx.writeFile( config.zipFilePath, zipFile, (err) => {
if ( err ) {
reject( err );
}
resolve( config.zipFilePath );
} );
} );
} );
}
function extractZip () {
const zip = new AdmZip( config.zipFilePath );
zip.extractAllTo( config.tempDir, true );
fsx.removeSync( config.zipFilePath );
return Promise.resolve();
}
function copyExtractedFiles () {
fsx.readdirSync( config.tempDir )
.forEach( extractFolder => {
let unzippedFolderPath = config.tempDir + '/' + extractFolder;
if ( isDirectory( unzippedFolderPath ) ) {
copyFiles( unzippedFolderPath, config.tempDir );
} else {
fsx.copySync( extractFolder, config.tempDir );
}
fsx.removeSync( unzippedFolderPath );
} );
return Promise.resolve();
}
function copyFiles (from, to) {
fsx.readdirSync( from )
.filter( file => !file.startsWith( '.' ) )
.forEach( file => {
fsx.copySync( from + '/' + file, to + '/' + file );
} );
return Promise.resolve( to );
}
}
function generateData () {
let contentTypesRaw = fsx.readdirSync( config.tempDir );
return contentTypesRaw
.map( file => {
const filePath = config.tempDir + '/' + file;
let items = [];
let type = file;
if ( isDirectory( filePath ) ) {
items = processItemsDir( filePath, file );
} else if ( '.json' === path.extname( file ) ) {
items = fsx.readJsonSync( filePath, 'utf-8' );
type = path.basename( file, '.json' );
} else {
return false;
}
items = Array.isArray( items ) ? items : [ items ];
items = typeProcessor( type )( items );
return { items, type };
} )
.filter( items => items );
function processItemsDir (dirPath, typeName) {
let items = [];
let itemFiles = fsx.readdirSync( dirPath )
.filter( slug => {
return isDirectory( dirPath + '/' + slug )
&& !slug.startsWith( '_' )
&& !slug.startsWith( '.' )
&& slug.toLowerCase() !== 'readme.md';
} );
itemFiles.forEach( slug => {
let itemDirPath = dirPath + '/' + slug;
let itemSource = { slug : slug };
let itemContents = fsx.readdirSync( itemDirPath );
let processedFiles = itemContents.map( file => {
let filePath = itemDirPath + '/' + file;
let fileName = path.basename( file, path.extname( file ) );
let extension = path.extname( file );
return fileProcessor( extension )( typeName, slug, fileName, filePath, itemSource );
} );
items.push( Object.assign( {}, ...processedFiles ) );
} );
return items;
}
function defaultFileProcessors () {
let processors = {
'.md' : function (typeName, slug, fileName, filePath, source) {
const markdownContent = fsx.readFileSync( filePath, 'utf-8' );
source[ fileName ] = marked( markdownContent );
return source;
},
'.json' : function (typeName, slug, fileName, filePath, source) {
const data = fsx.readJsonSync( filePath, { throws : false } );
return Object.assign( source, data || {} );
}
};
let linkFileExt = [ ...new Set( [ '.png', '.jpg' ].concat( config.linkFileExt ) ) ];
linkFileExt.forEach( ext => {
processors[ ext ] = fileLinker( ext );
} );
return processors;
function fileLinker (fileExt) {
return function (typeName, slug, fileName, filePath, source) {
source[ fileName ] = config.cdnUrl + '/'
+ typeName + '/' + slug + '/' + fileName + fileExt;
return source;
};
}
}
function fileProcessor (fileExt) {
let processors = defaultFileProcessors();
return function (typeName, slug, fileName, filePath, source) {
if ( processors.hasOwnProperty( fileExt ) && typeof processors[ fileExt ] === 'function' ) {
source = processors[ fileExt ]( typeName, slug, fileName, filePath, source );
}
return source;
}
}
function typeProcessor (type) {
let p = {
'humans' : function (item) {
item.role = Array.isArray( item.role ) ? item.role : [ item.role ];
return item;
}
};
return function (items) {
if ( p.hasOwnProperty( type ) && typeof p[ type ] === 'function' ) {
items = items.map( item => p[ type ]( item ) );
}
return items;
}
}
}
};
function isDirectory (path) {
return fsx.lstatSync( path ).isDirectory();
}