import * as fs from 'fs'; import * as process from 'process'; import * as rimraf from 'rimraf'; import { ncp } from 'ncp'; import * as replaceStream from 'replacestream'; import * as watch from 'node-watch'; import { runWebpackAzureFunction, runWebpackClient } from './run-webpack'; const isProduction = process.argv.some(x => x.match(/--prod/) != null); const isClient = process.argv.some(x => x.match(/--client/) != null); const shouldInject = process.argv.some(x => x.match(/--debug/) != null); const iSrcPathName = process.argv.map((x, i) => { if (x.match(/--src/) != null) { return i; } }).filter(x => x != null)[0]; const srcPathName = iSrcPathName != null ? process.argv[iSrcPathName + 1] : 'lib'; // declare var require: any; // declare var __dirname: string; // declare var process: { argv: string[] }; // const ncp = require('ncp').ncp; // const fs = require('fs'); // const replaceStream = require('replacestream'); // const watch = require('node-watch'); // const rimraf = require('rimraf'); function getBoilerplatePath(boilerplateResourcePath: string) { // return __dirname.replace(/(\\|\/)src-cli$/, '').replace( /(\\|\/)lib$/, '') + boilerplateResourcePath; return __dirname.replace(/(\\|\/)src-cli$/, '').replace(new RegExp(`(\\\\|\\/)${srcPathName}$`), '') + boilerplateResourcePath; } function delay(time = 1000) { return new Promise(resolve => setTimeout(() => resolve(), time)); } let timeoutId: any; let isRunning = false; function keepAlive() { clearTimeout(timeoutId); timeoutId = setTimeout(() => { isRunning = false; }, 30000); } function waitUntilReady() { return new Promise(resolve => { let id = setInterval(() => { if (!isRunning) { clearInterval(id); resolve(); } }, 1000); }); } async function createDeployment() { console.log(`src=${srcPathName}`); await waitUntilReady(); isRunning = true; keepAlive(); try { let functionDirsOrFiles: string[] = []; let entrySourceFiles: string[] = []; let pending = 0; let ready = () => { keepAlive(); pending--; if (pending > 0) { // console.log('Webpack Not Ready:', webpackPending, functionDirs); return; } (async () => { // Create Test Main await new Promise((resolve, reject) => { console.log('Create Test Main'); try { // Function Runner let functionNames = functionDirsOrFiles .filter(x => x.length > 0 && x.indexOf('.js') < 0) .map(x => x.replace('./deployment/', '')); functionNames.sort(); let functions = functionNames .map(x => `{name: '${x}', main: require('${`./../../${srcPathName}/src-server/` + x}').main }`) .join(',\n\t'); console.log('Create Test Main'); fs.mkdirSync('deployment/test-main'); fs.createReadStream(getBoilerplatePath('/resources/test-main-RESOURCES/test-main.js')) .on('end', () => resolve()) .pipe(replaceStream(afsLibPath, targetAfsLibPath)) .pipe(replaceStream('FUNCTION_MODULES', functions)) .pipe(fs.createWriteStream('./deployment/test-main/test-main.source.js')); functionDirsOrFiles.push('./deployment/test-main/test-main.source.js'); // Index let functionsLinks = functionNames .map(x => `${x}
`) .join('\n\t'); console.log('Create Test Main index.html'); fs.createReadStream(getBoilerplatePath('/resources/test-main-RESOURCES/index.html')) .on('end', () => resolve()) .pipe(replaceStream('FUNCTION_LINKS', functionsLinks)) .pipe(fs.createWriteStream('./deployment/resources/test-main.html')); } catch (err) { console.error(err); resolve(); } }); if (isProduction || isClient) { // Wabpack console.log('Webpack'); await runWebpackClient(entrySourceFiles, shouldInject); if (isProduction) { await runWebpackAzureFunction(functionDirsOrFiles, shouldInject); } } console.log('DONE'); })().then(); }; pending++; console.log('Create Deployment'); // Clean Directory try { console.log('Clean Deployment Folder'); if (fs.existsSync('deployment')) { rimraf.sync('deployment'); } console.log('Make Deployment Folders'); fs.mkdirSync('deployment'); // fs.mkdirSync('deployment/lib'); fs.mkdirSync('deployment/resources'); } catch (err) { // Debugger might prevent this } // Not Needed with Webpack // // Copy package.json // webpackPending++; // ncp('./package.json', './deployment/package.json', (err: any) => { // if (err) { // console.error(err); // } // console.log('Copied "package.json" to "deployment"'); // readyForWebpack(); // }); // Don't copy lib folder (instead change to point to lib folder) // // Copy lib folder // webpackPending++; // ncp('./lib', './deployment/lib', (err: any) => { // if (err) { console.error(err); } // console.log('Copied "lib/" to "deployment/lib/"'); // readyForWebpack(); // }); // Copy resources folder pending++; ncp('./resources', './deployment/resources', (err: any) => { if (err) { console.error(err); } console.log('Copied "resources/" to "deployment/resources"'); ready(); }); // Handle Local Run let afsLibPath = '@told/azure-functions-server/lib'; let targetAfsLibPath = afsLibPath; try { require(afsLibPath); } catch (err) { console.log('LOCAL RUN'); targetAfsLibPath = './../../lib'; } // TEST // await delay(25000); // Create Function Entries let serverDir = './src-server'; fs.readdir(serverDir, (err: any, files: string[]) => { if (err) { console.error(err); } let tsFiles = files.filter(x => x.indexOf('.ts') >= 0); pending += tsFiles.length; for (let i = 0; i < tsFiles.length; i++) { let f = tsFiles[i]; let path = serverDir + '/' + f; let stream = fs.readFile(path, 'utf8', (err: any, data: string) => { if (err) { console.error(err); } // Http Responses if (data.match(/export\s+async\s+function\s+main\s*\(/)) { console.log('src-server main file: ', f); let functionName = f.replace('.ts', ''); let functionDir = './deployment/' + functionName; let boilerplatePath = '/resources/function-BOILERPLATE'; if (f.match(/^default\./)) { boilerplatePath = '/resources/default-BOILERPLATE'; } // Clone the function-BOILERPLATE folder let functionBoilerplateDir = getBoilerplatePath(boilerplatePath); ncp(functionBoilerplateDir, functionDir, { transform: (read: any, write: any) => { read // Replace Function Name .pipe(replaceStream('FUNCTION_NAME', functionName)) .pipe(replaceStream(afsLibPath, targetAfsLibPath)) .pipe(write); } }, (err: any) => { if (err) { console.error(err); } console.log('Created Function Boilerplate for ' + functionName); }); // Ready for Webpack functionDirsOrFiles.push(functionDir); ready(); } // Timers else if (data.match(/export\s+async\s+function\s+tick\s*\(/)) { console.log('src-server tick file: ', f); let functionName = f.replace('.ts', ''); let functionDir = './deployment/' + functionName; let schedule = '0 */5 * * * *'; let targetSchedule = '0 */5 * * * *'; let scheduleRegex = /\/\/\s*schedule:((?:\s+(?:\*|\*\/[0-9]+|[0-9]+)){6})/; let mSchedule = data.match(scheduleRegex); if (mSchedule) { targetSchedule = mSchedule[1].trim(); } // Clone the function-BOILERPLATE folder let functionBoilerplateDir = getBoilerplatePath('/resources/timer-BOILERPLATE'); ncp(functionBoilerplateDir, functionDir, { transform: (read: any, write: any) => { read // Replace Function Name .pipe(replaceStream('FUNCTION_NAME', functionName)) .pipe(replaceStream(afsLibPath, targetAfsLibPath)) .pipe(replaceStream(schedule, targetSchedule)) .pipe(write); } }, (err: any) => { if (err) { console.error(err); } console.log('Created Function Boilerplate for ' + functionName); }); // Ready for Webpack functionDirsOrFiles.push(functionDir); ready(); } else { ready(); } }); } }); // Create Client Entries let clientDir = './src-client'; fs.readdir(clientDir, (err: any, files: string[]) => { if (err) { console.error(err); } let tsFiles = files.filter(x => x.indexOf('.ts') >= 0); pending += tsFiles.length; for (let i = 0; i < tsFiles.length; i++) { let f = tsFiles[i]; let path = clientDir + '/' + f; let stream = fs.readFile(path, 'utf8', (err: any, data: string) => { if (err) { console.error(err); } // Client // Must have setup(); at end of file if (data.match(/\n\s*setup\s*\(\s*\);\s*$/)) { console.log('src-client entry file: ', f); let entryName = f.replace('.ts', ''); let boilerplatePath = getBoilerplatePath('/resources/client-BOILERPLATE/ENTRY_NAME.source.js'); let targetPath = `./deployment/resources/${entryName}.source.js`; try { fs.createReadStream(boilerplatePath) // Replace Entry Name .pipe(replaceStream('ENTRY_NAME', entryName)) .pipe(replaceStream(afsLibPath, targetAfsLibPath)) .pipe(fs.createWriteStream(targetPath)); console.log('Created Client Boilerplate for ' + entryName); } catch (err) { console.error(err); } // Ready for Webpack entrySourceFiles.push(targetPath); ready(); } else { ready(); } }); } }); ready(); } catch (err) { console.error('Uncaught Exception: ', err); } isRunning = false; } createDeployment().then(); let timerId: any; function runWhenReady() { // Let TSC finish console.log('.'); clearInterval(timerId); timerId = setInterval(() => { if (!isRunning) { clearInterval(timerId); console.log('---'); createDeployment().then(); } else { console.log('.nr.'); } }, 1000); } if (process.argv.filter(x => x === '-w').length > 0) { watch([`./${srcPathName}`, './resources', './package.json'], () => runWhenReady()); }