Comparing version 0.0.8 to 0.0.10
12
index.js
@@ -1,11 +0,1 @@ | ||
const logger = require('./src/logger') | ||
const main = require('./src/main') | ||
const { logo } = require('./src/strings') | ||
const commands = process.argv.splice(process.execArgv.length + 2) | ||
logger.log(logo) | ||
main(commands).catch(err => { | ||
logger.error('Generation finished with error: ', err) | ||
}) | ||
require('./src/main')() |
{ | ||
"name": "symply", | ||
"version": "0.0.8", | ||
"version": "0.0.10", | ||
"description": "A dead-simple Bootstrap static site generator.", | ||
@@ -27,4 +27,5 @@ "author": "Oleg Legun <oleg.legun@gmail.com>", | ||
"handlebars": "^4.7.2", | ||
"js-yaml": "^3.13.1", | ||
"prettier": "^1.19.1" | ||
"js-yaml": "3.13.1", | ||
"prettier": "1.19.1", | ||
"yargs": "15.1.0" | ||
}, | ||
@@ -31,0 +32,0 @@ "devDependencies": { |
@@ -8,3 +8,2 @@ ![Logo](./assets/logo.png) | ||
- Total control of the generation process | ||
- Only few dependencies | ||
@@ -11,0 +10,0 @@ **Symply** is based on well-known fast and reliable [Handlebars.js](https://github.com/wycats/handlebars.js) templating engine. |
@@ -8,7 +8,7 @@ const fs = require('fs') | ||
EmptyConfigurationFileError, | ||
MissingRequiredConfigurationOptionError, | ||
} = require('./errors') | ||
// Mandatory configuration keys | ||
const DEFAULT_CONFIGURATION = { | ||
// directories | ||
SOURCE_DIR_NAME: 'src', | ||
@@ -19,3 +19,5 @@ DISTRIBUTION_DIR_NAME: 'dist', | ||
TEMPLATES_DIR_NAME: 'templates', | ||
IGNORE_MISSING_PROPERTIES: false | ||
// other options | ||
IGNORE_MISSING_PROPERTIES: false, | ||
} | ||
@@ -38,5 +40,13 @@ | ||
}, | ||
{ name: CONFIGURATION_FILE_NAME, dir: '.', contents: yaml.dump(DEFAULT_CONFIGURATION) }, | ||
{ | ||
name: CONFIGURATION_FILE_NAME, | ||
dir: '.', | ||
contents: | ||
'# Here you can change the default configuration\n' + yaml.dump(DEFAULT_CONFIGURATION), | ||
}, | ||
] | ||
/** | ||
* @return {DEFAULT_CONFIGURATION} | ||
*/ | ||
function getConfiguration() { | ||
@@ -54,3 +64,3 @@ try { | ||
return configuration | ||
return { ...DEFAULT_CONFIGURATION, ...configuration } | ||
} catch (err) { | ||
@@ -63,5 +73,5 @@ if (err.code === 'ENOENT') { | ||
process.exit(0) | ||
} else if (err instanceof MissingRequiredConfigurationOptionError) { | ||
logger.error(err.message) | ||
process.exit(0) | ||
// } else if (err instanceof MissingRequiredConfigurationOptionError) { | ||
// logger.error(err.message) | ||
// process.exit(0) | ||
} else if (err instanceof EmptyConfigurationFileError) { | ||
@@ -91,7 +101,7 @@ logger.info(err.message) | ||
defaultConfigurationKeys.forEach(key => { | ||
if (customConfigurationKeys.indexOf(key) === -1) { | ||
throw new MissingRequiredConfigurationOptionError(key, defaultConfigurationKeys) | ||
} | ||
}) | ||
// defaultConfigurationKeys.forEach(key => { | ||
// if (customConfigurationKeys.indexOf(key) === -1) { | ||
// throw new MissingRequiredConfigurationOptionError(key, defaultConfigurationKeys) | ||
// } | ||
// }) | ||
} | ||
@@ -105,3 +115,3 @@ | ||
let result = '' | ||
let paddingChars = ' ' | ||
const paddingChars = ' ' | ||
@@ -108,0 +118,0 @@ Object.keys(configObject).forEach(key => { |
@@ -106,3 +106,3 @@ const fs = require('fs-extra') | ||
* | ||
* @returns {boolean} Returns true if the directory is created | ||
* @return {boolean} Returns true if the directory is created | ||
*/ | ||
@@ -123,3 +123,3 @@ function createDirectoryIfNotExists(directoryPath) { | ||
* | ||
* @returns {boolean} Returns true if the file is created | ||
* @return {boolean} Returns true if the file is created | ||
*/ | ||
@@ -140,3 +140,3 @@ function createFileIfNotExists(filePath, contents) { | ||
* @param {string[]} pathParts | ||
* @returns {string} | ||
* @return {string} | ||
*/ | ||
@@ -143,0 +143,0 @@ function joinAndResolvePath(...pathParts) { |
284
src/main.js
@@ -1,225 +0,79 @@ | ||
const fs = require('fs') | ||
const path = require('path') | ||
const prettier = require('prettier') | ||
const Handlebars = require('handlebars') | ||
const config = require('./config') | ||
const yargs = require('yargs') | ||
const logger = require('./logger') | ||
const strings = require('./strings') | ||
const { loadPartials } = require('./partials') | ||
const { loadTemplates } = require('./templates') | ||
const { loadViews } = require('./views') | ||
const { | ||
scanFiles, | ||
isFileExtensionValid, | ||
getFileContents, | ||
createFileIfNotExists, | ||
createDirectoryIfNotExists, | ||
clearDirectoryContents, | ||
copyFileAsync, | ||
createDirectoryAsync, | ||
joinAndResolvePath, | ||
} = require('./fs-helpers') | ||
const version = require('../package').version | ||
const initialize = require('./initialize') | ||
const generate = require('./generate') | ||
const { Commands } = require('./cli-commands') | ||
async function main(commands) { | ||
switch (commands[0]) { | ||
case 'init': | ||
logger.info('Initializing project...') | ||
initialize() | ||
logger.info('Initialization successfully done.') | ||
break | ||
case 'generate': | ||
case 'start': | ||
await generate() | ||
break | ||
default: | ||
logger.log(strings.help) | ||
} | ||
} | ||
function main() { | ||
logger.log(strings.logo) | ||
/** | ||
* | ||
*/ | ||
async function generate() { | ||
/*----------------------------------------------------------------------------- | ||
* Load configuration | ||
*----------------------------------------------------------------------------*/ | ||
try { | ||
yargs | ||
.usage('Usage: $0 <command> [option]') | ||
.strict() | ||
/*----------------------------------------------------------------------------- | ||
* Init | ||
*----------------------------------------------------------------------------*/ | ||
.command([Commands.INIT.name], Commands.INIT.description, {}, args => { | ||
logger.info('Initializing project...') | ||
initialize() | ||
logger.info('Initialization successfully done.') | ||
}) | ||
/*----------------------------------------------------------------------------- | ||
* Generate | ||
*----------------------------------------------------------------------------*/ | ||
.command([Commands.GENERATE.name, Commands.GENERATE.alias], Commands.GENERATE.description, {}, args => { | ||
const start = Date.now() | ||
logger.info('Generating static files...') | ||
generate() | ||
.then(stats => { | ||
logger.info( | ||
`Generated ${stats.generatedFilesCount} files. Copied ${stats.copiedFilesCount} files.` | ||
) | ||
logger.info(`Generation successfully finished in ${Date.now() - start} ms.`) | ||
}) | ||
.catch(err => { | ||
throw err | ||
}) | ||
}) | ||
/*----------------------------------------------------------------------------- | ||
* Serve | ||
*----------------------------------------------------------------------------*/ | ||
.command([Commands.SERVE.name], Commands.SERVE.description, {}, args => { | ||
logger.info('Not implemented yet...') | ||
}) | ||
/*----------------------------------------------------------------------------- | ||
* Configuration | ||
*----------------------------------------------------------------------------*/ | ||
.command([Commands.CONFIGURATION.name], Commands.CONFIGURATION.description, {}, args => { | ||
logger.info('Not implemented yet...') | ||
}) | ||
/*----------------------------------------------------------------------------- | ||
* Bootstrap | ||
*----------------------------------------------------------------------------*/ | ||
.command([Commands.BOOTSTRAP.name], Commands.BOOTSTRAP.description, {}, args => { | ||
logger.info('Not implemented yet...') | ||
}) | ||
/*----------------------------------------------------------------------------- | ||
* Help | ||
*----------------------------------------------------------------------------*/ | ||
.command([Commands.HELP.name, Commands.HELP.alias], Commands.HELP.description, {}, args => { | ||
logger.info('Not implemented yet...') | ||
}) | ||
const configuration = config.getConfiguration() | ||
// disable default options '--optionName' | ||
.version(false) | ||
.help(false) | ||
/*----------------------------------------------------------------------------- | ||
* Load and register partials | ||
*----------------------------------------------------------------------------*/ | ||
const partials = loadPartials(configuration.PARTIALS_DIR_NAME) | ||
Object.keys(partials).forEach(name => { | ||
Handlebars.registerPartial(name, partials[name]) | ||
}) | ||
/*----------------------------------------------------------------------------- | ||
* Load views, templates and globals | ||
*----------------------------------------------------------------------------*/ | ||
const views = loadViews(configuration.VIEWS_DIR_NAME) | ||
const globals = require(joinAndResolvePath(config.GLOBALS_FILE_NAME)) | ||
const templates = loadTemplates(configuration.TEMPLATES_DIR_NAME) | ||
/*----------------------------------------------------------------------------- | ||
* Load and register helpers; Inject views | ||
*----------------------------------------------------------------------------*/ | ||
if (fs.existsSync(config.HELPERS_FILE_NAME)) { | ||
const helpersPath = joinAndResolvePath(config.HELPERS_FILE_NAME) | ||
const helpers = require(helpersPath) | ||
Object.keys(helpers).forEach(helperName => { | ||
Handlebars.registerHelper(helperName, injectHelperContextDecorator(helpers[helperName], views, globals)) | ||
}) | ||
// show help if no command is provided | ||
.showHelpOnFail(true) | ||
.epilog(`SYMPLY v${version}`) | ||
.demandCommand().argv | ||
} catch (err) { | ||
logger.error('Unhandled Error: ', err) | ||
} | ||
Handlebars.registerHelper('template', injectHelperContextDecorator(templateHelper, views, globals)) | ||
function templateHelper(templateName, data) { | ||
if (!templates[templateName]) { | ||
logger.error(`Template '${templateName}' is not found in directory '${configuration.TEMPLATES_DIR_NAME}/'.`) | ||
process.exit(1) | ||
} | ||
return templates[templateName].replace('{{}}', data.fn(this)) | ||
} | ||
if (!configuration.IGNORE_MISSING_PROPERTIES) { | ||
Handlebars.registerHelper('helperMissing', missingHelperOrPropertyHandler) | ||
} | ||
/*----------------------------------------------------------------------------- | ||
* Scan source files, detect template files for processing | ||
*----------------------------------------------------------------------------*/ | ||
const allSourceFiles = scanFiles(configuration.SOURCE_DIR_NAME, false, true, true) | ||
const templateSourceFiles = allSourceFiles.filter(file => { | ||
return isFileExtensionValid(file.name, ['html']) | ||
}) | ||
const otherSourceFiles = allSourceFiles.filter(file => { | ||
return !isFileExtensionValid(file.name, ['html']) | ||
}) | ||
/*----------------------------------------------------------------------------- | ||
* Compile templates with passing globals | ||
* Format HTML output | ||
* Save results to the distribution directory | ||
*----------------------------------------------------------------------------*/ | ||
clearDirectoryContents(joinAndResolvePath(configuration.DISTRIBUTION_DIR_NAME)) | ||
templateSourceFiles.forEach(file => { | ||
const templateContents = getFileContents( | ||
joinAndResolvePath(configuration.SOURCE_DIR_NAME, file.dirname, file.name) | ||
) | ||
let formattedHTML = '' | ||
try { | ||
const result = Handlebars.compile(templateContents)(globals) | ||
formattedHTML = prettier.format(result, { parser: 'html' }) | ||
} catch (err) { | ||
if (err instanceof RangeError) { | ||
logger.error( | ||
'Recursive partial structure detected. Check your partials and source files. ' + | ||
'Make sure that partials are not calling each other using {{> partialName}}.' | ||
) | ||
} else { | ||
logger.error(err) | ||
} | ||
process.exit(1) | ||
} | ||
createFileIfNotExists( | ||
joinAndResolvePath(configuration.DISTRIBUTION_DIR_NAME, file.dirname, file.name), | ||
formattedHTML | ||
) | ||
}) | ||
/*----------------------------------------------------------------------------- | ||
* Copy other source files to the distribution directory | ||
*----------------------------------------------------------------------------*/ | ||
const copyPromises = [] | ||
otherSourceFiles.forEach(file => { | ||
const srcFilePath = joinAndResolvePath(configuration.SOURCE_DIR_NAME, file.dirname, file.name) | ||
const fileDir = joinAndResolvePath(configuration.DISTRIBUTION_DIR_NAME, file.dirname) | ||
const distFilePath = joinAndResolvePath(configuration.DISTRIBUTION_DIR_NAME, file.dirname, file.name) | ||
copyPromises.push(createDirectoryAsync(fileDir).then(() => copyFileAsync(srcFilePath, distFilePath))) | ||
}) | ||
await Promise.all(copyPromises) | ||
logger.info('Generation successfully finished.') | ||
} | ||
function initialize() { | ||
/*----------------------------------------------------------------------------- | ||
* Load configuration | ||
*----------------------------------------------------------------------------*/ | ||
const configuration = config.getConfiguration() | ||
/*----------------------------------------------------------------------------- | ||
* Create system files and directories | ||
*----------------------------------------------------------------------------*/ | ||
config.systemFilesToBeCreated.forEach(file => { | ||
const fileIsCreated = createFileIfNotExists(path.join(file.dir, file.name), file.contents) | ||
if (fileIsCreated) { | ||
logger.info('Created file ' + file.name) | ||
} | ||
}) | ||
Object.keys(configuration) | ||
.filter(key => key.endsWith('DIR_NAME')) | ||
.forEach(dirNameKey => { | ||
const dirName = configuration[dirNameKey] | ||
const dirIsCreated = createDirectoryIfNotExists(dirName) | ||
if (dirIsCreated) { | ||
logger.info(`Created directory: ${dirName}/`) | ||
} | ||
}) | ||
} | ||
/** | ||
* | ||
* @param {(...args:Object[])=> {}} helperFunction | ||
* @param {Object<string, string>} views | ||
* @param {Object} globals | ||
*/ | ||
function injectHelperContextDecorator(helperFunction, views, globals) { | ||
return function(...args) { | ||
const passedArgs = args.slice(0, args.length - 1) | ||
const data = args[args.length - 1] | ||
const fn = data.fn // if block helper | ||
const viewName = data.hash && data.hash.view | ||
return new Handlebars.SafeString( | ||
viewName | ||
? helperFunction(...passedArgs, { globals, params: data.hash, view: views[viewName], fn }) | ||
: helperFunction(...passedArgs, { globals, params: data.hash, fn }) | ||
) | ||
} | ||
} | ||
function detectBlockHelper() {} | ||
function missingHelperOrPropertyHandler(data) { | ||
Object.keys(data).forEach(item => { | ||
const message = 'Missing helper or property: ' + data.name | ||
logger.error(message) | ||
throw new Error(message) | ||
}) | ||
} | ||
module.exports = main |
module.exports = { | ||
logo: ` | ||
███████╗ ████████╗ ███╗ ███╗ ████████╗ ██╗ ████████╗ | ||
@@ -12,16 +11,2 @@ ████▌/═╝ ╚██████╔╝ ████╗ ████║ ███╔═══██╗ ██║ ╚██████╔╝ | ||
`, | ||
help: ` | ||
Usage: symply <command> [options] | ||
Available commands: | ||
symply init Initialize project creating necessary files and directories | ||
symply generate Compile and generate static site files | ||
alias: symply start | ||
symply configuration Print current generator configuration (default + configuration.yaml) | ||
symply serve Start development server in distribution folder | ||
`, | ||
} |
@@ -13,3 +13,4 @@ const path = require('path') | ||
/** | ||
* @returns {Views} | ||
* @param {string} viewsPath | ||
* @return {Views} | ||
*/ | ||
@@ -31,3 +32,3 @@ function loadViews(viewsPath) { | ||
* @param {string} fileName | ||
* @returns {string} | ||
* @return {string} | ||
*/ | ||
@@ -38,2 +39,2 @@ function getViewName(fileName) { | ||
module.exports = { loadViews } | ||
module.exports = { loadViews } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
148968
28
766
5
25
+ Addedyargs@15.1.0
+ Addedansi-regex@5.0.1(transitive)
+ Addedansi-styles@4.3.0(transitive)
+ Addedcamelcase@5.3.1(transitive)
+ Addedcliui@6.0.0(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addeddecamelize@1.2.0(transitive)
+ Addedemoji-regex@8.0.0(transitive)
+ Addedfind-up@4.1.0(transitive)
+ Addedget-caller-file@2.0.5(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedjs-yaml@3.13.1(transitive)
+ Addedlocate-path@5.0.0(transitive)
+ Addedp-limit@2.3.0(transitive)
+ Addedp-locate@4.1.0(transitive)
+ Addedp-try@2.2.0(transitive)
+ Addedpath-exists@4.0.0(transitive)
+ Addedrequire-directory@2.1.1(transitive)
+ Addedrequire-main-filename@2.0.0(transitive)
+ Addedset-blocking@2.0.0(transitive)
+ Addedstring-width@4.2.3(transitive)
+ Addedstrip-ansi@6.0.1(transitive)
+ Addedwhich-module@2.0.1(transitive)
+ Addedwrap-ansi@6.2.0(transitive)
+ Addedy18n@4.0.3(transitive)
+ Addedyargs@15.1.0(transitive)
+ Addedyargs-parser@16.1.0(transitive)
- Removedjs-yaml@3.14.1(transitive)
Updatedjs-yaml@3.13.1
Updatedprettier@1.19.1