UNPKG

@sysdoc/sharepoint-util

Version:

A utility library for SharePoint solutions

1,191 lines (1,124 loc) 50.5 kB
const gulp = require('gulp'), pump = require('pump'), fs = require('fs'), url = require('url'), path = require('path'), colors = require('colors'), args = require('yargs').argv, request = require('request'), data = require('gulp-data'), webpack = require('webpack'), mkdirp = require('mkdirp'), nunjucks = require('nunjucks'), nunjucksTask = require('gulp-nunjucks'), concat = require('gulp-concat'), sass = require('gulp-sass'), uglify = require('gulp-uglify'), rename = require('gulp-rename'), cwd = process.cwd(), autoprefixer = require('gulp-autoprefixer'), configFilePath = `${cwd}/config/project.json`; let isDebug = args.dev ? true : false, isVerbose = args.verbose ? true : false, isPrototyping = args.prototype || args.isPrototyping?true:false, liveUpdate = args.liveUpdate?true:false, prototypes = [], prototypeDefinitionCache = {}; var config = { env:process.env.NODE_ENV || 'dev', assetsDir:'./assets', sassDir:'./sass', srcDir:'./src', distDir:'./dist', jsDistDir:'./dist/js', cssDistDir:'./dist/css', libDir:'./lib', templatesDir:'./templates', prototypeDir:'./prototype', masterPageTemplatesDir:'./templates/masterpages', pageLayoutTemplatesDir:'./templates/pagelayouts', prototypeTemplatesDir:'./templates/prototypes', deploymentDir:'Sysdoc', provisioningDir:'./deploy', cdn:[], masterpageCatalogDrive:null, siteAssetsDrive:null, styleLibraryDrive:null, }; var nunjucksEngine = null; var webpackConfig = null; var siteDefinition = null; const AssetSources = [ '*.png', '*.jpg', '*.ico', '*.svg', '*.ttf', '*.eot', '*.woff', '*.woff2', '*.js', '*.json', '**/*.png', '**/*.jpg', '**/*.ico', '**/*.svg', '**/*.ttf', '**/*.eot', '**/*.woff', '**/*.woff2', '**/*.js', '**/*.json' ]; /** * @type {wp.Compiler} */ var webpackCompiler = null; var webpackWatch = null; var uploadingPrototypes = false; function initNunjucks(config){ logVerbose('initNunjucks', `Going to load nunjucks templates from ${path.resolve(cwd, config.templatesDir)}`); logVerbose('initNunjucks', `Going to load nunjucks templates from ${path.resolve(cwd, config.masterPageTemplatesDir)}`); logVerbose('initNunjucks', `Going to load nunjucks templates from ${path.resolve(cwd, config.pageLayoutTemplatesDir)}`) nunjucksEngine = nunjucks.configure([path.resolve(cwd,config.templatesDir), path.resolve(cwd, config.masterPageTemplatesDir), path.resolve(cwd, config.pageLayoutTemplatesDir)],{ noCache:true }); } function updateConfigFromArgs(config,args){ config.env = args.dev?'dev':(args.env || config.env || 'dev'); isDebug = config.env === 'dev'; config.sassDir = args.sassDir || config.sassDir || './sass'; config.srcDir = args.srcDir || config.srcDir || './src'; config.distDir = args.distDir || config.distDir || './dist'; if (config.useSharePoint){ config.spHost = args.spHost || config.spHost || 'https://tenant.sharepoint.com/'; if (!args.spHost && !config.spHost){ logWarning('Configuration',`No spHost has been provided in your configuration To fix this: you can rerun your gulp task with --spHost YOUR_SP_HOST Going to use: https://tenant.sharepoint.com/ for now.`); } config.url = args.url || config.url || 'test-site'; if (!args.url && !config.url){ logWarning('Configuration', `No Site URL has been provided in your configuration To fix this: you can rerun your gulp task with --url YOUR_URL Going to use: 'test-site' for now.`); } } config.cssDistDir = args.cssDistDir || config.cssDistDir || './dist/css'; config.jsDistDir = args.jsDistDir || config.jsDistDir || './dist/js'; config.useSharePoint = args.useSharePoint || config.useSharePoint || false; config.siteCollectionUrl = config.spHost.endsWith('/') ? `${config.spHost}${config.url}` : `${config.spHost}/${config.url}`; config.templatesDir = args.templatesDir || config.templatesDir || './templates'; config.provisioningDir = args.provisioningDir || config.provisioningDir || './deploy'; config.masterPageTemplatesDir = args.masterPageTemplatesDir || config.masterPageTemplatesDir || './templates/masterpages'; config.pageLayoutTemplatesDir = args.pageLayoutTemplatesDir || config.pageLayoutTemplatesDir || './templates/pagelayouts'; config.prototypeDir = args.prototypeDir || config.prototypeDir || './prototype'; config.prototypeTemplatesDir = args.prototypeTemplatesDir || config.prototypeTemplatesDir || './templates/prototypes'; config.siteAssetsDrive = args.siteAssetsDrive || config.siteAssetsDrive; config.prototypeServerUrl = args.prototypeServerUrl || config.prototypeServerUrl || null; config.styleLibraryDrive = args.styleLibraryDrive || config.styleLibraryDrive; config.masterpageCatalogDrive = args.masterpageCatalogDrive || config.masterpageCatalogDrive; if (config.useSharePoint){ if (fs.existsSync(path.resolve(cwd,'./SiteDefinition.json'))){ siteDefinition = require(path.resolve(cwd, './SiteDefinition.json')); } } if (config.styleLibraryDrive){ try { fs.readdirSync(config.styleLibraryDrive+":\\"); }catch(err){ logVerbose('init', 'Style library drive is not connected'); config.styleLibraryDrive = null; } } if (config.siteAssetsDrive) { try { fs.readdirSync(config.siteAssetsDrive + ":\\"); } catch (err) { logVerbose('init', 'Site assets library drive is not connected'); config.siteAssetsDrive = null; } } if (config.masterpageCatalogDrive) { try { fs.readdirSync(config.masterpageCatalogDrive + ":\\"); } catch (err) { logVerbose('init', 'Master page catalog drive is not connected'); config.masterpageCatalogDrive = null; } } } function init(){ if (fs.existsSync(configFilePath)) { config = Object.assign({},config,require(configFilePath)); }else if (fs.existsSync(path.resolve(cwd,'.yo-rc.json'))){ var yoConfig = require(path.resolve(cwd, '.yo-rc.json')); config = Object.assign({}, config, yoConfig["generator-sharepoint-app"]||{}); } updateConfigFromArgs(config,args); initNunjucks(config); if (fs.existsSync(path.resolve(cwd,'webpack.config.js'))){ webpackConfig = require(path.resolve(cwd, 'webpack.config.js')); webpackConfig.output = webpackConfig.output || {}; try{ webpackConfig.output.path = path.resolve(cwd, config.jsDistDir); webpackCompiler = webpack(webpackConfig); }catch(err){ logError('init',`An error has occured while initializing Webpack ${err.message}`) } }else { logError('init', `Could not find webpack configuration file at ${path.resolve(cwd, 'webpack.config.js')}`); } } function log(operation,msg){ console.log(`${colors.magenta(new Date().toISOString())} - ${colors.blue(operation)} - ${colors.gray(msg)}`); } function logVerbose(operation,msg){ if (isVerbose){ console.log(`${colors.magenta(new Date().toISOString())} - ${colors.cyan(operation)} - ${colors.green(msg)}`); } } function logError(operation, msg) { console.log(`${colors.red(new Date().toISOString())} - ${colors.red(operation)} - ${colors.red(msg)}`); } function logWarning(operation,msg){ console.log(`${colors.yellow(new Date().toISOString())} - ${colors.yellow(operation)} - ${colors.yellow(msg)}`); } function getFileNameFromUrl(u){ const uu = new url.URL(u); const vv = uu.pathname.split('/').pop(); return vv; } function getFileData(file){ const fileName = file.path; if (prototypeDefinitionCache[fileName]){ return prototypeDefinitionCache[fileName]; } if ((fileName.indexOf(`${path.sep}prototypes`) !== -1) && (path.extname(fileName) === '.njk' || path.extname(fileName) === '.html')){ let definition = { baseName:path.basename(fileName,path.extname(fileName)), dirName:path.dirname(fileName) }; const fileDefinitionPath = path.resolve(path.dirname(fileName), path.basename(fileName, path.extname(fileName)))+'.json'; if (fs.existsSync(fileDefinitionPath)){ try{ definition = require(fileDefinitionPath); definition.baseName = path.basename(fileName, path.extname(fileName)); definition.dirName = path.dirname(fileName); prototypes.push(definition); }catch(err){ logError(`getFileData`,`Could not load definition for prototype ${err.message}`); } } return prototypeDefinitionCache[fileName] = Object.assign({}, { config, env: process.env, definition, prototypes }); } return { config, env:process.env }; } function getDataForPageLayout(file) { var layouts = (siteDefinition && siteDefinition.pageLayoutDefinitions) || []; var pageLayout = layouts.find((e)=>{ return e.template === path.basename(file.path); }); return { pageLayout, config, spHost:config.spHost, siteCollectionUrl:config.siteCollectionUrl, url:config.url, env: process.env, }; } function getDataForMasterPage(file) { var masterPages = (siteDefinition && siteDefinition.masterPageDefinitions) || []; var masterPage = masterPages.find((e) => { return e.template === path.basename(file.path); }); return { masterPage, config, spHost: config.spHost, siteCollectionUrl: config.siteCollectionUrl, url: config.url , env: process.env }; } function extractSassVariablesFromFolder(folderPath, newLines) { logVerbose('sass:variables', `Attempting to read files at ${folderPath}`); let files = fs.readdirSync(folderPath); let lines = []; logVerbose('sass:variables', `Found ${files.length} files at ${folderPath}`); files.forEach((e) => { if (e.endsWith('.scss')) { let filePath = path.resolve(folderPath, e); logVerbose('sass:variables', `File ${filePath} is a sass file, attempting to read its contents.`); var contents = fs.readFileSync(filePath).toString(); logVerbose('sass:variables', `Read contents of file ${filePath}`); contents.replace(/(\$[^:;@\{\}\(\)]+):([^:;\{\}@]+?);/g, (e, name, val) => { logVerbose('sass:variables', `Found sass variable ${name} with value '${val}'`); lines.push({ type: 'variable', name, val, }); }); if (lines.length) { lines.unshift({ type: 'comment', content: `/** * Variables from file ${e} at ${path.resolve(folderPath, e)} */ ` }); newLines.push(...lines); lines = []; } } }); return newLines; } gulp.task('sass:variables', () => { if (args.noSassVariables){ return false; } logVerbose('sass:variables', `Attempting to extract variables from your sass folders`); const sassFolder = path.resolve(cwd, config.sassDir); if (fs.existsSync(sassFolder)) { logVerbose('sass:variables', `Found sass folder at: ${sassFolder}`); logVerbose('sass:variables', `Attempting to read sass folder contents`); let dirs = fs.readdirSync(path.resolve(cwd, config.sassDir)); logVerbose('sass:variables', `Sass folder contents read successfully, found ${dirs.length} items`) var lines = []; let vars = {}; dirs.forEach((e) => { logVerbose('sass:variables', `Checking if ${path.resolve(cwd, config.sassDir, e)} is a directory`); if (fs.lstatSync(path.resolve(cwd, config.sassDir, e)).isDirectory()) { logVerbose('sass:variables', `${path.resolve(cwd, config.sassDir, e)} is a directory, attempting to read contents`); extractSassVariablesFromFolder(path.resolve(cwd, config.sassDir, e), lines); } }); lines.forEach((e) => { if (e.type === 'variable') { vars[e.name] = e.val; } }); let settingsVariables = {}; let settingsLines = []; if (fs.existsSync(path.resolve(cwd, config.sassDir, './_settings.scss'))) { logVerbose('sass:variables', `Found _settings.scss in the sass directory ${sassFolder}`); let settings = fs.readFileSync(path.resolve(cwd, config.sassDir, './_settings.scss')).toString(); settings.replace(/(\$[^:;@\{\}\)\()]+):([^:;\{\}@\)\()]+);/g, (e, name, val) => { logVerbose('sass:variables', `Found variable in _settings.scss ${name} with value '${val}'`); settingsVariables[name] = val; settingsLines.push({ type: 'variable', name, val }); if (vars[name]) { vars[name] = val; } else if (name.indexOf('color') !== -1) { for (var key in vars) { if (key.indexOf('color') !== -1) { vars[key] = vars[key].replace(new RegExp(val, 'ig'), name).trim(); } } } else if (name.indexOf('font-size') !== -1) { for (var key in vars) { if (key.indexOf('font-size')) { vars[key] = vars[key].replace(new RegExp(val, 'ig'), name).trim(); } } } else if (val.startsWith('#')) { for (var key in vars) { vars[key] = vars[key].replace(new RegExp(val, 'ig'), name).trim(); } } }); } lines.forEach((e) => { if (e.type === 'variable') { e.val = vars[e.name] || e.val; } }); logVerbose('sass:variables', `Writing _extracedvariables.scss file to sass directory ${sassFolder}`); var currentVariables = {}; var currentVariablesLines = []; if (fs.existsSync(path.resolve(cwd, config.sassDir,'./_extractedvariables.scss'))){ let contents = fs.readFileSync(path.resolve(cwd, config.sassDir, './_extractedvariables.scss')).toString(); contents.replace(/(\$[^:;@\{\}\)\()]+):([^:;\{\}@\)\()]+);/g,(c,name,val)=>{ currentVariables[name] = val; currentVariablesLines.push({ type:'variable', name, val }); }); } if (args.withSettings) { fs.writeFileSync(path.resolve(cwd, config.sassDir, './_extractedvariables.scss'), [...settingsLines.map((e) => { if (e.type === 'comment') { return e.content; } else if (e.type === 'variable') { if (currentVariables[e.name] && !args.forceReplace){ var res = `${e.name}:${currentVariables[e.name]};`; delete currentVariables[e.name]; return res; } return `${e.name}:${e.val};` } }), ...lines.map(e => { if (e.type === 'comment') { return e.content; } else if (e.type === 'variable') { if (currentVariables[e.name] && !args.forceReplace) { var res = `${e.name}:${currentVariables[e.name]};`; delete currentVariables[e.name]; return res; } return `${e.name}:${e.val};` } })].join('\n')); } else { fs.writeFileSync(path.resolve(cwd, config.sassDir, './_extractedvariables.scss'), lines.map(e => { if (e.type === 'comment') { return e.content; } else if (e.type === 'variable') { if (currentVariables[e.name] && !args.forceReplace) { var res = `${e.name}:${currentVariables[e.name]};`; delete currentVariables[e.name]; return res; } return `${e.name}:${e.val};`; } }).join('\n')); } } }); gulp.task('sass:compile:debug',['sass:compile'],(cb)=>{ if (config.siteAssetsDrive && isDebug) { logVerbose('sass:compile:debug', `Sass files to be written to mapped drives at ${(path.resolve(config.siteAssetsDrive + ':\\', config.deploymentDir, config.cssDistDir.split('/').pop()))}`); if (fs.existsSync(path.resolve(config.siteAssetsDrive + ':\\', config.deploymentDir, config.cssDistDir.split('/').pop()))) { pump([ gulp.src(path.resolve(cwd, config.cssDistDir, '*.css')), gulp.dest(gulp.dest(path.resolve(config.siteAssetsDrive + ':\\', config.deploymentDir, config.cssDistDir.split('/').pop()))) ], (err) => { if (err) { logError('sass:compile:debug', `An error has occured while writing CSS files to mapped drive ${err.message}`); cb(err); return; } logVerbose('sass:compile:debug', `CSS files written to mapped drive successfully`); cb(); }); } }else { cb(); } }); gulp.task('sass:compile:provisioning', ['sass:compile'], (cb) => { logVerbose('sass:compile:provisioning', `Sass files to be written to provisioning directory at ${(path.resolve(config.provisioningDir, config.deploymentDir, config.cssDistDir.split('/').pop()))}`); pump([ gulp.src(path.resolve(cwd, config.cssDistDir, '*.css')), gulp.dest(path.resolve(cwd,config.provisioningDir,config.deploymentDir)) ],(err)=>{ if (err){ logError('sass:compile:provisioning',`An error has occured while copying files to provisioning directory ${err.message}`); cb(err); return; } cb(); }); }); gulp.task('sass:compile:prototype',['sass:compile'],(cb)=>{ if (isPrototyping) { logVerbose('sass:compile:prototype', `Compiling prototype sass files into prototype output director ${path.resolve(config.prototypeDir, './css')}`); pump([gulp.src(path.resolve(cwd, config.cssDistDir, '*.css')), gulp.dest(path.resolve(cwd, config.prototypeDir, 'css'))], (err) => { if (err){ logError('sass:compile:prototype', `An error has occured while copying css files into prototype directory ${path.resolve(cwd, config.prototypeDir, 'css')}`); } logVerbose('sass:compile:prototype', `Finished copying main css into prototype css directory`); }); logVerbose('sass:compile:prototype', `Attempting to compile prototypes sass files into prototype output director ${path.resolve(config.prototypeDir, './css')}`); pump([ gulp.src([ path.resolve(cwd, config.sassDir, './prototypes/*.scss'), path.resolve(cwd, config.sassDir, './prototypes/**/*.scss')]), sass({ compress: !isDebug, }), autoprefixer(), gulp.dest(path.resolve(cwd, config.prototypeDir, 'css'))], (err) => { if (err) { logError('sass:compile:prototype', `An error has occured while compiling prorotypes sass files: ${err.message}`); } else { logVerbose('sass:compile:prototype', `Finished compiling prototypes sass files`); } cb(err); }); } else { cb(); } }); gulp.task('sass:compile',['sass:variables'],(cb)=>{ logVerbose('sass:compile','compiling sass files'); logVerbose('sass:compile', `Searching for sass files at: ${path.resolve(cwd, config.sassDir, '*.scss')}`); logVerbose('sass:compile', `Searching for sass files at: ${path.resolve(cwd, config.sassDir, '**/*.scss')}`); var sources = [path.resolve(cwd, config.sassDir, '*.scss'), path.resolve(cwd, config.sassDir, '**/*.scss'), '!' + path.resolve(cwd, config.sassDir, './prototypes/**')]; var tasks = [ gulp.src(sources), sass({ compress: !isDebug }), autoprefixer(), gulp.dest(path.resolve(cwd, config.cssDistDir)) ]; logVerbose('sass:compile', `Sass files will be written to ${path.resolve(cwd, config.cssDistDir)}`); pump(tasks,(err)=>{ if (err){ logError('sass:compile',`an error has occured while compiling sass files ${err.message}`); cb(err); return; } cb(); logVerbose('sass:compile',`finished compiling sass files successfully.`); }); }); gulp.task('sass:watch', (cb) => { logVerbose('sass:watch', `Watching for sass file changes on ${path.resolve(cwd, config.sassDir, './*.scss')}`); logVerbose('sass:watch', `Watching for sass file changes on ${path.resolve(cwd, config.sassDir, './**/*.scss')}`); var tasks = []; if (isPrototyping){ tasks.push('sass:compile:prototype'); } if (isDebug && config.siteAssetsDrive){ if (fs.existsSync(`${config.siteAssetsDrive}:\\`)){ tasks.push('sass:compile:debug'); } } if (config.useSharePoint){ tasks.push('sass:compile:provisioning'); } if (tasks.length === 0){ tasks.push('sass:compile'); } gulp.watch([ path.resolve(cwd, config.sassDir,'./*.scss'), path.resolve(cwd, config.sassDir, './**/*.scss')], tasks); }); gulp.task('lib:download',(cb)=>{ mkdirp(path.resolve(cwd, `${config.libDir}`),(err)=>{ if (err){ logError('lib:download', `An error has occured while creating lib folder at ${config.libDir}: ${err.message}`); cb(err); return; } logVerbose('lib:download','downloading library files'); var files = config.cdn.filter((fU) => { if (!args.force) { return !fs.existsSync(path.resolve(cwd, config.libDir, getFileNameFromUrl(fU))) } return true; }); var count = 0; if (!files.length){ cb(); return; } function done(fileUrl){ count++; logVerbose('lib:download', `finished downloading library file ${fileUrl}`); if (count === files.length){ logVerbose('lib:download', 'downloading libraries finished successfully'); cb(); } } files.map((fileUrl)=>{ logVerbose('lib:download', `downloading library file ${fileUrl}`); pump([ request(fileUrl), fs.createWriteStream(path.resolve(cwd, config.libDir, getFileNameFromUrl(fileUrl))) ],(err)=>{ if (err){ logError('lib:download', `an error has occured while downloading file ${fileUrl}. ${err.message}`); } done(fileUrl); }); }); }); }); gulp.task('lib:compile:js',(cb)=>{ logVerbose('lib:compile:js', 'compiling library js files'); let files = config.cdn.map((fileUrl) => { return getFileNameFromUrl(fileUrl) }) .filter((e) => { return path.extname(e) === '.js'; }) .map((e)=>{ return path.resolve(cwd,config.libDir,e); }); if (!files.length){ cb(); return; } return pump( [gulp.src(files), concat('vendor.js'), gulp.dest(path.resolve(cwd,config.jsDistDir))], (err)=>{ if (err){ logError('lib:compile:js', `an error has occured while compiling library js files ${err.message}`); cb(err); return; } if (isPrototyping){ logVerbose('lib:compile:js',`Protoyping mode detected, copying vendor files into prototype folder`); pump( [gulp.src(path.resolve(cwd,config.jsDistDir,'*.js')), gulp.dest(path.resolve(cwd,config.prototypeDir,'js'))],(err)=>{ if (err){ logError('lib:compile:js', `An error has occured while copying vendor files into prototype directory: ${err.message}`); cb(err); return; } logVerbose('lib:compile:js',`Finished copying vendor js files into prototype directory`); logVerbose('lib:compile:js', 'Finished compiling library js files successfully'); } ) }else { logVerbose('lib:compile:js','Finished compiling library js files successfully'); } } ); }); gulp.task('lib:compile:css', (cb) => { logVerbose('lib:compile:css', 'compiling library css files'); return pump( [gulp.src(config.cdn.map((fileUrl) => { return getFileNameFromUrl(fileUrl) }) .filter((e) => { return path.extname(e) === '.css'; })), concat('vendor.css'), gulp.dest(path.resolve(cwd, config.cssDistDir))], (err) => { if (err) { logError('lib:compile:css', `an error has occured while compiling library css files ${err.message}`); cb(err); return; } logVerbose('lib:compile:css', 'Finished compiling library css files successfully'); } ); }); gulp.task('masterpages:compile',(cb)=>{ const fullPath = path.resolve(cwd, config.masterPageTemplatesDir); mkdirp(fullPath,(err)=>{ if (err){ logError('masterpages:compile', `Could not create folder ${fullPath}`); cb(err); return; } logVerbose('masterpages:compile', 'Started compiling master page templates'); logVerbose('masterpages:compile', `Loading master page templates ('.master','.njk') from ${path.resolve(cwd, config.masterPageTemplatesDir)}`); var tasks = [gulp.src([path.resolve(cwd, config.masterPageTemplatesDir, '*.master'), path.resolve(cwd, config.masterPageTemplatesDir, '*.njk'), path.resolve(cwd, config.masterPageTemplatesDir, '**/*.master'), path.resolve(cwd, config.masterPageTemplatesDir, '**/*.njk')]), data(getDataForMasterPage), nunjucksTask.compile({ config },{env:nunjucksEngine}), rename({ extname: '.master' }), gulp.dest(path.resolve(cwd, config.provisioningDir))]; pump( tasks, (err) => { if (err) { logError('masterpages:compile', `An error has occured while compiling master page templates ${err.message}`); cb(err); return; } if (isDebug && config.masterpageCatalogDrive) { logVerbose('masterpages:compile', 'Adding masterpage catalog drive as a destination'); pump([ gulp.src(path.resolve(cwd, config.provisioningDir,'*.master')), gulp.dest(`${config.masterpageCatalogDrive}:\\`) ],(err)=>{ if (err){ logError('masterpages:compile',`Failed to copy master pages to mapped drived ${config.masterpageCatalogDrive}: ${err.message}`); cb(err); return; } logVerbose('masterpages:compile',`Master pages copied to mapped drive successfuly ${config.masterpageCatalogDrive}`); cb(); }); }else { logVerbose(`masterpages:compile`, `Finished compiling master page templates`); cb(err); } } ); }); }); gulp.task('masterpages:watch', (cb) => { logVerbose('masterpages:watch', 'Started watch for master page templates'); gulp.watch([path.resolve(cwd, config.masterPageTemplatesDir, './*.master'), path.resolve(cwd, config.masterPageTemplatesDir, './**/*.master'), path.resolve(cwd, config.masterPageTemplatesDir, './*.njk'), path.resolve(cwd, config.masterPageTemplatesDir, './**/*.njk') ], ['masterpages:compile']); }); gulp.task('pagelayouts:compile', (cb) => { const fullPath = path.resolve(cwd, config.pageLayoutTemplatesDir); mkdirp(fullPath,(err)=>{ if (err) { logError('pagelayouts:compile', `Could not create folder ${fullPath}`); cb(err); return; } logVerbose('pagelayouts:compile', 'Started compiling page layout templates'); var tasks = [gulp.src([path.resolve(cwd, config.pageLayoutTemplatesDir, '*.aspx'), path.resolve(cwd, config.pageLayoutTemplatesDir, '*.njk'), path.resolve(cwd, config.pageLayoutTemplatesDir, '**/*.aspx'), path.resolve(cwd, config.pageLayoutTemplatesDir, '**/*.njk')]), data(getDataForPageLayout), nunjucksTask.compile({ config }, { env: nunjucksEngine }), rename({ extname:'.aspx' }), gulp.dest(path.resolve(cwd, config.provisioningDir))]; pump( tasks, (err) => { if (err) { logError('pagelayouts:compile', `An error has occured while compiling page layout templates ${err.message}`); cb(err); return; } if (isDebug && config.masterpageCatalogDrive) { logVerbose('pagelayouts:compile', `Attempting to copy page layouts to mapped drive: ${config.masterpageCatalogDrive}`); if (fs.existsSync(config.masterpageCatalogDrive+':\\')){ pump([ gulp.src(path.resolve(config.provisioningDir,'*.aspx')), gulp.dest(`${config.masterpageCatalogDrive}:\\`) ],(err)=>{ if (err){ logError('pagelayouts:compile',`An error has occured while copying your page layouts to mapped drive ${config.masterpageCatalogDrive}: ${err.message}`); cb(err); return; } logVerbose('pagelayouts:compile',`Page layouts have been copied to mapped drive ${config.masterpageCatalogDrive} successfully.`); cb(); }) }else { logWarning('pagelayouts:compile',`Master page catalog drive (${config.masterpageCatalogDrive}:\\) is not connected`); } }else { logVerbose(`pagelayouts:compile`, `Finished compiling page layout templates`); cb(err); } } ); }) }); gulp.task('pagelayouts:watch', (cb) => { logVerbose('pagelayouts:watch','Started watch for page layout templates'); const paths = [path.resolve(cwd, config.pageLayoutTemplatesDir, './*.aspx'), path.resolve(cwd, config.pageLayoutTemplatesDir, './**/*.aspx'), path.resolve(cwd, config.pageLayoutTemplatesDir, './*.njk'), path.resolve(cwd, config.pageLayoutTemplatesDir, './**/*.njk')]; logVerbose('pagelayouts:watch',`Watching files on: ${paths.join('\t')}`); gulp.watch(paths, ['pagelayouts:compile']); }); gulp.task('js:compile:prototype',['js:compile'],(cb)=>{ if (isPrototyping) { logVerbose('js:compile:prototype', 'Attempting to copy compiled JavaScript files into prototype directory'); pump([ gulp.src([path.resolve(cwd, config.jsDistDir, '*.js'), path.resolve(cwd, config.jsDistDir, '**/*.js')]), gulp.dest(path.resolve(cwd, config.prototypeDir, config.jsDistDir.split(/[\\\/]/g).pop())) ], (err) => { if (err) { logError('js:compile:prototype', `Could not copy files to prototye directory: ${err.message}`); return; } else { logVerbose('js:compile:prototype', 'Finished copying JavaScript files into prototype directory'); } cb(); }); return; } cb(); }); gulp.task('js:compile:debug',['js:compile'],(cb)=>{ if (config.siteAssetsDrive && isDebug) { logVerbose('js:compile:debug',`Writing files to Site Assets mapped drive`); pump([gulp.src([path.resolve(cwd, `${config.jsDistDir}/*.js`), [path.resolve(cwd, `${config.distDir}/*.js`)]]), gulp.dest(path.resolve(config.siteAssetsDrive + ':\\', config.deploymentDir, config.jsDistDir.split(/[\\\/]/g).pop()))], (err) => { if (err) { logError('js:compile:debug', `An error has occured while writing js files to Site Assets ${path.resolve(config.siteAssetsDrive + ':\\', config.deploymentDir, config.jsDistDir.split(/[\\\/]/g).pop())}`); cb(err); return; } logVerbose('js:compile:debug', `Finished writing js files to Site Assets ${path.resolve(config.siteAssetsDrive + ':\\', config.deploymentDir, config.jsDistDir.split(/[\\\/]/g).pop())}`); cb(); }); return; } cb(); }); gulp.task('js:compile:provisioning', ['js:compile'], (cb) => { const outputPath = path.resolve(cwd, config.provisioningDir, config.deploymentDir, config.jsDistDir.split(/[\\\/]/g).pop()); logVerbose('js:compile:provisioning',`Writing js files to provisioning directory ${outputPath}`); pump([ gulp.src([path.resolve(cwd, `${config.jsDistDir}/*.js`)]), gulp.dest(path.resolve(outputPath)) ], (err) => { if (err) { logError('js:compile:provisioning', `An error has occured while writing js files to provisioning directory at: ${outputPath}`); cb(err); return; } logVerbose('js:compile:provisioning', `Finished writing js files to provisioning directory at: ${outputPath}`); cb(); }); }); gulp.task('js:compile',(cb)=>{ logVerbose('js:compile','Starting to compile JavaScript files'); const newConfig = { ...webpackConfig }; newConfig.entry = { ...webpackConfig.entry }; logVerbose('js:compile', 'Starting to compile JavaScript files'); const files = Object.keys(webpackConfig.entry); logVerbose('js:compile','Checking if any of the JS source files does not need to be compiled'); files.forEach((e) => { let contents = fs.readFileSync(webpackConfig.entry[e]).toString(); if (/\/\/\/[\s]*nocompile[\s]*:[\s]*true/gi.test(contents)) { logVerbose('js:compile', `Entry ${e} for file ${newConfig.entry[e]} has been removed`); delete newConfig.entry[e]; } }); webpack(newConfig,(err,stats)=>{ if (err){ cb(err); logError('js:compile', `An error has occured while compiling JavaScript files: ${err.message}`); }else{ if (stats.hasErrors()){ logError('js:compile', `Compile stats have errors: ${stats.toString()}`); } logVerbose('js:compile','Finished compiling JavaScript files'); cb(); } }); }); gulp.task('js:watch',(cb)=>{ var tasks = []; if (isPrototyping){ tasks.push('js:compile:prototype'); } if (isDebug && config.siteAssetsDrive){ if (fs.existsSync(`${config.siteAssetsDrive}:\\`)){ tasks.push('js:compile:debug'); } } if (config.useSharePoint){ tasks.push('js:compile:provisioning'); } if (tasks.length === 0){ tasks.push('js:compile'); } return gulp.watch([ path.resolve(cwd,'./src/*.ts'), path.resolve(cwd, './src/**/*.ts'), path.resolve(cwd, './src/*.tsx'), path.resolve(cwd, './src/**/*.tsx') ],['js:compile']); }); gulp.task('resources:compile',()=>{ return gulp.src([ path.resolve(cwd,`${config.resourcesDir||'./resources'}/*.resx`), path.resolve(cwd,`${config.resourcesDir||'./resources'}/*.spfont`), path.resolve(cwd,`${config.resourcesDir||'./resources'}/*.spcolor`), path.resolve(cwd,`${config.resourcesDir||'./resources'}/**/*.resx`), path.resolve(cwd,`${config.resourcesDir||'./resources'}/**/*.spfont`), path.resolve(cwd,`${config.resourcesDir||'./resources'}/**/*.spcolor`)]) .pipe(gulp.dest(path.resolve(cwd,`${config.provisioningDir || './deploy'}`))); }); gulp.task('config:init',(cb)=>{ logVerbose('config:init', `Creating configuration file at ${path.resolve(cwd, './config.json')}`); fs.writeFile(path.resolve(cwd,'./config.json'),JSON.stringify(config),(err)=>{ if (err){ logError('config:init', `Could not create configuration file at ${path.resolve(cwd, './config.json')}: ${err.message}`); cb(err); return; } logVerbose('config:init',`Created configuration file at ${path.resolve(cwd,'./config.json')}`); }); }); gulp.task('watch',['js:watch','sass:watch','pagelayouts:watch','masterpages:watch']); gulp.task('prototype:compile', (cb) => { const fullPath = path.resolve(cwd, config.masterPageTemplatesDir); mkdirp(fullPath, (err) => { if (err) { logError('prototype:compile', `Could not create folder ${fullPath}`); cb(err); return; } logVerbose('prototype:compile', 'Started compiling prototype templates'); logVerbose('prototype:compile', `Loading prototype templates ('.html','.njk') from ${path.resolve(cwd, config.prototypeTemplatesDir)}`); var tasks = [gulp.src([path.resolve(cwd, config.prototypeTemplatesDir, '*.html'), path.resolve(cwd, config.prototypeTemplatesDir, '*.njk'), path.resolve(cwd, config.prototypeTemplatesDir, '**/*.html'), path.resolve(cwd, config.prototypeTemplatesDir, '**/*.njk')]), data(getFileData), nunjucksTask.compile({ config },{ name:file=>`${path.basename(file.name,path.extname(file.name))}.html` }), rename({ extname:'.html' }), gulp.dest(path.resolve(cwd, config.prototypeDir))]; pump( tasks, (err) => { if (err) { logError('prototype:compile', `An error has occured while compiling prototype templates ${err.message}`); cb(err); return; } logVerbose(`prototype:compile`, `Finished compiling prototype templates`); cb(err); } ); }); }); gulp.task('assets:build:prototype',(cb)=>{ if (isPrototyping) { logVerbose('assets:build:prototype', `Prototyping mode detected`); logVerbose('assets:build:prototype', `Attempting to copy assets to prototyping directory: ${path.resolve(cwd, config.prototypeDir, 'assets')}`); pump([gulp.src(AssetSources.map(e=>{ logVerbose('assets:build:prototyping', `Including files in ${path.resolve(cwd, config.assetsDir, e)}`); return path.resolve(cwd,config.assetsDir,e) })), gulp.dest(path.resolve(cwd, config.prototypeDir,'assets'))], (err) => { if (err) { logError('assets:build:prototype', `An error has occured while copying files to prototype assets folder: ${err.message}`); cb(err); return; } cb(); logVerbose('assets:build:prototype', `Finished building assets folder for prototypes at: ${path.resolve(config.prototypeDir, 'assets')}`); }); return; } cb(); }); gulp.task('assets:build:sharepoint',(cb)=>{ if (config.useSharePoint) { logVerbose(`assets:build`, `SharePoint mode detected`); if (config.siteAssetsDrive && fs.existsSync(`${config.siteAssetsDrive}:\\`)) { logVerbose('assets:build', `SharePoint SiteAssets drive detected and is connected`); logVerbose('assets:build', `Attempting to copy assets to the deployment directory ${config.deploymentDir}\\assets at ${config.siteAssetsDrive}:\\`); pump([ gulp.src(AssetSources.map(e => path.resolve(cwd, config.assetsDir, e))), gulp.dest(`${config.siteAssetsDrive}:\\${config.deploymentDir}\\assets`) ], (err) => { if (err) { cb(err); logError('assets:build', `An error has occured while copy files to mapped drive at ${config.siteAssetsDrive}:\\${config.deploymentDir}\\assets: ${err.message}`); } logVerbose('assets:build', `Finished copying assets to mapped drive successfully`); }); return; }else { logWarning('assets:build',`Either the siteAssets mapped drive is missing or it is not connected`); cb(); return; } } cb(); }); gulp.task('assets:build:dist',(cb)=>{ logVerbose('assets:build:dist',`Copying asset files to dist folder ${config.distDir}\\assets`); pump([gulp.src(AssetSources.map(e => path.resolve(cwd, config.assetsDir, e))), gulp.dest(path.resolve(config.distDir,'assets'))], (err) => { if (err){ logError(`assets:build:dist`,`An error has occured while copying assets to dist folder at ${path.resolve(cwd,config.distDir)}: ${err.message}`); cb(err); return; } logVerbose('assets:build:dist', `Finished copying assets to dist directory ${path.resolve(cwd, config.distDir,'assets')}`); cb(); }); }); gulp.task('assets:build',['assets:build:prototype','assets:build:dist','assets:build:sharepoint'],(cb)=>{ logVerbose(`assets:build`,`Copying asset files`); pump([gulp.src(AssetSources.map(e => path.resolve(cwd, config.assetsDir, e))), gulp.dest(path.resolve(config.provisioningDir,config.deploymentDir,'assets'))],(err)=>{ if (err){ logError(`assets:build`,`An error has occured while copying files to provision directory assets folder: ${err.message}`); cb(err); return; } logVerbose('assets:build', `Finished building assets folder to provision directory at: ${path.resolve(config.provisioningDir, config.deploymentDir, 'assets')}`); cb(); }); }); gulp.task('prototype:watch',['prototype:compile'],()=>{ let inputDir = args.inputDir || `${config.templatesDir}`; gulp.watch([ path.resolve(inputDir, '*.njk'), path.resolve(inputDir, '**/*.njk'), path.resolve(inputDir, '*.html'), path.resolve(inputDir, '**/*.html') ], ['prototype:compile']); }); function getPrototypeDefinitions(){ let prototypesTemplateDir = path.resolve(cwd,config.prototypeTemplatesDir); if (fs.existsSync(prototypesTemplateDir)){ gulp.src(['*.json','**/*.json'].map(e=>path.resolve(prototypesTemplateDir,e))) .pipe() } } gulp.task('prototype',(cb)=>{ try{ isPrototyping = true; let express = require('express'); let staticFiles = require('serve-static'); outputDir = args.output || `${config.prototypeDir}`; let inputDir = args.inputDir || `${config.templatesDir}`; let port = args.port || 6565; const prototypesSrcDir = path.resolve(cwd, config.srcDir, './prototypes'); logVerbose('Prototyping', `In prototype mode. Attempting to add prototype source files from ${prototypesSrcDir}`); try { if (fs.existsSync(prototypesSrcDir)) { logVerbose('Prototyping', `Prototypes directory ${prototypesSrcDir} exists. Attempting to directory contents`); let prototypes = fs.readdirSync(prototypesSrcDir); prototypes = prototypes.filter((e) => path.extname(e) === '.ts' || path.extname(e) === '.tsx'); logVerbose('Prototyping', `Prototypes directory content read successfully in total ${prototypes.length} source files`); prototypes.forEach((file) => { logVerbose('Prototyping', `Adding prototype source file ${file} to webpack configuration object`); webpackConfig.entry[path.basename(file,path.extname(file))] = path.resolve(prototypesSrcDir, file); }); webpackCompiler = webpack(webpackConfig); } } catch (err) { logError(`Prototyping`, `An error has occured while reading prototypes directory ${prototypesSrcDir}. Not compiling any prototype source files: ${err.message}`); } function done(err) { if (err) { logError(`Prototyping`, `An error has occured while creating prototype folder at ${path.resolve(cwd, inputDir)}: ${err.message}`); cb(); return; } if (gulp.parallel){ gulp.parallel('lib:download','assets:build'); gulp.parallel('lib:compile:js','lib:compile:css'); gulp.parallel('prototype:watch','js:watch','sass:watch'); } else if (gulp.start) { gulp.start('lib:download'); gulp.start('assets:build'); gulp.start('lib:compile:js'); gulp.start('lib:compile:css'); gulp.start('prototype:watch'); gulp.start('js:watch'); gulp.start('sass:watch'); } let app = express(); app.use(staticFiles(path.resolve(outputDir))); if (fs.existsSync(path.resolve(cwd,'./extension/index.js'))){ require(path.resolve(cwd, './extension/index.js'))(app,config); } app.listen(port,()=>{ log('Prototyping',`Prototyping server has started on port ${port}. You can access the server on http://localhost:${port}`); }); } if (fs.existsSync(path.resolve(cwd, inputDir))) { mkdirp(path.resolve(cwd, inputDir), done); } else { done(null); } }catch(err){ console.log(`Prototyping`,`Could not start prototyping server because of missing depenendencies: ${err.message}`) } }); gulp.task('prototype:upload',(cb)=>{ uploadingPrototypes = true; const gzip = require('gulp-gzip'), tar = require('gulp-tar'); if (!config.prototypeServerUrl){ logError('prototype:upload',`Cannot upload your prototypes, no prototyep server has been setup. Use 'yo sharepoint-app' to setup the prototype server`); cb(); return; } fs.writeFileSync(path.resolve(cwd,config.prototypeDir,'project.json'),JSON.stringify(config,null,' ')); logVerbose('prototype:upload', `Attempting to zip your prototypes to: ${path.resolve(cwd, './zip')}`); pump([ gulp.src([`./${config.prototypeDir}/**`]), tar(`${config.name}.tar`), gzip(), gulp.dest(path.resolve(cwd,'./zip')) ],(err)=>{ if (err){ logError('prototype:upload', `Could not zip your prototypes to ${path.resolve(cwd, './zip')} because: ${err.message}`); cb(err); return; } logVerbose('prototype:upload', `Attempting to upload your prototypes to: ${config.prototypeServerUrl}`); const zipFileName = path.resolve(cwd,'./zip',`${config.name}.tar.gz`); const readStream = fs.createReadStream(zipFileName); request.post({ url: config.prototypeServerUrl + `${config.prototypeServerUrl.endsWith('/')?'':'/'}upload`, formData:{ projectName:config.name, prototypes:readStream } },(err)=>{ if (err){ logError('prototype:upload',`Could not upload your prototypes to ${config.prototypeServerUrl} because: ${err.message}`); cb(e