From 58f92373bd930a7eaab2fe49a2c4019c7169b1c2 Mon Sep 17 00:00:00 2001 From: Daniel Hillmann Date: Wed, 12 Jul 2017 15:01:56 +0200 Subject: [PATCH] Build project --- .gitignore | 4 +- .../lib/createDirectoryResourceProvider.js | 208 +++++++++++ .../lib/createScriptManager.js | 32 ++ .../lib/getShortDescription.js | 83 +++++ .../lib/index.js | 18 + .../lib/renderDirectoryResource.js | 46 +++ .../lib/renderDirectoryResourceCss.js | 69 ++++ .../lib/renderDirectoryResourceHtml.js | 109 ++++++ .../lib/createFileResourceProvider.js | 123 +++++++ .../nocms-plugin-file-resources/lib/index.js | 26 ++ .../lib/createImageResourceProvider.js | 171 +++++++++ .../nocms-plugin-image-resources/lib/index.js | 18 + .../lib/createJsonResourceProvider.js | 118 +++++++ .../nocms-plugin-json-resources/lib/index.js | 18 + packages/nocms/bin/nocms-worker.js | 155 ++++++++ packages/nocms/bin/nocms.js | 184 ++++++++++ packages/nocms/lib/config.js | 42 +++ .../createCachingDecoratedResourceProvider.js | 42 +++ .../lib/createCompositeResourceProvider.js | 50 +++ .../createLoggingDecoratedResourceProvider.js | 44 +++ .../lib/createPluginActivationContext.js | 22 ++ .../lib/createRegisterResourceProvider.js | 11 + packages/nocms/lib/createResourceMap.js | 22 ++ packages/nocms/lib/createResourceTree.js | 36 ++ packages/nocms/lib/debug.js | 16 + packages/nocms/lib/io.js | 241 +++++++++++++ packages/nocms/lib/nocmsAscii.js | 17 + packages/nocms/lib/plugins.js | 44 +++ packages/nocms/lib/server.js | 74 ++++ packages/nocms/lib/threading.js | 331 ++++++++++++++++++ 30 files changed, 2371 insertions(+), 3 deletions(-) create mode 100644 packages/nocms-plugin-directory-resources/lib/createDirectoryResourceProvider.js create mode 100644 packages/nocms-plugin-directory-resources/lib/createScriptManager.js create mode 100644 packages/nocms-plugin-directory-resources/lib/getShortDescription.js create mode 100644 packages/nocms-plugin-directory-resources/lib/index.js create mode 100644 packages/nocms-plugin-directory-resources/lib/renderDirectoryResource.js create mode 100644 packages/nocms-plugin-directory-resources/lib/renderDirectoryResourceCss.js create mode 100644 packages/nocms-plugin-directory-resources/lib/renderDirectoryResourceHtml.js create mode 100644 packages/nocms-plugin-file-resources/lib/createFileResourceProvider.js create mode 100644 packages/nocms-plugin-file-resources/lib/index.js create mode 100644 packages/nocms-plugin-image-resources/lib/createImageResourceProvider.js create mode 100644 packages/nocms-plugin-image-resources/lib/index.js create mode 100644 packages/nocms-plugin-json-resources/lib/createJsonResourceProvider.js create mode 100644 packages/nocms-plugin-json-resources/lib/index.js create mode 100644 packages/nocms/bin/nocms-worker.js create mode 100644 packages/nocms/bin/nocms.js create mode 100644 packages/nocms/lib/config.js create mode 100644 packages/nocms/lib/createCachingDecoratedResourceProvider.js create mode 100644 packages/nocms/lib/createCompositeResourceProvider.js create mode 100644 packages/nocms/lib/createLoggingDecoratedResourceProvider.js create mode 100644 packages/nocms/lib/createPluginActivationContext.js create mode 100644 packages/nocms/lib/createRegisterResourceProvider.js create mode 100644 packages/nocms/lib/createResourceMap.js create mode 100644 packages/nocms/lib/createResourceTree.js create mode 100644 packages/nocms/lib/debug.js create mode 100644 packages/nocms/lib/io.js create mode 100644 packages/nocms/lib/nocmsAscii.js create mode 100644 packages/nocms/lib/plugins.js create mode 100644 packages/nocms/lib/server.js create mode 100644 packages/nocms/lib/threading.js diff --git a/.gitignore b/.gitignore index 3440ce7..0bf94ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ lerna-debug.log node_modules -# Ignore all build folders -packages/*/bin -packages/*/lib \ No newline at end of file +# Ignore all build folders \ No newline at end of file diff --git a/packages/nocms-plugin-directory-resources/lib/createDirectoryResourceProvider.js b/packages/nocms-plugin-directory-resources/lib/createDirectoryResourceProvider.js new file mode 100644 index 0000000..a38c580 --- /dev/null +++ b/packages/nocms-plugin-directory-resources/lib/createDirectoryResourceProvider.js @@ -0,0 +1,208 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createDirectoryResourceProvider; + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +var _renderDirectoryResource = require('./renderDirectoryResource'); + +var _renderDirectoryResource2 = _interopRequireDefault(_renderDirectoryResource); + +var _deepmerge = require('deepmerge'); + +var _deepmerge2 = _interopRequireDefault(_deepmerge); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +const pugFiles = ['_index.pug', 'index.pug']; +const scssFiles = ['_index.scss', 'index.scss']; +const pattern = '**/'; + +function createDirectoryResourceProvider({ findFiles, fileExists, readFile, watchFiles, writeFile, resolveInputPath }) { + let getDirectoryResources = (() => { + var _ref = _asyncToGenerator(function* () { + try { + if (!directoryResourceCache) { + let directories = yield getDirectories(); + let globals = yield getGlobals(); + let directoryResources = yield createDirectoryResources(directories, globals); + directoryResourceCache = directoryResources.reduce(function (directoryResourceCache, directoryResource) { + directoryResourceCache[directoryResource.id] = directoryResource; + return directoryResourceCache; + }, {}); + } + + return Object.values(directoryResourceCache); + } catch (err) { + throw err; + } + }); + + return function getDirectoryResources() { + return _ref.apply(this, arguments); + }; + })(); + + let createDirectoryResources = (() => { + var _ref2 = _asyncToGenerator(function* (directories, globals) { + let createDirectoryResourcePromises = directories.map(function (directory) { + return createDirectoryResource(directory, globals); + }); + + return Promise.all(createDirectoryResourcePromises); + }); + + return function createDirectoryResources(_x, _x2) { + return _ref2.apply(this, arguments); + }; + })(); + + let createDirectoryResource = (() => { + var _ref3 = _asyncToGenerator(function* (directory, globals) { + try { + let id = getDirectoryResourceId(directory); + let inDir = directory; + let outFile = _path2.default.join(directory, 'index.html'); + let physicalPath = resolveInputPath(directory); + let locals = yield getData(directory); + let data = (0, _deepmerge2.default)(globals, locals); + let mimeType = 'text/html'; + + return { id, inDir, outFile, physicalPath, data, mimeType }; + } catch (err) { + throw err; + } + }); + + return function createDirectoryResource(_x3, _x4) { + return _ref3.apply(this, arguments); + }; + })(); + + let getDirectories = (() => { + var _ref4 = _asyncToGenerator(function* () { + return findFiles(pattern).then(function (directories) { + return directories.filter(function (directory) { + const doesNotStartWithAnUnderscore = _path2.default.parse(directory).base[0] !== '_'; + const hasPugFile = pugFiles.some(function (pugFile) { + return fileExists(_path2.default.join(directory, pugFile)); + }); + const hasScssFile = scssFiles.some(function (scssFile) { + return fileExists(_path2.default.join(directory, scssFile)); + }); + + return doesNotStartWithAnUnderscore && hasPugFile && hasScssFile; + }); + }); + }); + + return function getDirectories() { + return _ref4.apply(this, arguments); + }; + })(); + + let getData = (() => { + var _ref5 = _asyncToGenerator(function* (directory) { + try { + let dataJson = yield getDataJson(directory); + + return JSON.parse(dataJson); + } catch (err) { + return {}; + } + }); + + return function getData(_x5) { + return _ref5.apply(this, arguments); + }; + })(); + + let getDataJson = (() => { + var _ref6 = _asyncToGenerator(function* (directory) { + try { + let dataJsonPath = _path2.default.join(directory, '_data.json'); + let dataJson = yield readFile(dataJsonPath, 'utf8'); + + return dataJson; + } catch (err) { + throw err; + } + }); + + return function getDataJson(_x6) { + return _ref6.apply(this, arguments); + }; + })(); + + let compileDirectoryResource = (() => { + var _ref7 = _asyncToGenerator(function* (directoryResource, resourceCompilationContext) { + try { + let renderedDirectoryResource = yield (0, _renderDirectoryResource2.default)(directoryResource, resourceCompilationContext); + + return writeFile(directoryResource.outFile, renderedDirectoryResource, 'utf8'); + } catch (err) { + throw err; + } + }); + + return function compileDirectoryResource(_x7, _x8) { + return _ref7.apply(this, arguments); + }; + })(); + + let getGlobals = (() => { + var _ref8 = _asyncToGenerator(function* () { + try { + let globals; + let env = process.env.NODE_ENV || 'development'; + + let defaultGlobalsJson = yield readFile('_globals.json'); + let defaultGlobals = JSON.parse(defaultGlobalsJson); + + try { + let envGlobalsJson = yield readFile(`_globals.${env}.json`); + let envGlobals = JSON.parse(envGlobalsJson); + + globals = (0, _deepmerge2.default)(defaultGlobals, envGlobals); + } catch (err) { + // We do not force having env-specific _globals.json, thus just proceed if it does not exist. + globals = defaultGlobals; + } + + return globals; + } catch (err) { + throw err; + } + }); + + return function getGlobals() { + return _ref8.apply(this, arguments); + }; + })(); + + let directoryResourceCache; + + watchFiles(pattern).on('all', handleAll); + + function handleAll(event, directory) { + if (directoryResourceCache) { + directoryResourceCache = null; + } + } + + return { + getResources: getDirectoryResources, + compileResource: compileDirectoryResource + }; + + function getDirectoryResourceId(directory) { + return '/' + directory.split(_path2.default.sep).join('/'); + } +} \ No newline at end of file diff --git a/packages/nocms-plugin-directory-resources/lib/createScriptManager.js b/packages/nocms-plugin-directory-resources/lib/createScriptManager.js new file mode 100644 index 0000000..8f60765 --- /dev/null +++ b/packages/nocms-plugin-directory-resources/lib/createScriptManager.js @@ -0,0 +1,32 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createScriptManager; + +var _fs = require('fs'); + +var _fs2 = _interopRequireDefault(_fs); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function createScriptManager() { + const scriptsFiles = new Set(); + + function registerScript(scriptFile) { + scriptsFiles.add(scriptFile); + } + + function embedRegisteredScripts(html) { + const scripts = [...scriptsFiles].map(file => _fs2.default.readFileSync(file, 'utf8')); + const scriptElements = scripts.map(script => ``); + + return html.replace('', `${scriptElements.join('')}`); + } + + return { + registerScript, + embedRegisteredScripts + }; +} \ No newline at end of file diff --git a/packages/nocms-plugin-directory-resources/lib/getShortDescription.js b/packages/nocms-plugin-directory-resources/lib/getShortDescription.js new file mode 100644 index 0000000..01d8fe0 --- /dev/null +++ b/packages/nocms-plugin-directory-resources/lib/getShortDescription.js @@ -0,0 +1,83 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = getShortDescription; +/* +Rules: + 1. ignore first line if it starts with '#' + 2. take content until you meet '**' or '##' or '###' at start of line + 3. take no more than 4 paragraphs of text + 4. transform string like "Here's a few tips:" on last line to "Here's a few tips..." +*/ +function getShortDescription(fullContent) { + let lines = fullContent.split('\n'); + + removeFirstLineIfHeader(lines); + removeFirstLineIfEmpty(lines); + + removeLinesAfterStars(lines); + removeLastLineIfEmpty(lines); + + removeLinesAfterNParagraphs(lines, 4); + removeLastLineIfEmpty(lines); + + replaceLastSymbolOfLastLine(lines, ':', '...'); + + return lines.join('\n'); +} + +function removeFirstLineIfHeader(lines) { + if (lines[0].indexOf('#') === 0) { + lines.splice(0, 1); + } +} + +function removeFirstLineIfEmpty(lines) { + if (lines[0] === '') { + lines.splice(0, 1); + } +} + +function removeLinesAfterStars(lines) { + let breakIndex = lines.findIndex(l => l.indexOf('**') === 0 || l.indexOf('##') === 0); + removeLinesAfterIndex(lines, breakIndex); +} + +function removeLinesAfterIndex(lines, index) { + if (index !== -1) { + lines.splice(index, lines.length - index); + } +} + +function removeLastLineIfEmpty(lines) { + if (lines.length && lines[lines.length - 1] === '') { + lines.splice(lines.length - 1, 1); + } +} + +function removeLinesAfterNParagraphs(lines, n) { + let p = 0, + breakIndex = -1; + for (let i = 0; i < lines.length; i++) { + if (lines[i] !== '') { + p++; + } + if (p > n) { + breakIndex = i; + break; + } + } + removeLinesAfterIndex(lines, breakIndex); +} + +function replaceLastSymbolOfLastLine(lines, symbolToReplace, replaceToSymbols) { + if (lines.length) { + var lastLine = lines[lines.length - 1]; + if (lastLine.length && lastLine[lastLine.length - 1] === symbolToReplace) { + lastLine = lastLine.substring(0, lastLine.length - 1) + replaceToSymbols; + lines[lines.length - 1] = lastLine; + } + } +} \ No newline at end of file diff --git a/packages/nocms-plugin-directory-resources/lib/index.js b/packages/nocms-plugin-directory-resources/lib/index.js new file mode 100644 index 0000000..33646a8 --- /dev/null +++ b/packages/nocms-plugin-directory-resources/lib/index.js @@ -0,0 +1,18 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.activate = activate; + +var _createDirectoryResourceProvider = require('./createDirectoryResourceProvider'); + +var _createDirectoryResourceProvider2 = _interopRequireDefault(_createDirectoryResourceProvider); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function activate({ findFiles, fileExists, readFile, registerResourceProvider, resolveInputPath, watchFiles, writeFile }) { + let directoryResourceProvider = (0, _createDirectoryResourceProvider2.default)({ findFiles, fileExists, readFile, resolveInputPath, watchFiles, writeFile }); + + registerResourceProvider(directoryResourceProvider); +} \ No newline at end of file diff --git a/packages/nocms-plugin-directory-resources/lib/renderDirectoryResource.js b/packages/nocms-plugin-directory-resources/lib/renderDirectoryResource.js new file mode 100644 index 0000000..8db80fe --- /dev/null +++ b/packages/nocms-plugin-directory-resources/lib/renderDirectoryResource.js @@ -0,0 +1,46 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _renderDirectoryResourceCss = require('./renderDirectoryResourceCss'); + +var _renderDirectoryResourceCss2 = _interopRequireDefault(_renderDirectoryResourceCss); + +var _renderDirectoryResourceHtml = require('./renderDirectoryResourceHtml'); + +var _renderDirectoryResourceHtml2 = _interopRequireDefault(_renderDirectoryResourceHtml); + +var _cssBingo = require('css-bingo'); + +var _cssBingo2 = _interopRequireDefault(_cssBingo); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +exports.default = (() => { + var _ref = _asyncToGenerator(function* (directoryResource, resources) { + try { + let css = yield (0, _renderDirectoryResourceCss2.default)(directoryResource); + let html = yield (0, _renderDirectoryResourceHtml2.default)(directoryResource, resources); + let processedCss = (0, _cssBingo2.default)(css, html); + let htmlWithPurifiedCssEmbedded = embedCssInHtml(processedCss, html); + + return htmlWithPurifiedCssEmbedded; + } catch (err) { + throw err; + } + }); + + function renderDirectoryResource(_x, _x2) { + return _ref.apply(this, arguments); + } + + return renderDirectoryResource; +})(); + +function embedCssInHtml(css, html) { + return html.replace('', ``); +} \ No newline at end of file diff --git a/packages/nocms-plugin-directory-resources/lib/renderDirectoryResourceCss.js b/packages/nocms-plugin-directory-resources/lib/renderDirectoryResourceCss.js new file mode 100644 index 0000000..d74739f --- /dev/null +++ b/packages/nocms-plugin-directory-resources/lib/renderDirectoryResourceCss.js @@ -0,0 +1,69 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +let renderScssAsync = (() => { + var _ref2 = _asyncToGenerator(function* (file) { + return new Promise(function (resolve) { + _nodeSass2.default.render({ file }, function (err, result) { + if (err) { + throw err; + } + + let css = result.css.toString(); + + resolve(css); + }); + }); + }); + + return function renderScssAsync(_x2) { + return _ref2.apply(this, arguments); + }; +})(); + +var _fs = require('fs'); + +var _fs2 = _interopRequireDefault(_fs); + +var _nodeSass = require('node-sass'); + +var _nodeSass2 = _interopRequireDefault(_nodeSass); + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +exports.default = (() => { + var _ref = _asyncToGenerator(function* (directoryResource) { + try { + let scssPaths = [_path2.default.join(directoryResource.physicalPath, '_index.scss'), _path2.default.join(directoryResource.physicalPath, 'index.scss')]; + + let scssPath = scssPaths.find(function (path) { + return _fs2.default.existsSync(path); + }); + + if (!scssPath) { + throw new Error('directory has no _index.scss or index.scss file'); + } + + let css = yield renderScssAsync(scssPath); + + return css; + } catch (err) { + throw err; + } + }); + + function renderDirectoryResourceCss(_x) { + return _ref.apply(this, arguments); + } + + return renderDirectoryResourceCss; +})(); \ No newline at end of file diff --git a/packages/nocms-plugin-directory-resources/lib/renderDirectoryResourceHtml.js b/packages/nocms-plugin-directory-resources/lib/renderDirectoryResourceHtml.js new file mode 100644 index 0000000..e43a58c --- /dev/null +++ b/packages/nocms-plugin-directory-resources/lib/renderDirectoryResourceHtml.js @@ -0,0 +1,109 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var _fs = require('fs'); + +var _fs2 = _interopRequireDefault(_fs); + +var _getShortDescription = require('./getShortDescription.js'); + +var _getShortDescription2 = _interopRequireDefault(_getShortDescription); + +var _createScriptManager = require('./createScriptManager.js'); + +var _createScriptManager2 = _interopRequireDefault(_createScriptManager); + +var _jstransformer = require('jstransformer'); + +var _jstransformer2 = _interopRequireDefault(_jstransformer); + +var _jstransformerMarked = require('jstransformer-marked'); + +var _jstransformerMarked2 = _interopRequireDefault(_jstransformerMarked); + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +var _pug = require('pug'); + +var _pug2 = _interopRequireDefault(_pug); + +var _moment = require('moment'); + +var _moment2 = _interopRequireDefault(_moment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +const cache = {}; +const marked = (0, _jstransformer2.default)(_jstransformerMarked2.default); + +exports.default = (() => { + var _ref = _asyncToGenerator(function* (directoryResource, { resourceTree }) { + let pugPaths = [_path2.default.join(directoryResource.physicalPath, '_index.pug'), _path2.default.join(directoryResource.physicalPath, 'index.pug')]; + + let pugPath = pugPaths.find(function (path) { + return _fs2.default.existsSync(path); + }); + + if (!pugPath) { + throw new Error('directory has no _index.pug or index.scss pug'); + } + + let scriptManager = (0, _createScriptManager2.default)(); + + let opts = { + plugins: [{ + read: readFile + }], + filters: { + 'register-script': function (text, options) { + let { src, filename } = options; + let scriptFile = _path2.default.resolve(_path2.default.dirname(filename), src); + scriptManager.registerScript(scriptFile); + return ''; + } + } + }; + let renderHtml = _pug2.default.compileFile(pugPath, opts); + let locals = _extends({}, directoryResource.data, { + current: { path: [...directoryResource.id.split('/').filter(Boolean)] }, + public: resourceTree, + nocms: { + renderShortDescription: function (href) { + let sitePathBacktrack = directoryResource.id.split('/').filter(Boolean).map(d => '..').join('/'); + let sitePath = _path2.default.join(directoryResource.physicalPath, sitePathBacktrack); + let filePath = _path2.default.join(sitePath, href); + let file = readFile(filePath); + let shortDescription = (0, _getShortDescription2.default)(file); + return marked.render(shortDescription).body; + }, + moment: _moment2.default + } + }); + + let html = renderHtml(locals); + html = scriptManager.embedRegisteredScripts(html); + return html; + }); + + function renderDirectoryResourceHtml(_x, _x2) { + return _ref.apply(this, arguments); + } + + return renderDirectoryResourceHtml; +})(); + +function readFile(filename) { + if (process.env.NODE_ENV === 'production') { + return cache[filename] || (cache[filename] = _fs2.default.readFileSync(filename, 'utf8')); + } + return _fs2.default.readFileSync(filename, 'utf8'); +} \ No newline at end of file diff --git a/packages/nocms-plugin-file-resources/lib/createFileResourceProvider.js b/packages/nocms-plugin-file-resources/lib/createFileResourceProvider.js new file mode 100644 index 0000000..4c0587c --- /dev/null +++ b/packages/nocms-plugin-file-resources/lib/createFileResourceProvider.js @@ -0,0 +1,123 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createFileResourceProvider; + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +var _mime = require('mime'); + +var _mime2 = _interopRequireDefault(_mime); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +function createFileResourceProvider(findFiles, readFile, watchFiles, writeFile, directory, pattern) { + let getFileResources = (() => { + var _ref = _asyncToGenerator(function* () { + try { + if (!fileResourceCache) { + let files = yield findFiles(pattern); + let fileResources = yield createFileResources(files); + + fileResourceCache = fileResources.reduce(function (fileResourceCache, fileResource) { + fileResourceCache[fileResource.id] = fileResource; + return fileResourceCache; + }, {}); + } + + return Object.values(fileResourceCache); + } catch (err) { + throw err; + } + }); + + return function getFileResources() { + return _ref.apply(this, arguments); + }; + })(); + + let createFileResources = (() => { + var _ref2 = _asyncToGenerator(function* (files) { + let createFileResourcePromises = files.map(function (file) { + return createFileResource(file); + }); + + return Promise.all(createFileResourcePromises); + }); + + return function createFileResources(_x) { + return _ref2.apply(this, arguments); + }; + })(); + + let createFileResource = (() => { + var _ref3 = _asyncToGenerator(function* (file) { + try { + let newPath = _path2.default.relative(directory, file); + let id = createFileResourceId(newPath); + let outFile = newPath; + let inFile = file; + let data = {}; + let mimeType = _mime2.default.lookup(id); + + return { id, inFile, outFile, mimeType, data }; + } catch (err) { + throw err; + } + }); + + return function createFileResource(_x2) { + return _ref3.apply(this, arguments); + }; + })(); + + let compileFileResource = (() => { + var _ref4 = _asyncToGenerator(function* (fileResource) { + try { + let fileBuffer = yield readFile(fileResource.inFile); + return writeFile(fileResource.outFile, fileBuffer); + } catch (err) { + throw err; + } + }); + + return function compileFileResource(_x3) { + return _ref4.apply(this, arguments); + }; + })(); + + pattern = _path2.default.posix.join(directory, pattern); + let fileResourceCache; + + watchFiles(pattern).on('all', handleAll); + + return { + getResources: getFileResources, + compileResource: compileFileResource + }; + + function handleAll(event, file) { + if (fileResourceCache) { + switch (event) { + case 'add': + case 'change': + createFileResource(file).then(fileResource => fileResourceCache[fileResource.id] = fileResource); + break; + case 'unlink': + let fileResourceId = createFileResourceId(file); + delete fileResourceCache[fileResourceId]; + break; + } + } + } + + function createFileResourceId(file) { + return ['', ...file.split(_path2.default.sep)].join('/'); + } +} \ No newline at end of file diff --git a/packages/nocms-plugin-file-resources/lib/index.js b/packages/nocms-plugin-file-resources/lib/index.js new file mode 100644 index 0000000..e5c64c9 --- /dev/null +++ b/packages/nocms-plugin-file-resources/lib/index.js @@ -0,0 +1,26 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.activate = activate; + +var _createFileResourceProvider = require('./createFileResourceProvider'); + +var _createFileResourceProvider2 = _interopRequireDefault(_createFileResourceProvider); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function activate({ registerResourceProvider, findFiles, readFile, watchFiles, writeFile, config }) { + + if (config === null) { + return; + } + const providers = config.plugins['file-resources'].providers; + + for (let i = 0; i < providers.length; i++) { + const providerConfig = providers[i]; + const fileResourceProvider = (0, _createFileResourceProvider2.default)(findFiles, readFile, watchFiles, writeFile, providerConfig.path, providerConfig.glob); + registerResourceProvider(fileResourceProvider); + } +} \ No newline at end of file diff --git a/packages/nocms-plugin-image-resources/lib/createImageResourceProvider.js b/packages/nocms-plugin-image-resources/lib/createImageResourceProvider.js new file mode 100644 index 0000000..7028815 --- /dev/null +++ b/packages/nocms-plugin-image-resources/lib/createImageResourceProvider.js @@ -0,0 +1,171 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +let getImageResources = (() => { + var _ref = _asyncToGenerator(function* (findFiles, readFile) { + try { + if (!imageResourceCache) { + let imageFiles = yield getImageFiles(findFiles); + let imageResources = yield createImageResources(readFile, imageFiles); + imageResourceCache = imageResources.reduce(function (imageResourceCache, imageResource) { + imageResourceCache[imageResource.id] = imageResource; + return imageResourceCache; + }, {}); + } + + return Object.values(imageResourceCache); + } catch (err) { + throw err; + } + }); + + return function getImageResources(_x, _x2) { + return _ref.apply(this, arguments); + }; +})(); + +let createImageResources = (() => { + var _ref2 = _asyncToGenerator(function* (readFile, imageFiles) { + let createImageResourcePromises = imageFiles.map(function (imageFile) { + return createImageResource(readFile, imageFile); + }); + + return Promise.all(createImageResourcePromises); + }); + + return function createImageResources(_x3, _x4) { + return _ref2.apply(this, arguments); + }; +})(); + +let createImageResource = (() => { + var _ref3 = _asyncToGenerator(function* (readFile, imageFile) { + try { + let id = createImageResourceId(imageFile); + let inFile = imageFile; + let outFile = imageFile; + let data = yield getImageData(readFile, imageFile); + let mimeType = _mime2.default.lookup(id); + + return { id, inFile, outFile, data, mimeType }; + } catch (err) { + throw err; + } + }); + + return function createImageResource(_x5, _x6) { + return _ref3.apply(this, arguments); + }; +})(); + +let getImageData = (() => { + var _ref4 = _asyncToGenerator(function* (readFile, imageFile) { + try { + let imageBuffer = yield readFile(imageFile); + let imageSize = yield getImageSize(imageBuffer); + + return { size: imageSize }; + } catch (err) { + throw err; + } + }); + + return function getImageData(_x7, _x8) { + return _ref4.apply(this, arguments); + }; +})(); + +let getImageSize = (() => { + var _ref5 = _asyncToGenerator(function* (imageBuffer) { + return new Promise(function (resolve) { + (0, _gm2.default)(imageBuffer).size(function (err, size) { + if (err) { + resolve(); + } + + resolve(size); + }); + }); + }); + + return function getImageSize(_x9) { + return _ref5.apply(this, arguments); + }; +})(); + +let compileImageResource = (() => { + var _ref6 = _asyncToGenerator(function* (readFile, writeFile, imageResource) { + try { + let imageBuffer = yield readFile(imageResource.inFile); + + return writeFile(imageResource.outFile, imageBuffer); + } catch (err) { + throw err; + } + }); + + return function compileImageResource(_x10, _x11, _x12) { + return _ref6.apply(this, arguments); + }; +})(); + +let getImageFiles = (() => { + var _ref7 = _asyncToGenerator(function* (findFiles) { + return findFiles('**/!(_)*.?(jpg|jpeg|png|gif)'); + }); + + return function getImageFiles(_x13) { + return _ref7.apply(this, arguments); + }; +})(); + +exports.default = createImageResourceProvider; + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +var _gm = require('gm'); + +var _gm2 = _interopRequireDefault(_gm); + +var _mime = require('mime'); + +var _mime2 = _interopRequireDefault(_mime); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +let imageResourceCache; + +function createImageResourceProvider({ findFiles, readFile, watchFiles, writeFile }) { + watchFiles('**/!(_)*.?(jpg|jpeg|png|gif)').on('all', handleAll); + + function handleAll(event, imageFile) { + if (imageResourceCache) { + switch (event) { + case 'add': + case 'change': + createImageResource(readFile, imageFile).then(imageResource => imageResourceCache[imageResource.id] = imageResource); + break; + case 'unlink': + let imageResourceId = createImageResourceId(imageFile); + delete imageResourceCache[imageResourceId]; + break; + } + } + } + + return { + getResources: getImageResources.bind(null, findFiles, readFile), + compileResource: compileImageResource.bind(null, readFile, writeFile) + }; +} + +function createImageResourceId(imageFile) { + return '/' + imageFile.split(_path2.default.sep).join('/'); +} \ No newline at end of file diff --git a/packages/nocms-plugin-image-resources/lib/index.js b/packages/nocms-plugin-image-resources/lib/index.js new file mode 100644 index 0000000..461e24a --- /dev/null +++ b/packages/nocms-plugin-image-resources/lib/index.js @@ -0,0 +1,18 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.activate = activate; + +var _createImageResourceProvider = require('./createImageResourceProvider'); + +var _createImageResourceProvider2 = _interopRequireDefault(_createImageResourceProvider); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function activate({ createResourceProvider, registerResourceProvider, findFiles, readFile, watchFiles, writeFile }) { + const imageResourceProvider = (0, _createImageResourceProvider2.default)({ findFiles, readFile, watchFiles, writeFile }); + + registerResourceProvider(imageResourceProvider); +} \ No newline at end of file diff --git a/packages/nocms-plugin-json-resources/lib/createJsonResourceProvider.js b/packages/nocms-plugin-json-resources/lib/createJsonResourceProvider.js new file mode 100644 index 0000000..4950e3e --- /dev/null +++ b/packages/nocms-plugin-json-resources/lib/createJsonResourceProvider.js @@ -0,0 +1,118 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createJsonResourceProvider; + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +function createJsonResourceProvider({ findFiles, readFile, watchFiles, writeFile }) { + let getJsonResources = (() => { + var _ref = _asyncToGenerator(function* () { + try { + if (!jsonResourceCache) { + let jsonFiles = yield findFiles(pattern); + let jsonResources = yield createJsonResources(jsonFiles); + jsonResourceCache = jsonResources.reduce(function (jsonResourceCache, jsonResource) { + jsonResourceCache[jsonResource.id] = jsonResource; + return jsonResourceCache; + }, {}); + } + + return Object.values(jsonResourceCache); + } catch (err) { + throw err; + } + }); + + return function getJsonResources() { + return _ref.apply(this, arguments); + }; + })(); + + let createJsonResources = (() => { + var _ref2 = _asyncToGenerator(function* (jsonFiles) { + let createJsonResourcePromises = jsonFiles.map(createJsonResource); + + return Promise.all(createJsonResourcePromises); + }); + + return function createJsonResources(_x) { + return _ref2.apply(this, arguments); + }; + })(); + + let createJsonResource = (() => { + var _ref3 = _asyncToGenerator(function* (jsonFile) { + try { + let id = createJsonResourceId(jsonFile); + let inFile = jsonFile; + let outFile = jsonFile; + let json = yield readFile(jsonFile); + let data = JSON.parse(json); + let mimeType = 'application/json'; + + return { id, inFile, outFile, mimeType, data }; + } catch (err) { + throw err; + } + }); + + return function createJsonResource(_x2) { + return _ref3.apply(this, arguments); + }; + })(); + + let compileJsonResource = (() => { + var _ref4 = _asyncToGenerator(function* (jsonResource) { + try { + let json = yield readFile(jsonResource.inFile); + let data = JSON.stringify(JSON.parse(json)); + + return writeFile(jsonResource.outFile, data, 'utf8'); + } catch (err) { + throw err; + } + }); + + return function compileJsonResource(_x3) { + return _ref4.apply(this, arguments); + }; + })(); + + let pattern = '**/!(_)*.json'; + let jsonResourceCache; + + watchFiles(pattern).on('all', handleAll); + + function handleAll(event, jsonFile) { + if (jsonResourceCache) { + switch (event) { + case 'add': + case 'change': + createJsonResource(jsonFile).then(jsonResource => jsonResourceCache[jsonResource.id] = jsonResource); + break; + case 'unlink': + let jsonResourceId = createJsonResourceId(jsonFile); + delete jsonResourceCache[jsonResourceId]; + break; + } + } + } + + return { + getResources: getJsonResources, + compileResource: compileJsonResource + }; + + function createJsonResourceId(jsonFile) { + return '/' + jsonFile.split(_path2.default.sep).join('/'); + } +} \ No newline at end of file diff --git a/packages/nocms-plugin-json-resources/lib/index.js b/packages/nocms-plugin-json-resources/lib/index.js new file mode 100644 index 0000000..9e6e4fc --- /dev/null +++ b/packages/nocms-plugin-json-resources/lib/index.js @@ -0,0 +1,18 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.activate = activate; + +var _createJsonResourceProvider = require('./createJsonResourceProvider'); + +var _createJsonResourceProvider2 = _interopRequireDefault(_createJsonResourceProvider); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function activate({ createResourceProvider, registerResourceProvider, findFiles, readFile, watchFiles, writeFile }) { + const jsonResourceProvider = (0, _createJsonResourceProvider2.default)({ findFiles, readFile, watchFiles, writeFile }); + + registerResourceProvider(jsonResourceProvider); +} \ No newline at end of file diff --git a/packages/nocms/bin/nocms-worker.js b/packages/nocms/bin/nocms-worker.js new file mode 100644 index 0000000..b7ee184 --- /dev/null +++ b/packages/nocms/bin/nocms-worker.js @@ -0,0 +1,155 @@ +#!/usr/bin/env node +'use strict'; + +let main = (() => { + var _ref = _asyncToGenerator(function* () { + try { + const debug = (0, _debug.createDebug)('worker'); + + const optionDefinitions = [{ name: 'help', alias: 'h' }, { name: 'in-dir', alias: 'i', type: String, description: 'Input directory to read resources from.' }, { name: 'out-dir', alias: 'o', type: String, description: 'Output directory to write compiled resource to.' }]; + + const usageDefinition = [{ header: 'NOCMS Worker Process Command Line Interface', content: _nocmsAscii2.default, raw: true }, { header: 'Synopsis', content: '$ nocms-worker ' }, { header: 'Options', optionList: optionDefinitions }]; + + let options; + + try { + options = (0, _commandLineArgs2.default)(optionDefinitions); + } catch (err) { + options = { help: true }; + } + + if (options.help) { + const usage = (0, _commandLineUsage2.default)(usageDefinition); + console.log(usage); + return; + } + + const inDir = options['in-dir']; + const outDir = options['out-dir']; + + // Load config rc file + const config = yield (0, _config2.default)(); + + // Create an array to hold all the resource providers. + const resourceProviders = []; + + // Create a function that plugins can use to register resource providers. + const registerResourceProvider = (0, _createRegisterResourceProvider2.default)(resourceProviders); + + // Create an activation context that plugins will get when activated. + const pluginActivationContext = (0, _createPluginActivationContext2.default)(inDir, outDir, registerResourceProvider, config); + + // Load all the plugins with the activation content. + yield (0, _plugins.loadPlugins)(pluginActivationContext); + + // Create our logging decorated composite resource provider that uses all the resource providers registered by the plugins. + const resourceProvider = (0, _createLoggingDecoratedResourceProvider2.default)((0, _createCompositeResourceProvider2.default)(resourceProviders)); + + let resourceCompilationContext; + + // Create command handlers that will handle commands dispatched by the command dispatcher. + const commandHandlers = [(0, _threading.createCommandHandler)(['compileResource'], (() => { + var _ref2 = _asyncToGenerator(function* (command) { + debug('compileResource(%o)', command); + + const { resourceId, cache } = command.params; + + if (!resourceCompilationContext || !cache) { + const resources = yield resourceProvider.getResources(); + const resourceMap = (0, _createResourceMap2.default)(resources); + const resourceTree = (0, _createResourceTree2.default)(resourceMap); + + resourceCompilationContext = { resourceMap, resourceTree }; + } + + const resource = resourceCompilationContext.resourceMap[resourceId]; + + if (!resource) { + throw new Error(`Resource ${resourceId} Not Found`); + } + + return resourceProvider.compileResource(resource, resourceCompilationContext); + }); + + return function (_x) { + return _ref2.apply(this, arguments); + }; + })())]; + + // Create command dispatcher that dispatches commands to command handlers. + const commandDispatcher = (0, _threading.createCommandDispatcher)(commandHandlers); + + // Create a command receiver that receives commands and dispatches them using the command disaptcher. + const commandReceiver = (0, _threading.createCommandReceiver)(commandDispatcher); + + // Signal that the command receiver is now idle and ready to receive commands. + commandReceiver.idle(); + } catch (err) { + console.error(err); + } + }); + + return function main() { + return _ref.apply(this, arguments); + }; +})(); + +var _threading = require('../lib/threading'); + +var _debug = require('../lib/debug'); + +var _plugins = require('../lib/plugins'); + +var _commandLineArgs = require('command-line-args'); + +var _commandLineArgs2 = _interopRequireDefault(_commandLineArgs); + +var _commandLineUsage = require('command-line-usage'); + +var _commandLineUsage2 = _interopRequireDefault(_commandLineUsage); + +var _createCompositeResourceProvider = require('../lib/createCompositeResourceProvider'); + +var _createCompositeResourceProvider2 = _interopRequireDefault(_createCompositeResourceProvider); + +var _createLoggingDecoratedResourceProvider = require('../lib/createLoggingDecoratedResourceProvider'); + +var _createLoggingDecoratedResourceProvider2 = _interopRequireDefault(_createLoggingDecoratedResourceProvider); + +var _createPluginActivationContext = require('../lib/createPluginActivationContext'); + +var _createPluginActivationContext2 = _interopRequireDefault(_createPluginActivationContext); + +var _createRegisterResourceProvider = require('../lib/createRegisterResourceProvider'); + +var _createRegisterResourceProvider2 = _interopRequireDefault(_createRegisterResourceProvider); + +var _createResourceMap = require('../lib/createResourceMap'); + +var _createResourceMap2 = _interopRequireDefault(_createResourceMap); + +var _createResourceTree = require('../lib/createResourceTree'); + +var _createResourceTree2 = _interopRequireDefault(_createResourceTree); + +var _nocmsAscii = require('../lib/nocmsAscii'); + +var _nocmsAscii2 = _interopRequireDefault(_nocmsAscii); + +var _object = require('object.values'); + +var _object2 = _interopRequireDefault(_object); + +var _config = require('../lib/config'); + +var _config2 = _interopRequireDefault(_config); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +if (!Object.values) { + _object2.default.shim(); +} + +main(); \ No newline at end of file diff --git a/packages/nocms/bin/nocms.js b/packages/nocms/bin/nocms.js new file mode 100644 index 0000000..2352527 --- /dev/null +++ b/packages/nocms/bin/nocms.js @@ -0,0 +1,184 @@ +#!/usr/bin/env node +'use strict'; + +let main = (() => { + var _ref = _asyncToGenerator(function* () { + try { + const defaultOptionsDefinitions = [{ name: 'help', alias: 'h' }]; + + const commands = ['compile', 'server']; + + const commandOptionDefinitions = { + compile: [{ name: 'concurrency', alias: 'c', type: Number, defaultValue: (0, _os.cpus)().length, description: 'Concurrency.' }, { name: 'help', alias: 'h' }, { name: 'in-dir', alias: 'i', type: String, description: 'Input directory to read resources from.', required: true }, { name: 'out-dir', alias: 'o', type: String, description: 'Output directory to write compiled resource to.', required: true }], + server: [{ name: 'concurrency', alias: 'c', type: Number, defaultValue: (0, _os.cpus)().length, description: 'Concurrency.' }, { name: 'help', alias: 'h' }, { name: 'in-dir', alias: 'i', type: String, description: 'input directory.', required: true }, { name: 'out-dir', alias: 'o', type: String, description: 'output directory.', required: true }, { name: 'port', alias: 'p', type: Number, description: 'port to listen to.', required: true }] + }; + + const defaultUsageDefinition = [{ header: 'NOCMS Command Line Interface', content: _nocmsAscii2.default, raw: true }, { header: 'Synopsis', content: '$ nocms ' }, { + header: 'Commands', content: [{ name: 'compile', summary: 'Compile a site.' }, { name: 'server', summary: 'Start a web server.' }] + }, { header: 'Options', optionList: defaultOptionsDefinitions }]; + + const commandUsageDefinitions = { + compile: [{ header: 'NOCMS Command Line Interface', content: _nocmsAscii2.default, raw: true }, { header: 'Synopsis', content: '$ nocms compile ' }, { header: 'Options', optionList: commandOptionDefinitions['compile'] }], + server: [{ header: 'NOCMS Command Line Interface', content: _nocmsAscii2.default, raw: true }, { header: 'Synopsis', content: '$ nocms server ' }, { header: 'Options', optionList: commandOptionDefinitions['server'] }] + }; + + let command, options; + + try { + const result = (0, _commandLineCommands2.default)(commands); + command = result.command; + const optionDefinitions = commandOptionDefinitions[command]; + options = (0, _commandLineArgs2.default)(optionDefinitions, { argv: result.argv }); + + const valid = optionDefinitions.filter(function (optionDefinition) { + return optionDefinition.required; + }).every(function (optionDefinition) { + return Object.keys(options).indexOf(optionDefinition.name) >= -1; + }); + + if (!valid) { + options.help = true; + } + } catch (err) { + command = command || ''; + options = { help: true }; + } + + if (options.help) { + const usage = (0, _commandLineUsage2.default)(commandUsageDefinitions[command] || defaultUsageDefinition); + console.log(usage); + return; + } + + const inDir = options['in-dir']; + const outDir = options['out-dir']; + const port = options['port']; + const concurrency = options['concurrency']; + + // Load config rc file + const config = yield (0, _config2.default)(); + + // Create an array to hold all the resource providers. + const resourceProviders = []; + + // Create a function that plugins can use to register resource providers. + const registerResourceProvider = (0, _createRegisterResourceProvider2.default)(resourceProviders); + + // Create the activation contenxt that plugins will get when activated. + const pluginActivationContext = (0, _createPluginActivationContext2.default)(inDir, outDir, registerResourceProvider, config); + + // Load all the plugins with the activation content. + yield (0, _plugins.loadPlugins)(pluginActivationContext); + + // Create composite resource provider that uses all the resource providers registered by the plugins. + const resourceProvider = (0, _createCompositeResourceProvider2.default)(resourceProviders); + + // Create the commandworker process pool that will handle compilation of resources. + const commandWorkerProcessPool = (0, _threading.createCommandWorkerProcessPool)(concurrency, _path2.default.resolve(__dirname, './nocms-worker.js'), ['worker', '--in-dir', inDir, '--out-dir', outDir]); + + // Create the command sender that sends commands to the command worker process pool. + const commandSender = (0, _threading.createCommandSender)(commandWorkerProcessPool); + + switch (command) { + case 'compile': + let compileStarted = process.hrtime(); + let resources = yield resourceProvider.getResources(); + + yield (0, _io.cleanDirectoryAsync)(outDir); + + const commandPromises = resources.map(function (resource, idx) { + return { + id: idx, + name: 'compileResource', + params: { resourceId: resource.id, cache: true } + }; + }).map(commandSender.sendCommand); + + Promise.all(commandPromises).then(function (results) { + let compileFinished = process.hrtime(compileStarted); + console.info(`Compiled ${resources.length} resources in %ds %dms`, compileFinished[0], compileFinished[1] / 1000000); + + process.exit(0); + }).catch(function (err) { + console.error('error', err, err.stack); + + process.exit(1); + }); + + break; + + case 'server': + (0, _server.createServer)({ commandSender, resolveOutputPath: pluginActivationContext.resolveOutputPath, resourceProvider, port }); + break; + } + } catch (err) { + console.error(err, err.stack); + + process.exit(1); + } + }); + + return function main() { + return _ref.apply(this, arguments); + }; +})(); + +var _io = require('../lib/io'); + +var _os = require('os'); + +var _threading = require('../lib/threading'); + +var _server = require('../lib/server'); + +var _plugins = require('../lib/plugins'); + +var _commandLineArgs = require('command-line-args'); + +var _commandLineArgs2 = _interopRequireDefault(_commandLineArgs); + +var _commandLineCommands = require('command-line-commands'); + +var _commandLineCommands2 = _interopRequireDefault(_commandLineCommands); + +var _commandLineUsage = require('command-line-usage'); + +var _commandLineUsage2 = _interopRequireDefault(_commandLineUsage); + +var _createCompositeResourceProvider = require('../lib/createCompositeResourceProvider'); + +var _createCompositeResourceProvider2 = _interopRequireDefault(_createCompositeResourceProvider); + +var _createPluginActivationContext = require('../lib/createPluginActivationContext'); + +var _createPluginActivationContext2 = _interopRequireDefault(_createPluginActivationContext); + +var _createRegisterResourceProvider = require('../lib/createRegisterResourceProvider'); + +var _createRegisterResourceProvider2 = _interopRequireDefault(_createRegisterResourceProvider); + +var _nocmsAscii = require('../lib/nocmsAscii'); + +var _nocmsAscii2 = _interopRequireDefault(_nocmsAscii); + +var _object = require('object.values'); + +var _object2 = _interopRequireDefault(_object); + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +var _config = require('../lib/config'); + +var _config2 = _interopRequireDefault(_config); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +if (!Object.values) { + _object2.default.shim(); +} + +main(); \ No newline at end of file diff --git a/packages/nocms/lib/config.js b/packages/nocms/lib/config.js new file mode 100644 index 0000000..1f1f39b --- /dev/null +++ b/packages/nocms/lib/config.js @@ -0,0 +1,42 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _cosmiconfig = require('cosmiconfig'); + +var _cosmiconfig2 = _interopRequireDefault(_cosmiconfig); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +exports.default = (() => { + var _ref = _asyncToGenerator(function* () { + try { + let opts = { 'rcExtensions': true }; + + let explorer = (0, _cosmiconfig2.default)('nocms', opts); + + return explorer.load('./').then(function (configFile) { + if (configFile === null) { + console.error('Config file not found.'); + return configFile; + } else { + return configFile.config; + } + }).catch(function (err) { + console.error(err); + }); + } catch (error) { + console.error(error); + } + }); + + function loadConfig() { + return _ref.apply(this, arguments); + } + + return loadConfig; +})(); \ No newline at end of file diff --git a/packages/nocms/lib/createCachingDecoratedResourceProvider.js b/packages/nocms/lib/createCachingDecoratedResourceProvider.js new file mode 100644 index 0000000..000c6d2 --- /dev/null +++ b/packages/nocms/lib/createCachingDecoratedResourceProvider.js @@ -0,0 +1,42 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createCachingDecoratedResourceProvider; + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +function createCachingDecoratedResourceProvider(resourceProvider) { + let getResources = (() => { + var _ref = _asyncToGenerator(function* () { + try { + if (!resourceCache) { + resourceCache = yield resourceProvider.getResources(); + } + + return Promise.resolve(resourceCache); + } catch (err) { + throw err; + } + }); + + return function getResources() { + return _ref.apply(this, arguments); + }; + })(); + + let compileResource = (() => { + var _ref2 = _asyncToGenerator(function* (resource, resourceCompilationContext) { + return resourceProvider.compileResource(resource, resourceCompilationContext); + }); + + return function compileResource(_x, _x2) { + return _ref2.apply(this, arguments); + }; + })(); + + let resourceCache; + + return { getResources, compileResource }; +} \ No newline at end of file diff --git a/packages/nocms/lib/createCompositeResourceProvider.js b/packages/nocms/lib/createCompositeResourceProvider.js new file mode 100644 index 0000000..d91cb93 --- /dev/null +++ b/packages/nocms/lib/createCompositeResourceProvider.js @@ -0,0 +1,50 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createCompositeResourceProvider; + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +function createCompositeResourceProvider(resourceProviders) { + let getResources = (() => { + var _ref = _asyncToGenerator(function* () { + return Promise.all(resourceProviders.map(function (resourceProvider) { + return resourceProvider.getResources().then(function (resources) { + resources.forEach(function (resource) { + resourceResourceProviderMap.set(resource, resourceProvider); + }); + + return resources; + }); + })).then(function (resourceProvidersResources) { + return [].concat.apply([], resourceProvidersResources); + }); + }); + + return function getResources() { + return _ref.apply(this, arguments); + }; + })(); + + let compileResource = (() => { + var _ref2 = _asyncToGenerator(function* (resource, resourceCompilationContext) { + let resourceProvider = resourceResourceProviderMap.get(resource); + + if (!resourceProvider) { + throw new Error(`No resource provider found for resource ${resource.id}`); + } + + return resourceProvider.compileResource(resource, resourceCompilationContext); + }); + + return function compileResource(_x, _x2) { + return _ref2.apply(this, arguments); + }; + })(); + + let resourceResourceProviderMap = new Map(); + + return { getResources, compileResource }; +} \ No newline at end of file diff --git a/packages/nocms/lib/createLoggingDecoratedResourceProvider.js b/packages/nocms/lib/createLoggingDecoratedResourceProvider.js new file mode 100644 index 0000000..5f42718 --- /dev/null +++ b/packages/nocms/lib/createLoggingDecoratedResourceProvider.js @@ -0,0 +1,44 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createLoggingDecoratedResourceProvider; + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +function createLoggingDecoratedResourceProvider(resourceProvider) { + let getResources = (() => { + var _ref = _asyncToGenerator(function* () { + console.log('getResources'); + + return resourceProvider.getResources(); + }); + + return function getResources() { + return _ref.apply(this, arguments); + }; + })(); + + let compileResource = (() => { + var _ref2 = _asyncToGenerator(function* (resource, resourceCompilationContext) { + try { + let compileStarted = process.hrtime(); + let result = yield resourceProvider.compileResource(resource, resourceCompilationContext); + let compileFinished = process.hrtime(compileStarted); + + console.info('Compiled %s in %ds %dms', resource.id, compileFinished[0], compileFinished[1] / 1000000); + + return result; + } catch (err) { + throw err; + } + }); + + return function compileResource(_x, _x2) { + return _ref2.apply(this, arguments); + }; + })(); + + return { getResources, compileResource }; +} \ No newline at end of file diff --git a/packages/nocms/lib/createPluginActivationContext.js b/packages/nocms/lib/createPluginActivationContext.js new file mode 100644 index 0000000..4500bfc --- /dev/null +++ b/packages/nocms/lib/createPluginActivationContext.js @@ -0,0 +1,22 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createPluginActivationContext; + +var _io = require('./io'); + +function createPluginActivationContext(inDir, outDir, registerResourceProvider, config) { + return { + registerResourceProvider, + config: config, + fileExists: (0, _io.createDirectoryBoundFileExists)(inDir), + findFiles: (0, _io.createDirectoryBoundFindFilesAsync)(inDir), + readFile: (0, _io.createDirectoryBoundReadFileAsync)(inDir), + watchFiles: (0, _io.createDirectoryBoundWatchFiles)(inDir), + writeFile: (0, _io.createDirectoryBoundWriteFileAsync)(outDir), + resolveInputPath: (0, _io.createDirectoryBoundResolvePath)(inDir), + resolveOutputPath: (0, _io.createDirectoryBoundResolvePath)(outDir) + }; +} \ No newline at end of file diff --git a/packages/nocms/lib/createRegisterResourceProvider.js b/packages/nocms/lib/createRegisterResourceProvider.js new file mode 100644 index 0000000..76d5855 --- /dev/null +++ b/packages/nocms/lib/createRegisterResourceProvider.js @@ -0,0 +1,11 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createRegisterResourceProvider; +function createRegisterResourceProvider(resourceProviders) { + return function registerResourceProvider(resourceProvider) { + resourceProviders.push(resourceProvider); + }; +} \ No newline at end of file diff --git a/packages/nocms/lib/createResourceMap.js b/packages/nocms/lib/createResourceMap.js new file mode 100644 index 0000000..bfa4f20 --- /dev/null +++ b/packages/nocms/lib/createResourceMap.js @@ -0,0 +1,22 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createResourceMap; + +var _deepFreeze = require('deep-freeze'); + +var _deepFreeze2 = _interopRequireDefault(_deepFreeze); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function createResourceMap(resources) { + let resourceMap = resources.reduce((resourceMap, resource) => { + resourceMap[resource.id] = resource; + + return resourceMap; + }, {}); + + return (0, _deepFreeze2.default)(resourceMap); +} \ No newline at end of file diff --git a/packages/nocms/lib/createResourceTree.js b/packages/nocms/lib/createResourceTree.js new file mode 100644 index 0000000..022b88b --- /dev/null +++ b/packages/nocms/lib/createResourceTree.js @@ -0,0 +1,36 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = createResourceTree; + +var _deepFreeze = require('deep-freeze'); + +var _deepFreeze2 = _interopRequireDefault(_deepFreeze); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function createResourceTree(resourceMap) { + let resourceIds = Object.keys(resourceMap); + resourceIds.sort(); + + let resourceTree = resourceIds.reduce((resourceTree, resourceId) => { + let resource = resourceMap[resourceId]; + + let resourcePath = resourceId.split('/').filter(Boolean); + let node = resourceTree; + + for (let i = 0; i < resourcePath.length; i++) { + let fragment = resourcePath[i]; + node[fragment] = node[fragment] || {}; + node = node[fragment]; + } + + node._data = resource.data; + + return resourceTree; + }, {}); + + return (0, _deepFreeze2.default)(resourceTree); +} \ No newline at end of file diff --git a/packages/nocms/lib/debug.js b/packages/nocms/lib/debug.js new file mode 100644 index 0000000..008ae4e --- /dev/null +++ b/packages/nocms/lib/debug.js @@ -0,0 +1,16 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.createDebug = createDebug; + +var _debug = require('debug'); + +var _debug2 = _interopRequireDefault(_debug); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function createDebug(namespace) { + return (0, _debug2.default)(`nocms:${namespace}`); +} \ No newline at end of file diff --git a/packages/nocms/lib/io.js b/packages/nocms/lib/io.js new file mode 100644 index 0000000..0c0c4ba --- /dev/null +++ b/packages/nocms/lib/io.js @@ -0,0 +1,241 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.writeFileAsync = exports.readFileAsync = exports.findFilesAsync = exports.cleanDirectoryAsync = undefined; + +let cleanDirectoryAsync = exports.cleanDirectoryAsync = (() => { + var _ref = _asyncToGenerator(function* (directory) { + debug('cleanDirectoryAsync(%s)', directory); + + return new Promise(function (resolve) { + _fsExtra2.default.emptyDir(directory, function (err) { + if (err) { + throw err; + } + + resolve(); + }); + }); + }); + + return function cleanDirectoryAsync(_x) { + return _ref.apply(this, arguments); + }; +})(); + +let findFilesAsync = exports.findFilesAsync = (() => { + var _ref5 = _asyncToGenerator(function* (pattern, options) { + debug('findFilesAsync(%s, %o)', pattern, options); + + return new Promise(function (resolve, reject) { + (0, _glob2.default)(pattern, options, function (err, files) { + if (err) { + return reject(err); + } + + resolve(files); + }); + }); + }); + + return function findFilesAsync(_x9, _x10) { + return _ref5.apply(this, arguments); + }; +})(); + +let readFileAsync = exports.readFileAsync = (() => { + var _ref6 = _asyncToGenerator(function* (file, options) { + debug('readFileAsync(%s, %o)', file, options); + + return new Promise(function (resolve, reject) { + _fs2.default.readFile(file, options, function (err, data) { + if (err) { + return reject(err); + } + + resolve(data); + }); + }); + }); + + return function readFileAsync(_x11, _x12) { + return _ref6.apply(this, arguments); + }; +})(); + +let writeFileAsync = exports.writeFileAsync = (() => { + var _ref7 = _asyncToGenerator(function* (file, data, options) { + debug('writeFileAsync(%s, %o, %o)', file, options); + + return new Promise(function (resolve) { + let directory = _path2.default.parse(file).dir; + + _fsExtra2.default.ensureDir(directory, function (err) { + if (err) { + throw err; + } + + _fsExtra2.default.outputFile(file, data, options, function (err) { + if (err) { + throw err; + } + + resolve(); + }); + }); + }); + }); + + return function writeFileAsync(_x13, _x14, _x15) { + return _ref7.apply(this, arguments); + }; +})(); + +exports.createDirectoryBoundFileExists = createDirectoryBoundFileExists; +exports.createDirectoryBoundFindFilesAsync = createDirectoryBoundFindFilesAsync; +exports.createDirectoryBoundReadFileAsync = createDirectoryBoundReadFileAsync; +exports.createDirectoryBoundResolvePath = createDirectoryBoundResolvePath; +exports.createDirectoryBoundWatchFiles = createDirectoryBoundWatchFiles; +exports.createDirectoryBoundWriteFileAsync = createDirectoryBoundWriteFileAsync; +exports.fileExists = fileExists; +exports.watchFiles = watchFiles; + +var _chokidar = require('chokidar'); + +var _chokidar2 = _interopRequireDefault(_chokidar); + +var _fs = require('fs'); + +var _fs2 = _interopRequireDefault(_fs); + +var _fsExtra = require('fs-extra'); + +var _fsExtra2 = _interopRequireDefault(_fsExtra); + +var _glob = require('glob'); + +var _glob2 = _interopRequireDefault(_glob); + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +var _debug = require('./debug'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +const debug = (0, _debug.createDebug)('io'); + +function createDirectoryBoundFileExists(directory) { + debug('createDirectoryBoundFileExists(%s)', directory); + + return function directoryBoundFileExists(file) { + debug('directoryBoundFileExists(%s)', file); + file = _path2.default.resolve(directory, file); + + return fileExists(file); + }; +} + +function createDirectoryBoundFindFilesAsync(directory) { + debug('createDirectoryBoundFindFilesAsync(%s)', directory); + + return (() => { + var _ref2 = _asyncToGenerator(function* (pattern, options) { + debug('directoryBoundFindFilesAsync(%s, %o)', pattern, options); + + pattern = _path2.default.posix.join(directory, pattern); + + return findFilesAsync(pattern, options).then(function (files) { + return files.map(function (file) { + return _path2.default.relative(directory, file); + }); + }); + }); + + function directoryBoundFindFilesAsync(_x2, _x3) { + return _ref2.apply(this, arguments); + } + + return directoryBoundFindFilesAsync; + })(); +} + +function createDirectoryBoundReadFileAsync(directory) { + debug('createDirectoryBoundReadFileAsync(%s)', directory); + + return (() => { + var _ref3 = _asyncToGenerator(function* (file, options) { + debug('directoryBoundReadFileAsync(%s, %d)', file, options); + + file = _path2.default.resolve(directory, file); + + return readFileAsync(file, options); + }); + + function directoryBoundReadFileAsync(_x4, _x5) { + return _ref3.apply(this, arguments); + } + + return directoryBoundReadFileAsync; + })(); +} + +function createDirectoryBoundResolvePath(directory) { + debug('createDirectoryBoundResolvePath(%s)', directory); + + return function directoryBoundResolvePath(file) { + debug('directoryBoundResolvePath(%s)', file); + + return _path2.default.resolve(directory, file); + }; +} + +function createDirectoryBoundWatchFiles(directory) { + debug('createDirectoryBoundWatchFiles(%s)', directory); + + return function directoryBoundWatchFiles(pattern, options) { + debug('directoryBoundWatchFiles(%s, %o)', pattern, options); + + options = options || {}; + options.cwd = directory; + + return watchFiles(pattern, options); + }; +} + +function createDirectoryBoundWriteFileAsync(directory) { + debug('createDirectoryBoundWriteFileAsync(%s)', directory); + + return (() => { + var _ref4 = _asyncToGenerator(function* (file, data, options) { + debug('directoryBoundWriteFileAsync(%s, %o, %o)', file, data, options); + + file = _path2.default.resolve(directory, file); + + return writeFileAsync(file, data, options); + }); + + function directoryBoundWriteFileAsync(_x6, _x7, _x8) { + return _ref4.apply(this, arguments); + } + + return directoryBoundWriteFileAsync; + })(); +} + +function fileExists(file) { + debug('fileExists(%s)', file); + + return _fs2.default.existsSync(file); +} + +function watchFiles(pattern, options) { + debug('watchFiles(%s, %o)', pattern, options); + + return _chokidar2.default.watch(pattern, options); +} \ No newline at end of file diff --git a/packages/nocms/lib/nocmsAscii.js b/packages/nocms/lib/nocmsAscii.js new file mode 100644 index 0000000..d3acfba --- /dev/null +++ b/packages/nocms/lib/nocmsAscii.js @@ -0,0 +1,17 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = ` + ███▄ █ ▒█████ ▄████▄ ███▄ ▄███▓ ██████ + ██ ▀█ █ ▒██▒ ██▒▒██▀ ▀█ ▓██▒▀█▀ ██▒▒██ ▒ +▓██ ▀█ ██▒▒██░ ██▒▒▓█ ▄ ▓██ ▓██░░ ▓██▄ +▓██▒ ▐▌██▒▒██ ██░▒▓▓▄ ▄██▒▒██ ▒██ ▒ ██▒ +▒██░ ▓██░░ ████▓▒░▒ ▓███▀ ░▒██▒ ░██▒▒██████▒▒ +░ ▒░ ▒ ▒ ░ ▒░▒░▒░ ░ ░▒ ▒ ░░ ▒░ ░ ░▒ ▒▓▒ ▒ ░ +░ ░░ ░ ▒░ ░ ▒ ▒░ ░ ▒ ░ ░ ░░ ░▒ ░ ░ + ░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ + ░ ░ ░ ░ ░ ░ ░ + ░ +`; \ No newline at end of file diff --git a/packages/nocms/lib/plugins.js b/packages/nocms/lib/plugins.js new file mode 100644 index 0000000..cbd2f25 --- /dev/null +++ b/packages/nocms/lib/plugins.js @@ -0,0 +1,44 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.loadPlugins = undefined; + +let loadPlugins = exports.loadPlugins = (() => { + var _ref = _asyncToGenerator(function* (pluginActivationContext) { + return Promise.all([(0, _io.findFilesAsync)('node_modules/nocms-plugin-*/', {}).then(function (pluginDirectories) { + return pluginDirectories.map(function (pluginsDirectory) { + return _path2.default.basename(pluginsDirectory); + }); + }), (0, _io.findFilesAsync)('plugins/nocms-plugin-*/', { cwd: __dirname }).then(function (pluginDirectories) { + return pluginDirectories.map(function (pluginsDirectory) { + return './' + pluginsDirectory; + }); + })]).then(function (modules) { + return [].concat.apply([], modules); + }).then(function (modules) { + return modules.map(function (moduleName) { + return require(moduleName); + }); + }).then(function (plugins) { + return plugins.map(function (plugin) { + plugin.activate(pluginActivationContext); + }); + }); + }); + + return function loadPlugins(_x) { + return _ref.apply(this, arguments); + }; +})(); + +var _io = require('./io.js'); + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } \ No newline at end of file diff --git a/packages/nocms/lib/server.js b/packages/nocms/lib/server.js new file mode 100644 index 0000000..efc9d34 --- /dev/null +++ b/packages/nocms/lib/server.js @@ -0,0 +1,74 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.createServer = createServer; + +var _express = require('express'); + +var _express2 = _interopRequireDefault(_express); + +var _url = require('url'); + +var _url2 = _interopRequireDefault(_url); + +var _debug = require('./debug'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +const debug = (0, _debug.createDebug)('server'); + +function createServer({ resolveOutputPath, resourceProvider, commandSender, port }) { + debug('createServer()'); + + const app = (0, _express2.default)(); + let count = 0; + + app.get('/*', (() => { + var _ref = _asyncToGenerator(function* (req, res, next) { + try { + const resourceId = _url2.default.parse(req.url).pathname; + const resources = yield resourceProvider.getResources(); + const resource = resources.find(function (resource) { + return resource.id === resourceId; + }); + + if (!resource) { + throw new Error('Resource Not Found'); + } + + yield commandSender.sendCommand({ + id: ++count, + name: 'compileResource', + params: { resourceId } + }); + + res.setHeader('Content-Type', resource.mimeType); + res.statusCode = 200; + res.sendFile(resolveOutputPath(resource.outFile)); + } catch (err) { + if (err.message === 'Resource Not Found') { + res.statusCode = 404; + } else { + res.statusCode = 500; + } + + if (err.message) { + res.write(err.message); + } + res.end(); + } + }); + + return function (_x, _x2, _x3) { + return _ref.apply(this, arguments); + }; + })()); + + app.listen(port, () => { + console.info(`listening on port ${port}`); + }); +} \ No newline at end of file diff --git a/packages/nocms/lib/threading.js b/packages/nocms/lib/threading.js new file mode 100644 index 0000000..d5139cb --- /dev/null +++ b/packages/nocms/lib/threading.js @@ -0,0 +1,331 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.BUSY = exports.IDLE = undefined; +exports.createCommandWorkerProcess = createCommandWorkerProcess; +exports.createCommandWorkerProcessPool = createCommandWorkerProcessPool; +exports.createCommandSender = createCommandSender; +exports.createCommandReceiver = createCommandReceiver; +exports.createCommandDispatcher = createCommandDispatcher; +exports.createCommandHandler = createCommandHandler; + +var _child_process = require('child_process'); + +var _debug = require('./debug'); + +function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } + +const debug = (0, _debug.createDebug)('threading'); + +const IDLE = exports.IDLE = 'IDLE'; +const BUSY = exports.BUSY = 'BUSY'; + +function createCommandWorkerProcess(id, moduleName, args) { + let sendCommand = (() => { + var _ref = _asyncToGenerator(function* (command) { + debug('commandWorkerProcess.%d.sendCommand: %o', id, command); + busy(); + + let message = { + topic: 'handleCommand', + data: command + }; + + child.send(message); + + return new Promise(function (resolve, reject) { + return commandPromises[command.id] = { resolve, reject }; + }).then(function (result) { + idle(); + return result; + }).catch(function (err) { + idle(); + throw err; + }); + }); + + return function sendCommand(_x) { + return _ref.apply(this, arguments); + }; + })(); + + debug('createCommandWorkerProcess(%d, %s, %o)', id, moduleName, args); + + moduleName = moduleName || process.argv[1]; + args = args || process.argv.slice(3); + + const commandPromises = {}; + const child = (0, _child_process.fork)(moduleName, args); + child.id = id; + child.idle = idle; + child.busy = busy; + child.sendCommand = sendCommand; + child.on('message', handleMessage); + child.on('error', handleError); + child.on('exit', handleExit); + + return child; + + function handleError(err) { + debug('commandWorkerProcess.%d.handleError(%o)', id, err); + + throw err; + } + + function handleExit(code) { + debug('commandWorkerProcess.%d.handleError(%d)', id, code); + + if (code) { + throw new Error(`commandWorkerProcess.${id} exited with non zero code: ${code}`); + } + } + + function handleMessage(message) { + debug('commandWorkerProcess.%d.handleMessage(%o)', id, message); + switch (message.topic) { + case 'busy': + busy(); + break; + + case 'idle': + idle(); + break; + + case 'commandHandled': + (() => { + let commandId = message.data.commandId; + let commandPromise = commandPromises[commandId]; + delete commandPromise[commandId]; + commandPromise.resolve(message.data); + })(); + break; + + case 'commandNotHandled': + (() => { + let commandId = message.data.commandId; + let commandPromise = commandPromises[commandId]; + delete commandPromise[commandId]; + commandPromise.reject(message.data); + })(); + break; + } + } + + function idle() { + debug('commandWorkerProcess.%d.idle', id); + child.state = IDLE; + child.emit('idle'); + } + + function busy() { + debug('commandWorkerProcess.%d.busy', id); + child.state = BUSY; + child.emit('busy'); + } +} + +function createCommandWorkerProcessPool(size, moduleName, args) { + let getFirstIdleCommandWorkerProcess = (() => { + var _ref2 = _asyncToGenerator(function* () { + debug('getFirstIdleCommandWorkerProcess()'); + + const firstIdleWorkerProcess = idleCommandWorkerProcesses.shift(); + + if (firstIdleWorkerProcess) { + firstIdleWorkerProcess.busy(); + + return Promise.resolve(firstIdleWorkerProcess); + } + + return new Promise(function (resolve, reject) { + return promises.push({ resolve, reject }); + }); + }); + + return function getFirstIdleCommandWorkerProcess() { + return _ref2.apply(this, arguments); + }; + })(); + + debug('createCommandWorkerProcessPool(%d, %s, %o)', size, moduleName, args); + + const promises = []; + const commandWorkerProcesses = Array.apply(null, { length: size }).map((value, id) => createCommandWorkerProcess(id, moduleName, args)); + const idleCommandWorkerProcesses = []; + + process.on('exit', code => { + debug('process.on(exit, %d)', code); + + commandWorkerProcesses.forEach(commandWorkerProcess => commandWorkerProcess.kill()); + }); + + process.on('SIGINT', () => { + debug('process.on(SIGINT)'); + + process.exit(1); + }); + + process.on('SIGTERM', () => { + debug('process.on(SIGTERM)'); + + process.exit(1); + }); + + commandWorkerProcesses.forEach(commandWorkerProcess => { + commandWorkerProcess.on('idle', () => { + debug('commandWorkerProcess.%d.on(%s)', commandWorkerProcess.id, 'idle'); + + idleCommandWorkerProcesses.push(commandWorkerProcess); + + const promise = promises.shift(); + + if (promise) { + const nextIdleCommandWorkerProcess = idleCommandWorkerProcesses.shift(); + + promise.resolve(nextIdleCommandWorkerProcess); + } + }); + + commandWorkerProcess.on('busy', () => { + debug('commandWorkerProcess.%d.on(%s)', commandWorkerProcess.id, 'busy'); + + const indexOfCommandWorkerProcess = idleCommandWorkerProcesses.indexOf(commandWorkerProcess); + + if (indexOfCommandWorkerProcess > -1) { + idleCommandWorkerProcesses.splice(indexOfCommandWorkerProcess, 1); + } + }); + }); + + return { getFirstIdleCommandWorkerProcess }; +} + +// Send a command +function createCommandSender(commandWorkerProcessPool) { + let sendCommand = (() => { + var _ref3 = _asyncToGenerator(function* (command) { + debug('commandSender.sendCommand(%o)', command); + + try { + + let commandWorkerProcess = yield commandWorkerProcessPool.getFirstIdleCommandWorkerProcess(); + + return commandWorkerProcess.sendCommand(command); + } catch (err) { + throw err; + } + }); + + return function sendCommand(_x2) { + return _ref3.apply(this, arguments); + }; + })(); + + debug('createCommandSender()'); + + let commandSender = { sendCommand }; + + commandWorkerProcessPool = commandWorkerProcessPool || createCommandWorkerProcessPool(); + + return commandSender; +} + +// Listens for process message and calls the command dispatcher when it receives a command; +function createCommandReceiver(commandDispatcher) { + debug('createCommandReceiver()'); + + process.on('message', handleMessage); + + return { busy, idle }; + + function handleMessage(message) { + debug('commandReceiver.handleMessage(%o)', message); + + switch (message.topic) { + case 'handleCommand': + let command = message.data; + + commandDispatcher.dispatchCommand(command).then(result => { + let message = { + topic: 'commandHandled', + data: { + commandId: command.id, + result + } + }; + + process.send(message); + }).catch(err => { + let message = { + topic: 'commandNotHandled', + data: { + commandId: command.id, + message: err.message, + stack: err.stack + } + }; + + process.send(message); + }); + break; + } + } + + function busy() { + debug('commandReceiver.busy()'); + process.send({ topic: 'busy' }); + } + + function idle() { + debug('commandReceiver.idle()'); + process.send({ topic: 'idle' }); + } +} + +// Creates a CommandDispatcher that Dispatches Commands to a CommandHandlers +function createCommandDispatcher(commandHandlers) { + let dispatchCommand = (() => { + var _ref4 = _asyncToGenerator(function* (command) { + let commandHandler = commandHandlers.filter(function (commandHandler) { + return commandHandler.canHandleCommand(command.name); + })[0]; + + if (!commandHandler) { + throw new Error('Command Handler Not Found'); + } + + return commandHandler.handleCommand(command); + }); + + return function dispatchCommand(_x3) { + return _ref4.apply(this, arguments); + }; + })(); + + debug('createCommandDispatcher()'); + + return { dispatchCommand }; +} + +// Creates a CommandHandler that Handles Command +function createCommandHandler(commandNames, _handleCommand) { + let handleCommand = (() => { + var _ref5 = _asyncToGenerator(function* (command) { + return _handleCommand(command); + }); + + return function handleCommand(_x4) { + return _ref5.apply(this, arguments); + }; + })(); + + debug('createCommandHandler()'); + + function canHandleCommand(commandName) { + return commandNames.includes(commandName); + } + + return { handleCommand, canHandleCommand }; +} \ No newline at end of file