From 1a3266e82a116eff4fbd34c441d04efbfbb8743c Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Sun, 28 May 2017 12:19:03 +0200 Subject: [PATCH 01/58] Use Jest and babel for tests, change indent to 2 --- .babelrc | 15 + .editorconfig | 16 + .eslintignore | 11 +- .eslintrc | 213 ------- .eslintrc.js | 60 ++ .jestrc.js | 37 ++ .travis.yml | 17 +- Makefile | 40 ++ VERSION.txt | 1 - docs/CHANGELOG.md | 8 + docs/CONTRIBUTING.md | 18 +- index.js | 13 +- jyt | 99 ++-- lib/constants.js | 42 +- lib/log-wrapper.js | 68 ++- lib/middleware.js | 27 +- lib/options-handler.js | 830 ++++++++++++++-------------- lib/options-schema.js | 0 lib/reader.js | 443 +++++++-------- lib/transformer.js | 635 +++++++++++---------- lib/type-definitions.js | 45 ++ lib/validator.js | 80 +-- lib/writer.js | 890 +++++++++++++++--------------- package.json | 207 +++---- test/helper-constants.js | 36 ++ test/logger.js | 136 +++-- test/test-log-wrapper.js | 213 ------- test/test-middleware.js | 153 ----- test/test-options-handler.js | 552 ------------------ test/test-reader.js | 510 ----------------- test/test-transformer.js | 332 ----------- test/test-validator.js | 44 -- test/test-writer.js | 636 --------------------- test/unit/test-index.js | 60 ++ test/unit/test-log-wrapper.js | 236 ++++++++ test/unit/test-middleware.js | 124 +++++ test/unit/test-options-handler.js | 338 ++++++++++++ test/unit/test-reader.js | 251 +++++++++ test/unit/test-transformer.js | 301 ++++++++++ test/unit/test-validator.js | 34 ++ test/unit/test-writer.js | 423 ++++++++++++++ 41 files changed, 3802 insertions(+), 4392 deletions(-) create mode 100644 .babelrc create mode 100644 .editorconfig delete mode 100644 .eslintrc create mode 100644 .eslintrc.js create mode 100644 .jestrc.js create mode 100644 Makefile delete mode 100644 VERSION.txt create mode 100644 lib/options-schema.js create mode 100644 lib/type-definitions.js create mode 100644 test/helper-constants.js delete mode 100644 test/test-log-wrapper.js delete mode 100644 test/test-middleware.js delete mode 100644 test/test-options-handler.js delete mode 100644 test/test-reader.js delete mode 100644 test/test-transformer.js delete mode 100644 test/test-validator.js delete mode 100644 test/test-writer.js create mode 100644 test/unit/test-index.js create mode 100644 test/unit/test-log-wrapper.js create mode 100644 test/unit/test-middleware.js create mode 100644 test/unit/test-options-handler.js create mode 100644 test/unit/test-reader.js create mode 100644 test/unit/test-transformer.js create mode 100644 test/unit/test-validator.js create mode 100644 test/unit/test-writer.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..0ab1c57 --- /dev/null +++ b/.babelrc @@ -0,0 +1,15 @@ +{ + "presets": [ + "stage-0", + ["env", { + "targets": { + "node": "current" + } + }] + ], + "plugins": [ + ["babel-plugin-transform-builtin-extend", { + "globals": ["Object", "Error", "Array"] + }] + ] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1970b3c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[Makefile] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore index 96212a3..dc14c97 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,10 @@ -**/*{.,-}min.js +coverage/** +node_modules/** +api-docs +coverage/ +readme/ +.idea +lib/ +index.js +test/data +test/tmp diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 9faa375..0000000 --- a/.eslintrc +++ /dev/null @@ -1,213 +0,0 @@ -ecmaFeatures: - modules: true - jsx: true - -env: - amd: true - browser: true - es6: true - jquery: true - node: true - -# http://eslint.org/docs/rules/ -rules: - # Possible Errors - comma-dangle: [2, never] - no-cond-assign: 2 - no-console: 0 - no-constant-condition: 2 - no-control-regex: 2 - no-debugger: 2 - no-dupe-args: 2 - no-dupe-keys: 2 - no-duplicate-case: 2 - no-empty: 2 - no-empty-character-class: 2 - no-ex-assign: 2 - no-extra-boolean-cast: 2 - no-extra-parens: 0 - no-extra-semi: 2 - no-func-assign: 2 - no-inner-declarations: [2, functions] - no-invalid-regexp: 2 - no-irregular-whitespace: 2 - no-negated-in-lhs: 2 - no-obj-calls: 2 - no-regex-spaces: 2 - no-sparse-arrays: 2 - no-unexpected-multiline: 2 - no-unreachable: 2 - use-isnan: 2 - valid-jsdoc: 0 - valid-typeof: 2 - - # Best Practices - accessor-pairs: 2 - block-scoped-var: 0 - complexity: [2, 6] - consistent-return: 0 - curly: 0 - default-case: 0 - dot-location: 0 - dot-notation: 0 - eqeqeq: 2 - guard-for-in: 2 - no-alert: 2 - no-caller: 2 - no-case-declarations: 2 - no-div-regex: 2 - no-else-return: 0 - no-empty-label: 2 - no-empty-pattern: 2 - no-eq-null: 2 - no-eval: 2 - no-extend-native: 2 - no-extra-bind: 2 - no-fallthrough: 2 - no-floating-decimal: 0 - no-implicit-coercion: 0 - no-implied-eval: 2 - no-invalid-this: 0 - no-iterator: 2 - no-labels: 0 - no-lone-blocks: 2 - no-loop-func: 2 - no-magic-number: 0 - no-multi-spaces: 0 - no-multi-str: 0 - no-native-reassign: 2 - no-new-func: 2 - no-new-wrappers: 2 - no-new: 2 - no-octal-escape: 2 - no-octal: 2 - no-proto: 2 - no-redeclare: 2 - no-return-assign: 2 - no-script-url: 2 - no-self-compare: 2 - no-sequences: 0 - no-throw-literal: 0 - no-unused-expressions: 2 - no-useless-call: 2 - no-useless-concat: 2 - no-void: 2 - no-warning-comments: 0 - no-with: 2 - radix: 2 - vars-on-top: 0 - wrap-iife: 2 - yoda: 0 - - # Strict - strict: 0 - - # Variables - init-declarations: 0 - no-catch-shadow: 2 - no-delete-var: 2 - no-label-var: 2 - no-shadow-restricted-names: 2 - no-shadow: 0 - no-undef-init: 2 - no-undef: 0 - no-undefined: 0 - no-unused-vars: 0 - no-use-before-define: 0 - - # Node.js and CommonJS - callback-return: 2 - global-require: 2 - handle-callback-err: 2 - no-mixed-requires: 0 - no-new-require: 0 - no-path-concat: 2 - no-process-exit: 2 - no-restricted-modules: 0 - no-sync: 0 - - # Stylistic Issues - array-bracket-spacing: 0 - block-spacing: 0 - brace-style: 0 - camelcase: 0 - comma-spacing: 0 - comma-style: 0 - computed-property-spacing: 0 - consistent-this: 0 - eol-last: 0 - func-names: 0 - func-style: 0 - id-length: 0 - id-match: 0 - indent: 0 - jsx-quotes: 0 - key-spacing: 0 - linebreak-style: 0 - lines-around-comment: 0 - max-depth: 0 - max-len: 0 - max-nested-callbacks: 0 - max-params: 0 - max-statements: [2, 30] - new-cap: 0 - new-parens: 0 - newline-after-var: 0 - no-array-constructor: 0 - no-bitwise: 0 - no-continue: 0 - no-inline-comments: 0 - no-lonely-if: 0 - no-mixed-spaces-and-tabs: 0 - no-multiple-empty-lines: 0 - no-negated-condition: 0 - no-nested-ternary: 0 - no-new-object: 0 - no-plusplus: 0 - no-restricted-syntax: 0 - no-spaced-func: 0 - no-ternary: 0 - no-trailing-spaces: 0 - no-underscore-dangle: 0 - no-unneeded-ternary: 0 - object-curly-spacing: 0 - one-var: 0 - operator-assignment: 0 - operator-linebreak: 0 - padded-blocks: 0 - quote-props: 0 - quotes: 0 - require-jsdoc: 0 - semi-spacing: 0 - semi: 0 - sort-vars: 0 - space-after-keywords: 0 - space-before-blocks: 0 - space-before-function-paren: 0 - space-before-keywords: 0 - space-in-parens: 0 - space-infix-ops: 0 - space-return-throw-case: 0 - space-unary-ops: 0 - spaced-comment: 0 - wrap-regex: 0 - - # ECMAScript 6 - arrow-body-style: 0 - arrow-parens: 0 - arrow-spacing: 0 - constructor-super: 0 - generator-star-spacing: 0 - no-arrow-condition: 0 - no-class-assign: 0 - no-const-assign: 0 - no-dupe-class-members: 0 - no-this-before-super: 0 - no-var: 0 - object-shorthand: 0 - prefer-arrow-callback: 0 - prefer-const: 0 - prefer-reflect: 0 - prefer-spread: 0 - prefer-template: 0 - require-yield: 0 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..53ff894 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,60 @@ +module.exports = { + extends: 'airbnb-base', + parser: 'babel-eslint', + parserOptions: { + ecmaVersion: 8, + }, + plugins: [ + 'filenames', + 'jest', + 'jest-async', + 'jsdoc', + ], + rules: { + 'comma-dangle': ['error', { + 'arrays': 'only-multiline', + 'objects': 'only-multiline', + 'imports': 'only-multiline', + 'exports': 'only-multiline', + 'functions': 'ignore', + }], + 'prefer-template': 'off', + 'consistent-return': 'error', + 'no-case-declarations': 'error', + 'no-plusplus': ['error', { 'allowForLoopAfterthoughts': true }], + 'arrow-body-style': 'off', + 'import/no-commonjs': 'error', + 'import/no-amd': 'error', + 'import/prefer-default-export': 'off', + strict: ['error', 'never'], + 'max-len': ['error', 120, 4], + 'no-param-reassign': ['error', { props: false }], + 'require-jsdoc': ['error', { + require: { + FunctionDeclaration: true, + MethodDefinition: true, + ClassDeclaration: true + } + }], + 'jsdoc/check-param-names': 'error', + 'jsdoc/check-tag-names': 'error', + 'jsdoc/check-types': 'error', + 'jsdoc/newline-after-description': 'error', + 'jsdoc/require-description-complete-sentence': 'error', + 'jsdoc/require-hyphen-before-param-description': 'error', + 'jsdoc/require-param': 'error', + 'jsdoc/require-param-description': 'error', + 'jsdoc/require-param-type': 'error', + 'jsdoc/require-returns-description': 'error', + 'jsdoc/require-returns-type': 'error', + 'filenames/match-regex': ['error', '^[a-z0-9-]+$'], + 'jest/no-disabled-tests': 'warn', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/valid-expect': 'error', + 'jest-async/expect-return': 'error', + }, + env: { + 'jest/globals': true + }, +}; diff --git a/.jestrc.js b/.jestrc.js new file mode 100644 index 0000000..7eb6f79 --- /dev/null +++ b/.jestrc.js @@ -0,0 +1,37 @@ +module.exports = { + // this is a workaround that jest does not create a jest_0/ folder in the project root dir! + cacheDirectory: '/tmp/jest-cache', + collectCoverageFrom: [ + 'lib/**/*.js', + 'src/**/*.js', + 'index.js' + ], + coverageDirectory: './coverage/', + coverageReporters: ['html', 'lcov', 'lcovonly', 'text'], + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100 + } + }, + mapCoverage: true, + // testMatch: [ + // // '**/test/**/*.js!**/test/functional/util/**', + // '**/test/unit/*.js', + // // '**/test/test-log-wrapper.js', + // // '**/test/test-middleware.js', + // // '**/test/test-index.js', + // //'/*.js!**/test/functional/util/**', + // // '!**/test/*.js', + // ], + testRegex: '\\/test\\/unit\\/.*|\/\\.js?$/', + testEnvironment: 'node', + testPathIgnorePatterns: [ + // '/test/data/.*', + // '/test/tmp/.*', + // '/test/logger.js', + ], + verbose: true, +}; diff --git a/.travis.yml b/.travis.yml index f28689b..a3a77f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,22 @@ language: node_js node_js: +- "8" - "7" - "6" - "5" -- "5.1" - "4" -- "4.4" -- "4.3" -- "4.2" -- "4.1" -- "4.0" -- "0.12" -- "0.11" -- "0.10" -- "iojs" + os: - linux - osx + after_success: - ./node_modules/codecov/bin/codecov -e TRAVIS_NODE_VERSION - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js --verbose - ./node_modules/codeclimate-test-reporter/bin/codeclimate.js < ./coverage/lcov.info + branches: only: # whitelist - master - - development - - /^(bugfix|feature)\/#\d+.*$/ + - /^(bugfix|feature|refactor)\/#\d+.*$/ - /^hotfix\/.*$/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..790d10a --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +.PHONY: test clean readme + +.DEFAULT_GOAL:=help + +build: ## Babel transpiles files from _./src_ to _./lib_. + @printf "Transpiling files from ./src to ./lib (babel)...\n" + npm run build + +install: ## Installs all modules + @printf "Install all modules...\n" + npm install + +clean: ## Removes generated files in folders ./node_modules, ./lib and ./coverage" + @printf "Removing ./lib, ./node_modules, ./lib and ./coverage...\n" + rm -rf lib + rm -rf node_modules + rm -rf coverage + +test: ## Runs the test suite and ESLint. + @printf "Running test suite and ESLint...\n" + npm test + npm run eslint + +readme: ## Creates all the documentation parts of the project: _README.md_, _MAKE.md_, _PACKAGE.md_ and _API.md_ (the latter based on [JSDoc](http://usejsdoc.org/)). + @printf "Create documentation...\n" + npm run readme + +publish: test readme ## Publishes module to NPM registry. + @printf "Publish module to NPM repo...\n" + npm publish + +eslint: ## Runs ESLint. + @echo "Running ESLint...\n" + npm run eslint + +help: ## Prints the help about targets. + @printf "Usage: make [\033[34mtarget\033[0m]\n" + @printf "Default: \033[34m%s\033[0m\n" $(.DEFAULT_GOAL) + @printf "Targets:\n" + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " \033[34m%-14s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort diff --git a/VERSION.txt b/VERSION.txt deleted file mode 100644 index 21e8796..0000000 --- a/VERSION.txt +++ /dev/null @@ -1 +0,0 @@ -1.0.3 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5b0e428..4304e90 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,3 +1,11 @@ +#### v3.0.0 + +- Removal of _development_ branch +- Switch to babel +- Use native promises instead of bluebird +- Use named import only for all classes (i.e. also for `Transformer`) +- Tests written in Jest (got rid of _assert_, _mocha_ and _istanbul_) + #### v2.0.1 - [[#39](https://github.com/deadratfink/jy-transform/issues/39)] Maintenance release diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index f99835c..90c33fe 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -3,27 +3,23 @@ When contributing as coder, please take care of the following conventions: - Enter yourself in the `contributors` section of _package.json_. -- We strictly follow [Semantic Versioning 2](http://semver.org) rules. -- The `development` branch is the leading branch and is protected. Create bugfix and feature +- Strictly follow [Semantic Versioning 2](http://semver.org) rules. +- The `master` branch is the leading branch, is protected and is the stable branch after a release. + Create `bugfix`, `feature` and `refactor` branches (or fork into you own namespace) and create pull - requests to `development` when finished. Any of these should be prefixed with - `bugfix/#...` or `feature/#...` (followed by issue number and a short, "underscored" + requests to `master` when finished. Any of these should be prefixed with + `bugfix/#...`, `feature/#...` or `refactor/#...` (followed by issue number and a short, "underscored" proper meaning), e.g. - `bugfix/#8_fix_js_reading_with_require` - `feature/#14_multidocument_support` - Remember that name could need to be enclosed in quotes, e.g. ```$ git checkout -b 'feature/#19_...'``` when using git shell command. -- The `master` branch is protected and is the stable branch after a release. - It will never be pushed directly (only on release build). -- Indention for any file is 4 SPACEs. - Keep code coverage high (> 95%). -- Doc everything with [JSDocs](http://usejsdoc.org/) and document concepts in +- Doc everything with [JSDoc](http://usejsdoc.org/) and document concepts in [README.md](https://github.com/deadratfink/jy-transform/blob/development/README.md) or [Wiki](https://github.com/deadratfink/jy-transform/wiki). -- Use _single_ parenthesis (`'...'`) in _*.js_ files instead of _double_ parenthesis (`"..."`). -- Avoid the of use parenthesis for keys in JSON objects. -- Use the strict mode (`'use strict';`) in _*.js_ files. +- Coding style is defined by _.eslintrc.js_. - File names should be lower-case with hyphens as divider, e.g. _options-handler.js_. - Markdown documentation files should be upper-case with _.md_ as extension, placed in _./docs_, e.g. _USAGE.md_. The _README.md_ is build up by these files concatenated diff --git a/index.js b/index.js index 94ae223..c0df3b8 100755 --- a/index.js +++ b/index.js @@ -1,7 +1,8 @@ -'use strict'; +module.exports = { + Transformer: require('./lib/transformer.js'), + Reader: require('./lib/reader.js'), + Writer: require('./lib/writer.js'), + Constants: require('./lib/constants.js'), + Middleware: require('./lib/middleware.js'), +}; -module.exports = module.exports.Transformer = require('./lib/transformer.js'); -module.exports.Reader = require('./lib/reader.js'); -module.exports.Writer = require('./lib/writer.js'); -module.exports.middleware = require('./lib/middleware.js'); -module.exports.constants = require('./lib/constants.js'); diff --git a/jyt b/jyt index 029ece4..266f1b8 100755 --- a/jyt +++ b/jyt @@ -1,16 +1,14 @@ #!/usr/bin/env node -'use strict'; - +var path = require('path'); var constants = require('./lib/constants.js'); var cli = require('cli'); -var path = require('path'); var Transformer = require('./lib/transformer.js'); var transformer = new Transformer(cli); -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CLI INIT -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// /** * How to use the CLI. @@ -26,23 +24,30 @@ var usage = path.basename(__filename) + ' INPUT-FILE [OUTPUT-FILE] [OPTIONS]'; * @type {string} * @private */ -var packagePath = __dirname + '/package.json'; +var packagePath = path.join(__dirname, 'package.json'); /** * The options description for parsing the command line input, must be an object with opts defined like: * ``` * long_tag: [short_tag, description, value_type, default_value]; * ``` - * @type {{origin: *[], target: *[], src: string[], dest: *[], indent: *[], force: string[], imports: string, exports: string}} + * @type {{origin: *[], target: *[], src: string[], dest: *[], indent: *[], force: string[], imports: string, + * exports: string}} * @private */ var options = { - origin: [ 'o', 'The origin type of INPUT-FILE: [ ' + constants.JS + ' | ' + constants.JSON + ' | ' + constants.YAML + ' ]', 'string', constants.DEFAULT_OPTIONS.origin ], - target: [ 't', 'The target type of OUTPUT-FILE: [ ' + constants.JS + ' | ' + constants.JSON + ' | ' + constants.YAML + ' ]', 'string', constants.DEFAULT_OPTIONS.target ], - indent: [ 'i', 'The indention for pretty-print: 1 - 8', 'int', constants.DEFAULT_INDENT ], - force: [ 'f', 'Force overwriting of existing output files on write phase: when files are not overwritten (which is default), then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of foo.yaml it would be foo(1).yaml' ], - imports: [ 'm', 'Define a \'module.exports[.identifier] = \' identifier (to read from JS _source_ file only, must be a valid JS identifier!)', 'string', constants.DEFAULT_OPTIONS.imports ], - exports: [ 'x', 'Define a \'module.exports[.identifier] = \' identifier (for usage in JS destination file only, must be a valid JS identifier!)', 'string', constants.DEFAULT_OPTIONS.exports ] + origin: ['o', 'The origin type of INPUT-FILE: [ ' + constants.JS + ' | ' + constants.JSON + ' | ' + constants.YAML + + ' ]', 'string', constants.DEFAULT_OPTIONS.origin], + target: ['t', 'The target type of OUTPUT-FILE: [ ' + constants.JS + ' | ' + constants.JSON + ' | ' + constants.YAML + + ' ]', 'string', constants.DEFAULT_OPTIONS.target], + indent: ['i', 'The indention for pretty-print: 1 - 8', 'int', constants.DEFAULT_INDENT], + force: ['f', 'Force overwriting of existing output files on write phase: when files are not overwritten (which' + + ' is default), then the next transformation with same output file name gets a consecutive number on the base' + + ' file name, e.g. in case of foo.yaml it would be foo(1).yaml'], + imports: ['m', 'Define a \'module.exports[.identifier] = \' identifier (to read from JS _source_ file only, must' + + ' be a valid JS identifier!)', 'string', constants.DEFAULT_OPTIONS.imports], + exports: ['x', 'Define a \'module.exports[.identifier] = \' identifier (for usage in JS destination file only,' + + ' must be a valid JS identifier!)', 'string', constants.DEFAULT_OPTIONS.exports] }; /** @@ -52,13 +57,13 @@ var options = { * @private */ function error(err) { - cli.error('////////////////////////////////////////////////////////////////////////////////'); - cli.error(err); - if (err.stack) { - cli.debug(err.stack); - } - cli.error('////////////////////////////////////////////////////////////////////////////////'); - cli.getUsage(1); + cli.error('////////////////////////////////////////////////////////////////////////////////'); + cli.error(err); + if (err.stack) { + cli.debug(err.stack); + } + cli.error('////////////////////////////////////////////////////////////////////////////////'); + cli.getUsage(1); } /** @@ -66,41 +71,39 @@ function error(err) { * given on CLI, then does the transformation with these options and finally, it * prints the result to the CLI. * - * @param {array} args - The first mandatory argument is the input file - * (`args[0]`), the second (optional) argument is the - * output file (`args[0]`). - * @param {object} options - The options set on CLI. + * @param {Array} args - The first mandatory argument is the input file (`args[0]`), the second (optional) + * argument is the output file (`args[1]`). + * @param {module:type-definitions~Options} cliOptions - The options provided via CLI. * @private */ -function main(args, options) { +function main(args, cliOptions) { + // read file args and set to options - // read file args and set to options + if (args.length > 0) { + cli.debug('input file: ' + args[0]); + cliOptions.src = args[0]; + } else { + error('please specify an input file as first argument!'); + } + if (args.length > 1) { + cli.debug('output file: ' + args[1]); + cliOptions.dest = args[1]; + } else { + cli.debug('output file not specified, using default'); + } - if (args.length > 0) { - cli.debug('input file: ' + args[0]); - options.src = args[0]; - } else { - error('please specify an input file as first argument!'); - } - if (args.length > 1) { - cli.debug('output file: ' + args[1]); - options.dest = args[1]; - } else { - cli.debug('output file not specified, using default'); - } + // transform with options - // transform with options - - return transformer.transform(options) - .then(function (msg) { - cli.info(msg); - }) - .catch(function (err) { - error(err); - }); + return transformer.transform(cliOptions) + .then(function (msg) { + cli.info(msg); + }) + .catch(function (err) { + error(err); + }); } -/** +/* * Init the CLI instance. */ cli.width = 120; diff --git a/lib/constants.js b/lib/constants.js index fab466e..0f3af46 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,8 +1,8 @@ 'use strict'; -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CONSTRUCTOR -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// /** * Constructs the constants. @@ -12,7 +12,7 @@ * @class Class which defines all constants usable in or with this module. */ function Constants() { - return this; + return this; } Constants.prototype = {}; @@ -20,9 +20,9 @@ Constants.prototype.constructor = Constants; var constants = new Constants(); module.exports = constants; -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CONSTANTS -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// /** * The 'utf8' constant. @@ -187,13 +187,13 @@ Constants.prototype.DEFAULT_JS_EXPORTS_IDENTIFIER = undefined; * @see {@link DEST_DESCRIPTION} */ Constants.prototype.DEFAULT_OPTIONS = { - origin: constants.ORIGIN_DESCRIPTION, - target: constants.TARGET_DESCRIPTION, - dest: constants.DEST_DESCRIPTION, - indent: constants.DEFAULT_INDENT, - force: constants.DEFAULT_FORCE_FILE_OVERWRITE, - imports: constants.DEFAULT_JS_IMPORTS_IDENTIFIER, - exports: constants.DEFAULT_JS_EXPORTS_IDENTIFIER + origin: constants.ORIGIN_DESCRIPTION, + target: constants.TARGET_DESCRIPTION, + dest: constants.DEST_DESCRIPTION, + indent: constants.DEFAULT_INDENT, + force: constants.DEFAULT_FORCE_FILE_OVERWRITE, + imports: constants.DEFAULT_JS_IMPORTS_IDENTIFIER, + exports: constants.DEFAULT_JS_EXPORTS_IDENTIFIER }; /** @@ -285,13 +285,13 @@ Constants.prototype.JS_TO_JS = 'js2js'; * @public */ Constants.prototype.TRANSFORMATIONS = [ - constants.YAML_TO_JS, - constants.YAML_TO_JSON, - constants.JS_TO_YAML, - constants.JSON_TO_YAML, - constants.JSON_TO_JS, - constants.JS_TO_JSON, - constants.YAML_TO_YAML, - constants.JSON_TO_JSON, - constants.JS_TO_JS + constants.YAML_TO_JS, + constants.YAML_TO_JSON, + constants.JS_TO_YAML, + constants.JSON_TO_YAML, + constants.JSON_TO_JS, + constants.JS_TO_JSON, + constants.YAML_TO_YAML, + constants.JSON_TO_JSON, + constants.JS_TO_JS ]; diff --git a/lib/log-wrapper.js b/lib/log-wrapper.js index 2bbb053..909b690 100644 --- a/lib/log-wrapper.js +++ b/lib/log-wrapper.js @@ -1,10 +1,8 @@ -'use strict'; - var Promise = require('bluebird'); -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CONSTRUCTOR -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// /** * Constructs the `LogWrapper`. @@ -22,21 +20,21 @@ var Promise = require('bluebird'); */ function LogWrapper(logger) { - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = logger || console; + /** + * The logger instance. + * + * @member {(logger|cli|console)} + * @private + */ + this.logInstance = logger || console; } LogWrapper.prototype = {}; LogWrapper.prototype.constructor = LogWrapper; -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // LOGGER METHODS -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// /** * Log the options with INFO level. @@ -50,7 +48,7 @@ LogWrapper.prototype.constructor = LogWrapper; * @public */ LogWrapper.prototype.info = function (msg) { - this.logInstance.info(msg); + this.logInstance.info(msg); }; /** @@ -65,11 +63,11 @@ LogWrapper.prototype.info = function (msg) { * @public */ LogWrapper.prototype.debug = function (msg) { - if (this.logInstance.debug && typeof this.logInstance.debug === 'function') { - this.logInstance.debug(msg); - } else { - this.info(msg); - } + if (this.logInstance.debug && typeof this.logInstance.debug === 'function') { + this.logInstance.debug(msg); + } else { + this.info(msg); + } }; /** @@ -85,11 +83,11 @@ LogWrapper.prototype.debug = function (msg) { * @see {@link #debug} */ LogWrapper.prototype.trace = function (msg) { - if (this.logInstance.trace && typeof this.logInstance.trace === 'function') { - this.logInstance.trace(msg); - } else { - this.debug(msg); - } + if (this.logInstance.trace && typeof this.logInstance.trace === 'function') { + this.logInstance.trace(msg); + } else { + this.debug(msg); + } }; /** @@ -104,7 +102,7 @@ LogWrapper.prototype.trace = function (msg) { * @public */ LogWrapper.prototype.error = function (msg) { - this.logInstance.error(msg); + this.logInstance.error(msg); }; /** @@ -125,16 +123,16 @@ LogWrapper.prototype.error = function (msg) { * @public */ LogWrapper.prototype.verboseOptions = function (options) { - var self = this; - return Promise.resolve() - .then(function () { - self.info('origin: ' + options.origin); - self.info('target: ' + options.target); - self.info('src: ' + options.src); - self.info('dest: ' + options.dest); - self.info('indent: ' + options.indent); - return options; - }); + var self = this; + return Promise.resolve() + .then(function () { + self.info('origin: ' + options.origin); + self.info('target: ' + options.target); + self.info('src: ' + options.src); + self.info('dest: ' + options.dest); + self.info('indent: ' + options.indent); + return options; + }); }; exports = module.exports = LogWrapper; diff --git a/lib/middleware.js b/lib/middleware.js index 3fe981c..2cbc39d 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -1,10 +1,8 @@ -'use strict'; - var Promise = require('bluebird'); -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CONSTRUCTOR -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// /** * Constructs the `Middleware`. @@ -29,7 +27,7 @@ module.exports = new Middleware(); * @private */ function identity(object) { - return Promise.resolve(object); + return Promise.resolve(object); } /** @@ -61,9 +59,8 @@ Middleware.prototype.identityMiddleware = identity; * ``` * * **NOTE:** the Promise has to return the processed JSON! - * @returns {Promise} - The given middleware Promise or a new JSON 'identity' middleware Promise. - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. + * @returns {Function} - The given middleware Promise or a new JSON 'identity' middleware Promise function. + * @throws {TypeError} - Will throw this error when the passed `middleware` is not type of `Function`. * @example * var middleware = require('./lib/middleware.js'); * var myMiddleware = function(object) { @@ -76,11 +73,11 @@ Middleware.prototype.identityMiddleware = identity; * @public */ Middleware.prototype.ensureMiddleware = function (middleware) { - if (middleware !== undefined && (typeof middleware !== 'function')) { - return Promise.reject(new TypeError('The provided middleware is not a Function type')); - } - if (!middleware) { - middleware = identity; - } - return middleware; + if (middleware !== undefined && (typeof middleware !== 'function')) { + throw new TypeError('The provided middleware is not a Function type'); + } + if (!middleware) { + middleware = identity; + } + return middleware; }; diff --git a/lib/options-handler.js b/lib/options-handler.js index 0dd221c..637be22 100644 --- a/lib/options-handler.js +++ b/lib/options-handler.js @@ -1,5 +1,3 @@ -'use strict'; - var Constants = require('./constants'); var LogWrapper = require('./log-wrapper'); var Promise = require('bluebird'); @@ -7,20 +5,12 @@ var path = require('path'); var fs = require('fs'); var isStream = require('is-stream'); -/** - * @typedef {object} Options - * @property {string} [origin=yaml] - The origin type. - * @property {string} [target=js] - The target type. - * @property {(string|Readable|object)} src - The source (`string` type is treated as a file path). - * @property {(string|Writable|object)} [dest] - The destination (`string` type is treated as a file path). - * @property {number} [indent=4] - The indention in files. - * @property {string} [imports=undefined] - The exports name for reading from JS source file or objects only. - * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. - */ +// TODO use joi for validation +// TODO turn into class -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CONSTRUCTOR -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// /** * Constructs the `OptionsHandler` with an (optional) logger. @@ -41,411 +31,417 @@ var isStream = require('is-stream'); */ function OptionsHandler(logger) { - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); + /** + * The logger instance. + * + * @member {(logger|cli|console)} + * @private + */ + this.logInstance = new LogWrapper(logger); - var self = this; + var self = this; - /** - * Get a file extension for a given output target. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing a proper file extension (including '.', e.g. _'.yaml'_). - * @private - */ - function getDestFileExt(options) { - return new Promise(function (resolve, reject) { - var dot = '.'; - switch (options.target) { - case Constants.YAML: - return resolve(dot + Constants.YAML); - case Constants.JS: - return resolve(dot + Constants.JS); - case Constants.JSON: - return resolve(dot + Constants.JSON); - default: - reject(new Error('Invalid target option found while creating destination file extension: ' + options.target)); - } - }); - } + /** + * Get a file extension for a given output target. + * + * @param {Options} options - The configuration for a transformation. + * @returns {Promise} - A Promise containing a proper file extension (including '.', e.g. _'.yaml'_). + * @private + */ + function getDestFileExt(options) { + return new Promise(function (resolve, reject) { + var dot = '.'; + switch (options.target) { + case Constants.YAML: + return resolve(dot + Constants.YAML); + case Constants.JS: + return resolve(dot + Constants.JS); + case Constants.JSON: + return resolve(dot + Constants.JSON); + default: + reject(new Error('Invalid target option found while creating destination file extension: ' + options.target)); + } + }); + } - /** - * Checks if the given type is a valid one. - * - * @param {string} type - One of `[ 'yaml' | 'json' | 'js']`. - * @returns {boolean} - `true` if is valid type, else `false`. - * @see {@link Constants#TYPES} - * @private - */ - function isValidType(type) { - return (Constants.TYPES.indexOf(type) >= 0); - } + /** + * Checks if the given type is a valid one. + * + * @param {string} type - One of `[ 'yaml' | 'json' | 'js']`. + * @returns {boolean} - `true` if is valid type, else `false`. + * @see {@link Constants#TYPES} + * @private + */ + function isValidType(type) { + return (Constants.TYPES.indexOf(type) >= 0); + } - /** - * Promise which asserts that an origin or target is correct. If correct it - * resolved the passed `options` object, else if rejects with a `Error`. - * - * @param {Options} options - The configuration for a transformation. - * @param {string} typeName - The type name. - * @returns {Promise} - A Promise containing the passed `options` object. - * @private - */ - function assertType(options, typeName) { - return assertOptions(options, [typeName]) - .then(function (assertedOptions) { - if (isValidType(assertedOptions[typeName])) { - return assertedOptions; - } else { - return Promise.reject(new Error('Invalid ' + typeName + ' \'' + assertedOptions[typeName] + '\' found, must be one of ' + JSON.stringify(Constants.TYPES))); - } - }); - } + /** + * Promise which asserts that an origin or target is correct. If correct it + * resolved the passed `options` object, else if rejects with a `Error`. + * + * @param {Options} options - The configuration for a transformation. + * @param {string} typeName - The type name. + * @returns {Promise} - A Promise containing the passed `options` object. + * @private + */ + function assertType(options, typeName) { + return assertOptions(options, [typeName]) + .then(function (assertedOptions) { + if (isValidType(assertedOptions[typeName])) { + return assertedOptions; + } else { + return Promise.reject(new Error('Invalid ' + typeName + ' \'' + assertedOptions[typeName] + '\' found, must be one of ' + JSON.stringify(Constants.TYPES))); + } + }); + } - /** - * A simple map for extensions to type. - * - * @type {{yml: string, yaml: string, js: string, json: string}} - * @private - */ - var typeMap = { - yml: Constants.YAML, - yaml: Constants.YAML, - js: Constants.JS, - json: Constants.JSON - }; + /** + * A simple map for extensions to type. + * + * @type {{yml: string, yaml: string, js: string, json: string}} + * @private + */ + var typeMap = { + yml: Constants.YAML, + yaml: Constants.YAML, + js: Constants.JS, + json: Constants.JSON + }; - /** - * Infer from path extension to a type using default value as fallback. - * - * @param {string} pathStr - The file path with or without extension. - * @param {boolean} origin - If the type is origin (true) or target (false) - * @param {string} defaultValue - The default value to use if type cannot be inferred from path. - * @returns {string} - A type value. - * @private - */ - function getTypeFromFilePath(pathStr, origin, defaultValue) { - var ext = path.extname(pathStr); - self.logInstance.debug('extension: ' + ext); - if (ext.charAt(0) === '.') { - ext = ext.substr(1); - } + /** + * Infer from path extension to a type using default value as fallback. + * + * @param {string} pathStr - The file path with or without extension. + * @param {boolean} origin - If the type is origin (true) or target (false) + * @param {string} defaultValue - The default value to use if type cannot be inferred from path. + * @returns {string} - A type value. + * @private + */ + function getTypeFromFilePath(pathStr, origin, defaultValue) { + var ext = path.extname(pathStr); + self.logInstance.debug('extension: ' + ext); + if (ext.charAt(0) === '.') { + ext = ext.substr(1); + } - var type = typeMap[ext]; - if (!type) { - self.logInstance.debug('cannot resolve ' + (origin ? 'origin' : 'target') + ' type from file ' + pathStr + ', using default: ' + defaultValue); - type = defaultValue; - } - return type; + var type = typeMap[ext]; + if (!type) { + self.logInstance.debug('cannot resolve ' + (origin ? 'origin' : 'target') + ' type from file ' + pathStr + ', using default: ' + defaultValue); + type = defaultValue; } + return type; + } - /////////////////////////////////////////////////////////////////////////////// - // OPTIONS INIT & VALIDATION METHODS - /////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // OPTIONS INIT & VALIDATION METHODS + /////////////////////////////////////////////////////////////////////////////// - /** - * Completes the given `options` object by enriching from default values or using - * type inference if something required is "missing" (a missing `options.src` cannot - * be completed becaue this is mandatory). - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @throws {Error} - If `options` or `options.src` not passed. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.completeOptions(options) - * .then(function (copiedOptions) { + /** + * Completes the given `options` object by enriching from default values or using + * type inference if something required is "missing" (a missing `options.src` cannot + * be completed becaue this is mandatory). + * + * @param {Options} options - The configuration for a transformation. + * @returns {Promise} - A Promise containing the passed `options` object. + * @throws {Error} - If `options` or `options.src` not passed. + * @example + * var OptionsHandler = require('./options-handler.js'); + * var logger = ...; + * var options = {...}; + * var optionsHandler = new OptionsHandler(logger); + * + * optionsHandler.completeOptions(options) + * .then(function (copiedOptions) { * ... * }); - * @public - */ - this.completeOptions = function (options) { - return assertOptions(options, ['src']) - .then(function (assertedOptions) { - var srcType; - var destType; - if (assertedOptions.src) { - if (typeof assertedOptions.src === 'string') { - srcType = getTypeFromFilePath(assertedOptions.src, true, Constants.YAML); - } else if (typeof assertedOptions.src === 'object') { - srcType = Constants.JS; - } // TODO: what about stream? - } - self.logInstance.debug('srcType: ' + srcType); - if (assertedOptions.dest && (typeof assertedOptions.dest === 'string')) { - self.logInstance.debug('options.dest: ' + assertedOptions.dest); - destType = getTypeFromFilePath(assertedOptions.dest, false, Constants.JS); // TODO: what about stream? - } - self.logInstance.debug('destType: ' + destType); + * @public + */ + this.completeOptions = function (options) { + return assertOptions(options, ['src']) + .then(function (assertedOptions) { + var srcType; + var destType; + if (assertedOptions.src) { + if (typeof assertedOptions.src === 'string') { + srcType = getTypeFromFilePath(assertedOptions.src, true, Constants.YAML); + } else if (typeof assertedOptions.src === 'object') { + srcType = Constants.JS; + } // TODO: what about stream? + } + self.logInstance.debug('srcType: ' + srcType); + if (assertedOptions.dest && (typeof assertedOptions.dest === 'string')) { + self.logInstance.debug('options.dest: ' + assertedOptions.dest); + destType = getTypeFromFilePath(assertedOptions.dest, false, Constants.JS); // TODO: what about stream? + } + self.logInstance.debug('destType: ' + destType); - assertedOptions.origin = (assertedOptions.origin && (assertedOptions.origin !== Constants.ORIGIN_DESCRIPTION)) ? assertedOptions.origin : (srcType || Constants.DEFAULT_ORIGIN); - assertedOptions.target = (assertedOptions.target && (assertedOptions.target !== Constants.TARGET_DESCRIPTION)) ? assertedOptions.target : (destType || Constants.DEFAULT_TARGET); - assertedOptions.dest = assertedOptions.dest || Constants.DEFAULT_OPTIONS.dest; - assertedOptions.indent = assertedOptions.indent || Constants.DEFAULT_OPTIONS.indent; - assertedOptions.force = assertedOptions.force || Constants.DEFAULT_OPTIONS.force; - assertedOptions.imports = assertedOptions.imports || Constants.DEFAULT_OPTIONS.imports; - assertedOptions.exports = assertedOptions.exports || Constants.DEFAULT_OPTIONS.exports; - return assertedOptions; - }); - }; + assertedOptions.origin = (assertedOptions.origin && (assertedOptions.origin !== Constants.ORIGIN_DESCRIPTION)) ? assertedOptions.origin : (srcType || Constants.DEFAULT_ORIGIN); + assertedOptions.target = (assertedOptions.target && (assertedOptions.target !== Constants.TARGET_DESCRIPTION)) ? assertedOptions.target : (destType || Constants.DEFAULT_TARGET); + assertedOptions.dest = assertedOptions.dest || Constants.DEFAULT_OPTIONS.dest; + assertedOptions.indent = assertedOptions.indent || Constants.DEFAULT_OPTIONS.indent; + assertedOptions.force = assertedOptions.force || Constants.DEFAULT_OPTIONS.force; + assertedOptions.imports = assertedOptions.imports || Constants.DEFAULT_OPTIONS.imports; + assertedOptions.exports = assertedOptions.exports || Constants.DEFAULT_OPTIONS.exports; + return assertedOptions; + }); + }; - /** - * Ensures that the given input source is valid. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @throws {Error} - If the `options.src` is not defined or the file represented by `options.src` does not exist. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.ensureSrc(options) - * .then(function (ensuredOptions) { + /** + * Ensures that the given input source is valid. + * + * @param {Options} options - The configuration for a transformation. + * @returns {Promise} - A Promise containing the passed `options` object. + * @throws {Error} - If the `options.src` is not defined or the file represented by `options.src` does not exist. + * @example + * var OptionsHandler = require('./options-handler.js'); + * var logger = ...; + * var options = {...}; + * var optionsHandler = new OptionsHandler(logger); + * + * optionsHandler.ensureSrc(options) + * .then(function (ensuredOptions) { * ... * }); - * @public - */ - this.ensureSrc = function (options) { - //return assertOptions(options, ['src']) - // .then(function (assertedOptions) { - // if (typeof assertedOptions.src === 'string') { - // self.logInstance.debug('options.src is to be verfied as File path: ' + assertedOptions.src); - // // check for existing source file - // try { - // var stats = fs.statSync(assertedOptions.src); - // if (stats.isDirectory()) { - // return Promise.reject(new Error('Source file (options.src) is a directory, pls specify a valid file resource!')); - // } - // } catch (err) { - // err.message = 'Error occurred while checking input file \'' + assertedOptions.src + '\' which should be existing and accessible, code: ' + err.code + ', cause: ' + err.message; - // return Promise.reject(err); - // } - // } else if (isStream.readable(assertedOptions.src)) { - // self.logInstance.debug('options.src is Readable stream'); - // if (!options.origin) { - // return Promise.reject(new Error('When options.src is a Readable stream then setting options.origin is mandatory!')); - // } - // } else { - // self.logInstance.debug('options.src is JSON Object'); - // } - // return assertedOptions; - //}); + * @public + */ + this.ensureSrc = function (options) { + //return assertOptions(options, ['src']) + // .then(function (assertedOptions) { + // if (typeof assertedOptions.src === 'string') { + // self.logInstance.debug('options.src is to be verfied as File path: ' + assertedOptions.src); + // // check for existing source file + // try { + // var stats = fs.statSync(assertedOptions.src); + // if (stats.isDirectory()) { + // return Promise.reject(new Error('Source file (options.src) is a directory, pls specify a valid file resource!')); + // } + // } catch (err) { + // err.message = 'Error occurred while checking input file \'' + assertedOptions.src + '\' which should be existing and accessible, code: ' + err.code + ', cause: ' + err.message; + // return Promise.reject(err); + // } + // } else if (isStream.readable(assertedOptions.src)) { + // self.logInstance.debug('options.src is Readable stream'); + // if (!options.origin) { + // return Promise.reject(new Error('When options.src is a Readable stream then setting options.origin is mandatory!')); + // } + // } else { + // self.logInstance.debug('options.src is JSON Object'); + // } + // return assertedOptions; + //}); - return assertOptions(options, ['src']) - .then(function (options) { - return assertFileSrc(options); - }) - .spread(function (checked, options) { - if (!checked) { - return assertStreamSrc(options); - } - return [checked, options]; - }) - .spread(function (checked, options) { - if (!checked) { - self.logInstance.debug('options.src is JSON Object'); - } - return Promise.resolve(options); - }) - .catch(function (err) { - self.logInstance.error('options.src is unknown or invalid object: ' + options.src); - return Promise.reject(err); - }); - }; + return assertOptions(options, ['src']) + .then(function (options) { + return assertFileSrc(options); + }) + .spread(function (checked, options) { + if (!checked) { + return assertStreamSrc(options); + } + return [checked, options]; + }) + .spread(function (checked, options) { + if (!checked) { + self.logInstance.debug('options.src is JSON Object'); + } + return Promise.resolve(options); + }) + .catch(function (err) { + self.logInstance.error('options.src is unknown or invalid object: ' + options.src); + return Promise.reject(err); + }); + }; - function assertFileSrc(options) { - return new Promise(function (resolve, reject) { - if (typeof options.src === 'string') { - self.logInstance.debug('options.src is to be verified as File path: ' + options.src); - // check for existing source file - try { - var stats = fs.statSync(options.src); - if (stats.isDirectory()) { - return reject(new Error('Source file (options.src) is a directory, pls specify a valid file resource!')); - } - return resolve([true, options]); - } catch (err) { - err.message = 'Error occurred while checking input file \'' + options.src + '\' which should be existing and accessible, code: ' + err.code + ', cause: ' + err.message; - return reject(err); - } - } else { - return resolve([false, options]); - } - }); - } + function assertFileSrc(options) { + return new Promise(function (resolve, reject) { + if (typeof options.src === 'string') { + self.logInstance.debug('options.src is to be verified as File path: ' + options.src); + // check for existing source file + try { + var stats = fs.statSync(options.src); + if (stats.isDirectory()) { + return reject(new Error('Source file (options.src) is a directory, pls specify a valid file resource!')); + } + return resolve([true, options]); + } catch (err) { + // here we get a "native" error which seems to be not type of Error but simple Object! + // For the sake of consistency we turn it into real Error type. + const realError = new Error('Error occurred while checking input file \'' + options.src + '\' which should be existing and accessible, code: ' + err.code + ', cause: ' + err.message); + realError.code = err.code; + realError.errno = err.errno; + realError.syscall = err.syscall; + realError.path = err.path; + return reject(realError); + } + } else { + return resolve([false, options]); + } + }); + } - function assertStreamSrc(options) { - return new Promise(function (resolve, reject) { - if (isStream.readable(options.src)) { - self.logInstance.debug('options.src is Readable stream'); - if (!options.origin) { - return reject(new Error('when options.src is a Readable stream, then setting options.origin is mandatory!')); - } - return resolve([true, options]); - } else { - return resolve([false, options]); - } - }); - } + function assertStreamSrc(options) { + return new Promise(function (resolve, reject) { + if (isStream.readable(options.src)) { + self.logInstance.debug('options.src is Readable stream'); + if (!options.origin) { + return reject(new Error('when options.src is a Readable stream, then setting options.origin is mandatory!')); + } + return resolve([true, options]); + } else { + return resolve([false, options]); + } + }); + } - /** - * This method ensures that destination file path is created if not set in - * options. If not, then it creates the path relative to the source file using - * its name and appending a proper extension depending on the `json` - * property of `options` (if `true` then '.js', else '.json'). - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.ensureDest(options) - * .then(function (ensuredOptions) { + /** + * This method ensures that destination file path is created if not set in + * options. If not, then it creates the path relative to the source file using + * its name and appending a proper extension depending on the `json` + * property of `options` (if `true` then '.js', else '.json'). + * + * @param {Options} options - The configuration for a transformation. + * @returns {Promise} - A Promise containing the passed `options` object. + * @example + * var OptionsHandler = require('./options-handler.js'); + * var logger = ...; + * var options = {...}; + * var optionsHandler = new OptionsHandler(logger); + * + * optionsHandler.ensureDest(options) + * .then(function (ensuredOptions) { * ... * }); - * @public - */ - this.ensureDest = function (options) { - return assertOptions(options) - .then(function (assertedOptions) { - if (assertedOptions.dest === Constants.DEFAULT_OPTIONS.dest) { - return getDestFileExt(assertedOptions) - .then(function (destExt) { - var destDirName = path.dirname(assertedOptions.src); - var srcExt = path.extname(assertedOptions.src); - var destName = path.basename(assertedOptions.src, srcExt); - assertedOptions.dest = path.join(destDirName, destName + destExt); - self.logInstance.debug('Destination file: ' + assertedOptions.dest); - return assertedOptions; - }); - } else if (isStream.writable(assertedOptions.dest)) { - self.logInstance.debug('options.dest is Writable stream'); - if (!assertedOptions.target) { - return Promise.reject(new Error('When options.dest is a Writable stream then setting options.target is mandatory!')); - } - } else { - self.logInstance.debug('Destination file: ' + assertedOptions.dest); - } - return assertedOptions; + * @public + */ + this.ensureDest = function (options) { + return assertOptions(options) + .then(function (assertedOptions) { + if (assertedOptions.dest === Constants.DEFAULT_OPTIONS.dest) { + return getDestFileExt(assertedOptions) + .then(function (destExt) { + var destDirName = path.dirname(assertedOptions.src); + var srcExt = path.extname(assertedOptions.src); + var destName = path.basename(assertedOptions.src, srcExt); + assertedOptions.dest = path.join(destDirName, destName + destExt); + self.logInstance.debug('Destination file: ' + assertedOptions.dest); + return assertedOptions; }); - }; + } else if (isStream.writable(assertedOptions.dest)) { + self.logInstance.debug('options.dest is Writable stream'); + if (!assertedOptions.target) { + return Promise.reject(new Error('When options.dest is a Writable stream then setting options.target is mandatory!')); + } + } else { + self.logInstance.debug('Destination file: ' + assertedOptions.dest); + } + return assertedOptions; + }); + }; - /** - * Checks if the given origin is valid. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.assertOrigin(options) - * .then(function (ensuredOptions) { + /** + * Checks if the given origin is valid. + * + * @param {Options} options - The configuration for a transformation. + * @returns {Promise} - A Promise containing the passed `options` object. + * @example + * var OptionsHandler = require('./options-handler.js'); + * var logger = ...; + * var options = {...}; + * var optionsHandler = new OptionsHandler(logger); + * + * optionsHandler.assertOrigin(options) + * .then(function (ensuredOptions) { * ... * }); - * @public - */ - this.assertOrigin = function (options) { - return assertType(options, 'origin'); - }; + * @public + */ + this.assertOrigin = function (options) { + return assertType(options, 'origin'); + }; - /** - * Checks if the given target is valid. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.assertTarget(options) - * .then(function (ensuredOptions) { + /** + * Checks if the given target is valid. + * + * @param {Options} options - The configuration for a transformation. + * @returns {Promise} - A Promise containing the passed `options` object. + * @example + * var OptionsHandler = require('./options-handler.js'); + * var logger = ...; + * var options = {...}; + * var optionsHandler = new OptionsHandler(logger); + * + * optionsHandler.assertTarget(options) + * .then(function (ensuredOptions) { * ... * }); - * @public - */ - this.assertTarget = function (options) { - return assertType(options, 'target'); - }; + * @public + */ + this.assertTarget = function (options) { + return assertType(options, 'target'); + }; - /** - * Checks if a valid indention value is given and corrects values if invalid (with default value: 4 SPACEs). - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.ensureIndent(options) - * .then(function (ensuredOptions) { + /** + * Checks if a valid indention value is given and corrects values if invalid (with default value: 4 SPACEs). + * @param {Options} options - The configuration for a transformation. + * @returns {Promise} - A Promise containing the passed `options` object. + * @see {@link Constants#MIN_INDENT} + * @see {@link Constants#DEFAULT_INDENT} + * @see {@link Constants#MAX_INDENT} + * @example + * var OptionsHandler = require('./options-handler.js'); + * var logger = ...; + * var options = {...}; + * var optionsHandler = new OptionsHandler(logger); + * + * optionsHandler.ensureIndent(options) + * .then(function (ensuredOptions) { * ... * }); - * @public - */ - this.ensureIndent = function (options) { - return assertOptions(options) - .then(function (assertedOptions) { - self.logInstance.trace('options before ensureIndent():: ' + JSON.stringify(assertedOptions, null, 4)); - if (assertedOptions.indent === undefined) { - self.logInstance.info('Missing indention, reset to default: ' + Constants.DEFAULT_INDENT); - assertedOptions.indent = Constants.DEFAULT_INDENT; - } else if (assertedOptions.indent < Constants.MIN_INDENT) { - self.logInstance.info('Indention \'' + assertedOptions.indent + '\' is too narrow, reset to default: ' + Constants.DEFAULT_INDENT); - assertedOptions.indent = Constants.DEFAULT_INDENT; - } else if (assertedOptions.indent > Constants.MAX_INDENT) { - self.logInstance.info('Indention \'' + assertedOptions.indent + '\' is too wide, reset to default: ' + Constants.DEFAULT_INDENT); - assertedOptions.indent = Constants.DEFAULT_INDENT; - } - self.logInstance.trace('options after ensureIndent():: ' + JSON.stringify(assertedOptions, null, 4)); - return assertedOptions; - }); - }; + * @public + */ + this.ensureIndent = function (options) { + return assertOptions(options) + .then(function (assertedOptions) { + self.logInstance.trace('options before ensureIndent():: ' + JSON.stringify(assertedOptions, null, 4)); + if (assertedOptions.indent === undefined) { + self.logInstance.info('Missing indention, reset to default: ' + Constants.DEFAULT_INDENT); + assertedOptions.indent = Constants.DEFAULT_INDENT; + } else if (assertedOptions.indent < Constants.MIN_INDENT) { + self.logInstance.info('Indention \'' + assertedOptions.indent + '\' is too narrow, reset to default: ' + Constants.DEFAULT_INDENT); + assertedOptions.indent = Constants.DEFAULT_INDENT; + } else if (assertedOptions.indent > Constants.MAX_INDENT) { + self.logInstance.info('Indention \'' + assertedOptions.indent + '\' is too wide, reset to default: ' + Constants.DEFAULT_INDENT); + assertedOptions.indent = Constants.DEFAULT_INDENT; + } + self.logInstance.trace('options after ensureIndent():: ' + JSON.stringify(assertedOptions, null, 4)); + return assertedOptions; + }); + }; - /** - * Log the options with INFO level. - * - * @param {Options} options - The configuration for a transformation. The properties to log with INFO. - * @returns {Promise} - A Promise containing the passed `options` object. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.verboseOptions(options) - * .then(function (loggedOptions) { + /** + * Log the options with INFO level. + * + * @param {Options} options - The configuration for a transformation. The properties to log with INFO. + * @returns {Promise} - A Promise containing the passed `options` object. + * @example + * var OptionsHandler = require('./options-handler.js'); + * var logger = ...; + * var options = {...}; + * var optionsHandler = new OptionsHandler(logger); + * + * optionsHandler.verboseOptions(options) + * .then(function (loggedOptions) { * ... * }); - * @private - */ - this.verboseOptions = function (options) { - return assertOptions(options) - .then(function (assertedOptions) { - return self.logInstance.verboseOptions(assertedOptions); - }); - }; + * @private + */ + this.verboseOptions = function (options) { + return assertOptions(options) + .then(function (assertedOptions) { + return self.logInstance.verboseOptions(assertedOptions); + }); + }; } OptionsHandler.prototype = {}; @@ -472,13 +468,13 @@ module.exports = OptionsHandler; * @public */ OptionsHandler.prototype.ensureOptions = function (options) { - return this.completeOptions(options) - .then(this.ensureSrc) - .then(this.ensureDest) - .then(this.assertOrigin) - .then(this.assertTarget) - .then(this.ensureIndent) - .then(this.verboseOptions); + return this.completeOptions(options) + .then(this.ensureSrc) + .then(this.ensureDest) + .then(this.assertOrigin) + .then(this.assertTarget) + .then(this.ensureIndent) + .then(this.verboseOptions); }; /** @@ -501,16 +497,16 @@ OptionsHandler.prototype.ensureOptions = function (options) { * @public */ OptionsHandler.prototype.validateTransformation = function (options) { - return assertOptions(options, ['origin', 'target']) - .then(function (assertedOptions) { - return new Promise(function (resolve, reject) { - var transformation = assertedOptions.origin + '2' + assertedOptions.target; - if (Constants.TRANSFORMATIONS.indexOf(transformation) < 0) { - reject(new Error('Unsupported target type transformation \'' + assertedOptions.origin + ' -> ' + assertedOptions.target + '\' configured in options.')); - } else { - resolve([assertedOptions, transformation]); - } - }); + return assertOptions(options, ['origin', 'target']) + .then(function (assertedOptions) { + return new Promise(function (resolve, reject) { + var transformation = assertedOptions.origin + '2' + assertedOptions.target; + if (Constants.TRANSFORMATIONS.indexOf(transformation) < 0) { + reject(new Error('Unsupported target type transformation \'' + assertedOptions.origin + ' -> ' + assertedOptions.target + '\' configured in options.')); + } else { + resolve([assertedOptions, transformation]); + } + }); }); }; @@ -549,21 +545,21 @@ OptionsHandler.prototype.assertOptions = assertOptions; * @private */ function assertOptions(options, properties) { - return new Promise(function (resolve, reject) { - if (!options) { - return reject(new Error('missing options object!')); - } - if (properties && properties.length > 0) { - var missing = []; - properties.forEach(function (p) { - if (!options[p]) { - missing.push('options.' + p); - } - }); - if (missing.length > 0) { - return reject(new Error('missing options property(s): ' + JSON.stringify(missing) + '!')); - } + return new Promise(function (resolve, reject) { + if (!options) { + return reject(new Error('missing options object!')); + } + if (properties && properties.length > 0) { + var missing = []; + properties.forEach(function (p) { + if (!options[p]) { + missing.push('options.' + p); } - resolve(options); - }); + }); + if (missing.length > 0) { + return reject(new Error('missing options property(s): ' + JSON.stringify(missing) + '!')); + } + } + resolve(options); + }); } diff --git a/lib/options-schema.js b/lib/options-schema.js new file mode 100644 index 0000000..e69de29 diff --git a/lib/reader.js b/lib/reader.js index 1c64e2d..5ba4deb 100644 --- a/lib/reader.js +++ b/lib/reader.js @@ -1,5 +1,3 @@ -'use strict'; - var Constants = require('./constants'); var LogWrapper = require('./log-wrapper'); var OptionsHandler = require('./options-handler'); @@ -12,10 +10,10 @@ var path = require('path'); var isStream = require('is-stream'); var stringify = require('json-stringify-safe'); -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CONSTRUCTOR -/////////////////////////////////////////////////////////////////////////////// - +// //////////////////////////////////////////////////////////////////////////// +// TODO turn into class /** * Constructs the `Reader` with an (optional) logger. * @@ -31,266 +29,269 @@ var stringify = require('json-stringify-safe'); * var reader = new Reader(logger); */ function Reader(logger) { - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); + /** + * The logger instance. + * + * @member {(logger|cli|console)} + * @private + */ + this.logInstance = new LogWrapper(logger); - /** - * The options handler. - * - * @type {OptionsHandler} - * @private - */ - this.optionsHandler = new OptionsHandler(logger); + /** + * The options handler. + * + * @type {OptionsHandler} + * @private + */ + this.optionsHandler = new OptionsHandler(logger); - /** - * The validator. - * - * @type {Validator} - */ - this.validator = new Validator(logger); + /** + * The validator. + * + * @type {Validator} + */ + this.validator = new Validator(logger); - var self = this; + var self = this; - /** - * Creates a function to read from the passed source in to the given buffer array. - * - * @param {stream.Readable} readable - The source to read from. - * @param {array} bufs - The temporary buffer array. - * @returns {Function} - The function which reads and buffers. - * @private - */ - function createReadableFunction(readable, bufs) { - return function () { - var chunk; - while (null !== (chunk = readable.read())) { - self.logInstance.trace('JSON chunk: ', chunk); - bufs.push(chunk); - } - }; - } + /** + * Creates a function to read from the passed source in to the given buffer array. + * + * @param {stream.Readable} readable - The source to read from. + * @param {array} bufs - The temporary buffer array. + * @returns {Function} - The function which reads and buffers. + * @private + */ + function createReadableFunction(readable, bufs) { + return function () { + var chunk; + while (null !== (chunk = readable.read())) { + self.logInstance.trace('JSON chunk: ', chunk); + bufs.push(chunk); + } + }; + } - /** - * Reads from a passed stream and resolves by callback. - * - * @param {stream.Readable} readable - The source to read from. - * @param {function} resolve - Callback for success case. - * @param {function} reject - Callback for Error case. - * @param {string} origin - Origin type, must be 'yaml' or 'json'/'js'. - * @private - */ - function readFromStream(readable, resolve, reject, origin) { - var bufs = []; - readable - .on('readable', createReadableFunction(readable, bufs)) - .on('error', function (err) { - reject(err); - }) - .on('end', function () { - var buffer = Buffer.concat(bufs); - try { - self.logInstance.debug(origin + ' reading from Readable'); - if (origin === Constants.JSON || origin === Constants.JS) { - resolve(JSON.parse(buffer.toString(Constants.UTF8))); - } else {// HINT: commented (see below): if (origin === Constants.YAML) { - resolve(jsYaml.safeLoad(buffer.toString(Constants.UTF8))); - } - // HINT: for the sake of test coverage it's commented, since this is a private method we have control over options.origin inside this class! - //else { - // reject(new Error('Unsupported type: ' + origin + ' to read from Readable')); - //} - } catch (err) { // probably a SyntaxError for JSON or a YAMLException - self.logInstance.error('Unexpected error: ' + err.stack); - readable.emit('error', err); // send to .on('error',... - } - }); - } + /** + * Reads from a passed stream and resolves by callback. + * + * @param {stream.Readable} readable - The source to read from. + * @param {function} resolve - Callback for success case. + * @param {function} reject - Callback for Error case. + * @param {string} origin - Origin type, must be 'yaml' or 'json'/'js'. + * @private + */ + function readFromStream(readable, resolve, reject, origin) { + var bufs = []; + readable + .on('readable', createReadableFunction(readable, bufs)) + .on('error', function (err) { + reject(err); + }) + .on('end', function () { + var buffer = Buffer.concat(bufs); + try { + self.logInstance.debug(origin + ' reading from Readable'); + if (origin === Constants.JSON || origin === Constants.JS) { + resolve(JSON.parse(buffer.toString(Constants.UTF8))); + } else {// HINT: commented (see below): if (origin === Constants.YAML) { + resolve(jsYaml.safeLoad(buffer.toString(Constants.UTF8))); + } + // HINT: for the sake of test coverage it's commented, since this is a private method we have control over options.origin inside this class! + //else { + // reject(new Error('Unsupported type: ' + origin + ' to read from Readable')); + //} + } catch (err) { // probably a SyntaxError for JSON or a YAMLException + self.logInstance.error('Unexpected error: ' + err.stack); + readable.emit('error', err); // send to .on('error',... + } + }); + } - /////////////////////////////////////////////////////////////////////////////// - // API METHODS (PUBLIC) - /////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // API METHODS (PUBLIC) + /////////////////////////////////////////////////////////////////////////////// - /** - * Reads the data from a given JS or JSON source. - * - * @param {Options} options - Contains the JS/JSON source reference to read from. - * @returns {Promise} - Contains the read JS object. - * @public - * @example - * var Reader = require('jy-transform').Reader; - * var logger = ...; - * var reader = new Reader(logger); - * - * // --- from file path - * - * var options = { + /** + * Reads the data from a given JS or JSON source. + * + * @param {Options} options - Contains the JS/JSON source reference to read from. + * @returns {Promise} - Contains the read JS object. + * @public + * @example + * var Reader = require('jy-transform').Reader; + * var logger = ...; + * var reader = new Reader(logger); + * + * // --- from file path + * + * var options = { * src: 'foo.js' * }; - * - * reader.readJs(options) - * .then(function (obj){ + * + * reader.readJs(options) + * .then(function (obj){ * logger.info(JSON.stringify(obj)); * }) - * .catch(function (err) { + * .catch(function (err) { * logger.error(err.stack); * }); - * - * - * // --- from Readable - * - * options = { + * + * + * // --- from Readable + * + * options = { * src: fs.createReadStream('foo.js') * }; - * - * reader.readJs(options) - * .then(function (obj){ + * + * reader.readJs(options) + * .then(function (obj){ * logger.info(JSON.stringify(obj)); * }) - * .catch(function (err) { + * .catch(function (err) { * logger.error(err.stack); * }); - * - * - * // --- from object - * - * options = { + * + * + * // --- from object + * + * options = { * src: { * foo: 'bar' * } * }; - * - * reader.readJs(options) - * .then(function (obj){ + * + * reader.readJs(options) + * .then(function (obj){ * logger.info(JSON.stringify(obj)); * }) - * .catch(function (err) { + * .catch(function (err) { * logger.error(err.stack); * }); - */ - this.readJs = function (options) { - self.logInstance.trace('OPTIONS BEFORE ASSERTING IN readJs:::' + JSON.stringify(options)); - return self.optionsHandler.assertOptions(options, ['src']) - .then(function (assertedOptions) { - return new Promise(function (resolve, reject) { - if (typeof assertedOptions.src === 'string') { - try { - var resolvedPath = path.resolve('', assertedOptions.src); + */ + this.readJs = function (options) { + self.logInstance.trace('OPTIONS BEFORE ASSERTING IN readJs:::' + JSON.stringify(options)); + return self.optionsHandler.assertOptions(options, ['src']) + .then(function (assertedOptions) { + return new Promise(function (resolve, reject) { + if (typeof assertedOptions.src === 'string') { + try { + var resolvedPath = path.resolve('', assertedOptions.src); - if ((path.extname(assertedOptions.src) === '.js' || options.origin === Constants.JS) && options.imports && options.imports !== '') { + if ((path.extname(assertedOptions.src) === '.js' || options.origin === Constants.JS) && options.imports && options.imports !== '') { - if (!self.validator.validateIdentifier(options.imports)) { - reject(new Error('found invalid identifier for reading from exports: ' + stringify(options.imports, null, 4))); - } else { - var json = require(resolvedPath)[options.imports]; - self.logInstance.trace('LOADED JSON object (' + options.imports + '):: ' + stringify(json, null, 4)); - if (!json) { - reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object but could not find this object, pls ensure that file ' + assertedOptions.src + ' contains it.')); - } else { - resolve(json); - } - } + if (!self.validator.validateIdentifier(options.imports)) { + reject(new Error('found invalid identifier for reading from exports: ' + stringify(options.imports, null, 4))); + } else { + var json = require(resolvedPath)[options.imports]; + self.logInstance.trace('LOADED JSON object (' + options.imports + '):: ' + stringify(json, null, 4)); + if (!json) { + reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object but could not find this object, pls ensure that file ' + assertedOptions.src + ' contains it.')); + } else { + resolve(json); + } + } - } else { - resolve(require(resolvedPath)); - } - } catch (err) { // probably a SyntaxError - self.logInstance.error('Unexpected error: ' + err.stack); - reject(err); - } - } else if (isStream.readable(assertedOptions.src)) { - readFromStream(assertedOptions.src, resolve, reject, Constants.JSON); - } else if (options.imports && options.imports !== '') { + } else { + resolve(require(resolvedPath)); + } + } catch (err) { // probably a SyntaxError + self.logInstance.error('Unexpected error: ' + err.stack); + reject(err); + } + } else if (isStream.readable(assertedOptions.src)) { + readFromStream(assertedOptions.src, resolve, reject, Constants.JSON); + } else if (options.imports && options.imports !== '') { - if (!self.validator.validateIdentifier(options.imports)) { - reject(new Error('found invalid identifier for reading from object: ' + stringify(options.imports, null, 4))); - } else { - var obj = assertedOptions.src[options.imports]; - self.logInstance.trace('LOADED JSON object (' + options.imports + '):: ' + stringify(obj, null, 4)); - if (!obj) { - reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object but could not find this object, pls ensure that object source contains it.')); - } else { - resolve(obj); - } - } - } else { - resolve(assertedOptions.src); - } - }); - }); - }; + if (!self.validator.validateIdentifier(options.imports)) { + reject(new Error('found invalid identifier for reading from object: ' + stringify(options.imports, null, 4))); + } else { + var obj = assertedOptions.src[options.imports]; + self.logInstance.trace('LOADED JSON object (' + options.imports + '):: ' + stringify(obj, null, 4)); + if (!obj) { + reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object but could not find this object, pls ensure that object source contains it.')); + } else { + resolve(obj); + } + } + } else { + resolve(assertedOptions.src); + } + }); + }); + }; - /** - * Loads a single YAML source containing document and returns a JS object. - * - * *NOTE:* This function does not understand multi-document sources, it throws - * exception on those. - * - * @param {Options} options - Contains the YAML source reference to read from. - * @returns {Promise} - Contains the read JS object. - * @public - * @example - * var Reader = require('jy-transform').Reader; - * var logger = ...; - * var reader = new Reader(logger); - * - * // --- from file path - * - * options = { + /** + * Loads a single YAML source containing document and returns a JS object. + * + * *NOTE:* This function does not understand multi-document sources, it throws + * exception on those. + * + * @param {Options} options - Contains the YAML source reference to read from. + * @returns {Promise} - Contains the read JS object. + * @public + * @example + * var Reader = require('jy-transform').Reader; + * var logger = ...; + * var reader = new Reader(logger); + * + * // --- from file path + * + * options = { * src: 'foo.yml' * }; - * - * reader.readYaml(options) - * .then(function (obj){ + * + * reader.readYaml(options) + * .then(function (obj){ * logger.info(JSON.stringify(obj)); * }) - * .catch(function (err) { + * .catch(function (err) { * logger.error(err.stack); * }); - * - * - * // --- from Readable - * - * options = { + * + * + * // --- from Readable + * + * options = { * src: fs.createReadStream('foo.yml') * }; - * - * reader.readJs(options) - * .then(function (obj){ + * + * reader.readJs(options) + * .then(function (obj){ * logger.info(JSON.stringify(obj)); * }) - * .catch(function (err) { + * .catch(function (err) { * logger.error(err.stack); * }); - */ - this.readYaml = function (options) { - self.logInstance.trace('OPTIONS BEFORE ASSERTING IN readYaml::: ' + JSON.stringify(options)); - return self.optionsHandler.assertOptions(options, ['src']) - .then(function (assertedOptions) { - return new Promise(function (resolve, reject) { - if (typeof assertedOptions.src === 'string') { - // load source from YAML file - fs.readFileAsync(assertedOptions.src, Constants.UTF8) - .then(function (yaml) { - self.logInstance.debug('YAML loaded from file ' + assertedOptions.src); - try { - resolve(jsYaml.safeLoad(yaml)); - } catch (err) { // probably a YAMLException - self.logInstance.error('Unexpected error: ' + err.stack); - reject(err); - } - }); - } else if (isStream.readable(assertedOptions.src)) { - readFromStream(assertedOptions.src, resolve, reject, Constants.YAML); - } else { - resolve(assertedOptions.src); - } - }); - }); - }; + */ + this.readYaml = function (options) { + self.logInstance.trace('OPTIONS BEFORE ASSERTING IN readYaml::: ' + JSON.stringify(options)); + return self.optionsHandler.assertOptions(options, ['src']) + .then(function (assertedOptions) { + return new Promise(function (resolve, reject) { + if (typeof assertedOptions.src === 'string') { + // load source from YAML file + fs.readFileAsync(assertedOptions.src, Constants.UTF8) + .then(function (yaml) { + self.logInstance.debug('YAML loaded from file ' + assertedOptions.src); + try { + resolve(jsYaml.safeLoad(yaml)); + } catch (err) { // probably a YAMLException + self.logInstance.error('Unexpected error: ' + err.stack); + reject(err); + } + }) + .catch((err) => { + // TODO error handling for reading here? + }); + } else if (isStream.readable(assertedOptions.src)) { + readFromStream(assertedOptions.src, resolve, reject, Constants.YAML); + } else { + resolve(assertedOptions.src); + } + }); + }); + }; ///** // * Parses string as single YAML source containing multiple YAML document and turns a JS objects array. diff --git a/lib/transformer.js b/lib/transformer.js index e1e5790..4e1b594 100644 --- a/lib/transformer.js +++ b/lib/transformer.js @@ -1,5 +1,3 @@ -'use strict'; - var Writer = require('./writer'); var Reader = require('./reader'); var LogWrapper = require('./log-wrapper'); @@ -7,10 +5,10 @@ var OptionsHandler = require('./options-handler'); var middleware = require('./middleware'); var path = require('path'); -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CONSTRUCTOR -/////////////////////////////////////////////////////////////////////////////// - +// //////////////////////////////////////////////////////////////////////////// +// TODO turn into class /** * Constructs the `Transformer` with options and an (optional) logger. * @@ -26,343 +24,342 @@ var path = require('path'); */ function Transformer(logger) { - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); + /** + * The logger instance. + * + * @member {(logger|cli|console)} + * @private + */ + this.logInstance = new LogWrapper(logger); - /** - * The options handler. - * - * @type {OptionsHandler} - * @private - */ - this.optionsHandler = new OptionsHandler(logger); + /** + * The options handler. + * + * @type {OptionsHandler} + * @private + */ + this.optionsHandler = new OptionsHandler(logger); - /** - * The internal `Writer` instance. - * - * @type {Writer} - * @private - */ - var writer = new Writer(logger); + /** + * The internal `Writer` instance. + * + * @type {Writer} + * @private + */ + var writer = new Writer(logger); - /** - * The internal `Reader` instance. - * - * @type {Reader} - * @private - */ - var reader = new Reader(logger); + /** + * The internal `Reader` instance. + * + * @type {Reader} + * @private + */ + var reader = new Reader(logger); - /** - * Ensures that basic middleware is set. - */ - var ensureMiddleware = middleware.ensureMiddleware; + /** + * Ensures that basic middleware is set. + */ + var ensureMiddleware = middleware.ensureMiddleware; - /** - * Internal delegate function to execute transformation logic (ITMO): - * - Input - * - Transform - * - Middleware - * - Write - * - * @param {Options} options - The configuration for a transformation. - * @param {function} read - The reader function. - * @param {function} [middleware] - The middleware to apply. - * @param {function} write - The writer functions. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.itmo(options, reader.readYaml, middleware, writer.writeJson); - * @private - */ - function itmo(options, read, middleware, write) { - return read(options) - .then(ensureMiddleware(middleware)) - .then(function (json) { - return write(json, options); - }); - } + /** + * Internal delegate function to execute transformation logic (ITMO): + * - Input (read) + * - Transform + Middleware + * - Output (write) + * + * @param {Options} options - The configuration for a transformation. + * @param {function} read - The reader function. + * @param {function} [middleware] - The middleware to apply. + * @param {function} write - The writer functions. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { + * ... + * }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.itmo(options, reader.readYaml, middleware, writer.writeJson); + * @private + */ + function itmo(options, read, middleware, write) { + return read(options) + .then(ensureMiddleware(middleware)) + .then(function (json) { + return write(json, options); + }); + } - /////////////////////////////////////////////////////////////////////////////// - // TRANSFORMATION METHODS (DELEGATES) - /////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // TRANSFORMATION METHODS (DELEGATES) + /////////////////////////////////////////////////////////////////////////////// - /** - * Convert YAML to JS. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { + /** + * Convert YAML to JS. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { * ... * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.yamlToJs(options, middleware); - * @see itmo - * @private - */ - this.yamlToJs = function (options, middleware) { - return itmo(options, reader.readYaml, middleware, writer.writeJs); - }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.yamlToJs(options, middleware); + * @see itmo + * @private + */ + this.yamlToJs = function (options, middleware) { + return itmo(options, reader.readYaml, middleware, writer.writeJs); + }; - /** - * Convert YAML to JSON. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { + /** + * Convert YAML to JSON. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { * ... * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.yamlToJson(options, middleware); - * @see itmo - * @private - */ - this.yamlToJson = function (options, middleware) { - return itmo(options, reader.readYaml, middleware, writer.writeJson); - }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.yamlToJson(options, middleware); + * @see itmo + * @private + */ + this.yamlToJson = function (options, middleware) { + return itmo(options, reader.readYaml, middleware, writer.writeJson); + }; - /** - * Convert JS to YAML. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { + /** + * Convert JS to YAML. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { * ... * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsToYaml(options, middleware); - * @see itmo - * @private - */ - this.jsToYaml = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeYaml); - }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.jsToYaml(options, middleware); + * @see itmo + * @private + */ + this.jsToYaml = function (options, middleware) { + return itmo(options, reader.readJs, middleware, writer.writeYaml); + }; - /** - * Convert JSON to JS. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { + /** + * Convert JSON to JS. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { * ... * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsonToJs(options, middleware); - * @see itmo - * @private - */ - this.jsonToJs = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeJs); - }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.jsonToJs(options, middleware); + * @see itmo + * @private + */ + this.jsonToJs = function (options, middleware) { + return itmo(options, reader.readJs, middleware, writer.writeJs); + }; - /** - * Convert JS to JSON. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { + /** + * Convert JS to JSON. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { * ... * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsToJson(options, middleware); - * @see itmo - * @private - */ - this.jsToJson = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeJson); - }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.jsToJson(options, middleware); + * @see itmo + * @private + */ + this.jsToJson = function (options, middleware) { + return itmo(options, reader.readJs, middleware, writer.writeJson); + }; - /** - * Convert YAML to YAML. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { + /** + * Convert YAML to YAML. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { * ... * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.yamlToYaml(options, middleware); - * @see itmo - * @private - */ - this.yamlToYaml = function (options, middleware) { - return itmo(options, reader.readYaml, middleware, writer.writeYaml); - }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.yamlToYaml(options, middleware); + * @see itmo + * @private + */ + this.yamlToYaml = function (options, middleware) { + return itmo(options, reader.readYaml, middleware, writer.writeYaml); + }; - /** - * Convert JSON to JSON. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { + /** + * Convert JSON to JSON. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { * ... * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsonToJson(options, middleware); - * @see itmo - * @private - */ - this.jsonToJson = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeJson); - }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.jsonToJson(options, middleware); + * @see itmo + * @private + */ + this.jsonToJson = function (options, middleware) { + return itmo(options, reader.readJs, middleware, writer.writeJson); + }; - /** - * Convert JS to JS. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (json) { + /** + * Convert JS to JS. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (json) { * ... * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsToJs(options, middleware); - * @see itmo - * @private - */ - this.jsToJs = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeJs); - }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.jsToJs(options, middleware); + * @see itmo + * @private + */ + this.jsToJs = function (options, middleware) { + return itmo(options, reader.readJs, middleware, writer.writeJs); + }; - /** - * A transformation name to internal function mapping. - * - * @namespace - * @property {function} yaml2js - The transformation function for YAML -> JS. - * @property {function} yaml2json - The transformation function for YAML -> JSON. - * @property {function} yaml2yaml - The transformation function for YAML -> YAML. - * @property {function} json2yaml - The transformation function for JSON -> YAML. - * @property {function} json2js - The transformation function for JSON -> JS. - * @property {function} json2json - The transformation function for JSON -> JSON. - * @property {function} js2yaml - The transformation function for JS -> YAML. - * @property {function} js2json - The transformation function for JS -> JSON. - * @property {function} js2js - The transformation function for JS -> JS. - * @private - */ - this.transformations = { - yaml2js: this.yamlToJs, - yaml2json: this.yamlToJson, - yaml2yaml: this.yamlToYaml, - json2yaml: this.jsToYaml, - json2js: this.jsonToJs, - json2json: this.jsonToJson, - js2yaml: this.jsToYaml, - js2json: this.jsToJson, - js2js: this.jsToJs - }; + /** + * A transformation name to internal function mapping. + * + * @namespace + * @property {function} yaml2js - The transformation function for YAML -> JS. + * @property {function} yaml2json - The transformation function for YAML -> JSON. + * @property {function} yaml2yaml - The transformation function for YAML -> YAML. + * @property {function} json2yaml - The transformation function for JSON -> YAML. + * @property {function} json2js - The transformation function for JSON -> JS. + * @property {function} json2json - The transformation function for JSON -> JSON. + * @property {function} js2yaml - The transformation function for JS -> YAML. + * @property {function} js2json - The transformation function for JS -> JSON. + * @property {function} js2js - The transformation function for JS -> JS. + * @private + */ + this.transformations = { + yaml2js: this.yamlToJs, + yaml2json: this.yamlToJson, + yaml2yaml: this.yamlToYaml, + json2yaml: this.jsToYaml, + json2js: this.jsonToJs, + json2json: this.jsonToJson, + js2yaml: this.jsToYaml, + js2json: this.jsToJson, + js2js: this.jsToJs + }; } /////////////////////////////////////////////////////////////////////////////// @@ -413,12 +410,12 @@ exports = module.exports = Transformer; * }); */ Transformer.prototype.transform = function (options, middleware) { - var self = this; - this.logInstance.debug('transform'); - return this.optionsHandler.ensureOptions(options) - .then(this.optionsHandler.validateTransformation) - .spread(function (ensuredOptions, transformation) { - self.logInstance.info('Calling transformation: ' + transformation); - return self.transformations[transformation](ensuredOptions, middleware); - }); + var self = this; + this.logInstance.debug('transform'); + return this.optionsHandler.ensureOptions(options) + .then(this.optionsHandler.validateTransformation) + .spread(function (ensuredOptions, transformation) { + self.logInstance.info('Calling transformation: ' + transformation); + return self.transformations[transformation](ensuredOptions, middleware); + }); }; diff --git a/lib/type-definitions.js b/lib/type-definitions.js new file mode 100644 index 0000000..bcfe2cf --- /dev/null +++ b/lib/type-definitions.js @@ -0,0 +1,45 @@ +/** + * @module type-definitions + * @description The type definitions for this module. + */ + +// ///////////////////////////////////////////////////////////////////////////// +// EXTERNAL DEFINITIONS +// ///////////////////////////////////////////////////////////////////////////// + +/** + * Hapi.js Joi. + * @external joi + * @see {@link https://github.com/hapijs/joi Hapi Joi} + */ + +/** + * Joi validation error. + * @typedef ValidationError + * @memberof external:joi + * @see {@link hhttps://github.com/hapijs/joi/blob/v10.2.0/API.md#errors Joi errors} + */ + +/** + * Hapi.js Joi schema. + * @typedef Schema + * @memberof external:joi + * @see {@link https://github.com/hapijs/joi#usage Hapi Joi schema} + */ + +// ///////////////////////////////////////////////////////////////////////////// +// INTERNAL DEFINITIONS +// ///////////////////////////////////////////////////////////////////////////// + +/** + * The configuration properties provided to the framework functions. + * @typedef {object} Options + * @property {string} [origin=yaml] - The origin type. + * @property {string} [target=js] - The target type. + * @property {(string|Readable|object)} src - The source (`string` type is treated as a file path). + * @property {(string|Writable|object)} [dest] - The destination (`string` type is treated as a file path). + * @property {number} [indent=4] - The indention in files. + * @property {string} [imports=undefined] - The exports name for reading from JS source file or objects only. + * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. + * @public + */ diff --git a/lib/validator.js b/lib/validator.js index e1064f7..efa165a 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -1,5 +1,3 @@ -'use strict'; - var Constants = require('./constants'); var LogWrapper = require('./log-wrapper'); var OptionsHandler = require('./options-handler'); @@ -8,9 +6,9 @@ var stringify = require('json-stringify-safe'); var path = require('path'); var fs = Promise.promisifyAll(require('fs')); -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CONSTRUCTOR -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// /** * Created at [Generating a regular expression to match valid JavaScript identifiers](https://mathiasbynens.be/demo/javascript-identifier-regex). @@ -20,6 +18,8 @@ var fs = Promise.promisifyAll(require('fs')); */ var identifierRegExpECMAScript6 = /^(?!(?:do|if|in|for|let|new|try|var|case|else|enum|eval|null|this|true|void|with|await|break|catch|class|const|false|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$)(?:[\$A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D])(?:[\$0-9A-Z_a-z\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF])*$/; +// TODO turn into class + /** * Constructs the `Validator` with an (optional) logger. * @@ -37,45 +37,45 @@ var identifierRegExpECMAScript6 = /^(?!(?:do|if|in|for|let|new|try|var|case|else */ function Validator(logger) { - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); + /** + * The logger instance. + * + * @member {(logger|cli|console)} + * @private + */ + this.logInstance = new LogWrapper(logger); - /** - * The options handler. - * - * @type {OptionsHandler} - * @private - */ - this.optionsHandler = new OptionsHandler(logger); + /** + * The options handler. + * + * @type {OptionsHandler} + * @private + */ + this.optionsHandler = new OptionsHandler(logger); - var self = this; + var self = this; - /** - * This method checks if a given `identifier` is a valid ECMAScript 6 identifier. - * - * @param {string} identifier - The identifier to check. - * @returns {boolean} - `true` if valid, else `false`. - * @public - * @example - * var Validator = require('./validator.js'); - * var logger = ...; - * var validator = new Validator(logger); - * var identifier = 'foo'; - * - * logger.info('valid = ' + validator.validateIdentifier(identifier)); - */ - this.validateIdentifier = function (identifier) { - if (typeof identifier !== 'string') { - self.logInstance.debug('Invalid identifier, not a string, was type of: ' + (typeof identifier)); - return false; - } - return identifierRegExpECMAScript6.test(identifier); - }; + /** + * This method checks if a given `identifier` is a valid ECMAScript 6 identifier. + * + * @param {string} identifier - The identifier to check. + * @returns {boolean} - `true` if valid, else `false`. + * @public + * @example + * var Validator = require('./validator.js'); + * var logger = ...; + * var validator = new Validator(logger); + * var identifier = 'foo'; + * + * logger.info('valid = ' + validator.validateIdentifier(identifier)); + */ + this.validateIdentifier = function (identifier) { + if (typeof identifier !== 'string') { + self.logInstance.debug('Invalid identifier, not a string, was type of: ' + (typeof identifier)); + return false; + } + return identifierRegExpECMAScript6.test(identifier); + }; } Validator.prototype.constructor = Validator; diff --git a/lib/writer.js b/lib/writer.js index 0326e34..8613edd 100644 --- a/lib/writer.js +++ b/lib/writer.js @@ -1,5 +1,3 @@ -'use strict'; - var Constants = require('./constants'); var LogWrapper = require('./log-wrapper'); var OptionsHandler = require('./options-handler'); @@ -14,9 +12,10 @@ var fs = Promise.promisifyAll(require('fs')); var os = require('os'); var isStream = require('is-stream'); -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// // CONSTRUCTOR -/////////////////////////////////////////////////////////////////////////////// +// //////////////////////////////////////////////////////////////////////////// +// TODO turn into class /** * Constructs the `Writer` with an (optional) logger. @@ -35,474 +34,475 @@ var isStream = require('is-stream'); */ function Writer(logger) { - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); + /** + * The logger instance. + * + * @member {(logger|cli|console)} + * @private + */ + this.logInstance = new LogWrapper(logger); - /** - * The options handler. - * - * @type {OptionsHandler} - * @private - */ - this.optionsHandler = new OptionsHandler(logger); + /** + * The options handler. + * + * @type {OptionsHandler} + * @private + */ + this.optionsHandler = new OptionsHandler(logger); - /** - * The validator. - * - * @type {Validator} - */ - this.validator = new Validator(logger); + /** + * The validator. + * + * @type {Validator} + */ + this.validator = new Validator(logger); - var self = this; + var self = this; - /////////////////////////////////////////////////////////////////////////////// - // METHODS (PRIVATE) - /////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// + // METHODS (PRIVATE) + /////////////////////////////////////////////////////////////////////////////// - /** - * Creates a potential named `'module.exports[.exportsTo]'` string. - * - * @param {string} [exportsTo] - The export name. - * @returns {Promise} - Promise resolves with the exports string. - * @private - */ - function createExportsString(exportsTo) { - return new Promise(function (resolve, reject) { - var exports = 'module.exports'; - if (exportsTo && exportsTo !== '') { - if (!self.validator.validateIdentifier(exportsTo)) { - return reject(new Error('Found invalid identifier for exports: ' + exportsTo)); - } else { - exports = exports + '.' + exportsTo + ' = '; - } - } else { - exports = exports + ' = '; - } - resolve(exports); - }); - } + /** + * Creates a potential named `'module.exports[.exportsTo]'` string. + * + * @param {string} [exportsTo] - The export name. + * @returns {Promise} - Promise resolves with the exports string. + * @private + */ + function createExportsString(exportsTo) { + return new Promise(function (resolve, reject) { + var exports = 'module.exports'; + if (exportsTo && exportsTo !== '') { + if (!self.validator.validateIdentifier(exportsTo)) { + return reject(new Error('Found invalid identifier for exports: ' + exportsTo)); + } else { + exports = exports + '.' + exportsTo + ' = '; + } + } else { + exports = exports + ' = '; + } + resolve(exports); + }); + } - /** - * Serialize a JS object to string. - * - * @param {object} object - The JS Object to serialize. - * @param {number} indent - The indention. - * @param {string} [exportsTo] - Name for export (*IMPORTANT:* must be a valid ES6 identifier). - * @returns {Promise} - Promise resolve with the serialized JS object. - * @private - * @todo [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> `'\'use strict\';' + os.EOL + os.EOL + ...`)? - */ - function serializeJsToString(object, indent, exportsTo) { - return createExportsString(exportsTo) - .then(function(exportsStr) { - return exportsStr + serializeJs.serialize(object, {indent: indent}) + ';' + os.EOL; - }); - } + /** + * Serialize a JS object to string. + * + * @param {object} object - The JS Object to serialize. + * @param {number} indent - The indention. + * @param {string} [exportsTo] - Name for export (*IMPORTANT:* must be a valid ES6 identifier). + * @returns {Promise} - Promise resolve with the serialized JS object. + * @private + * @todo [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> `'\'use strict\';' + os.EOL + os.EOL + ...`)? + */ + function serializeJsToString(object, indent, exportsTo) { + return createExportsString(exportsTo) + .then(function(exportsStr) { + return exportsStr + serializeJs.serialize(object, {indent: indent}) + ';' + os.EOL; + }); + } - /** - * Serialize a JS object to JSON string. - * - * @param {object} object - Object to serialize. - * @param {number} indent - Indention. - * @returns {string} - The serialized JSON. - * @private - */ - function serializeJsToJsonString(object, indent) { - return jsonStringifySafe(object, null, indent) + os.EOL; - } + /** + * Serialize a JS object to JSON string. + * + * @param {object} object - Object to serialize. + * @param {number} indent - Indention. + * @returns {string} - The serialized JSON. + * @private + */ + function serializeJsToJsonString(object, indent) { + return jsonStringifySafe(object, null, indent) + os.EOL; + } - /** - * Turns the destination file name into a name containing a consecutive - * number if it exists. It iterates over the files until it finds a file - * name which does not exist. - * - * @param {string} dest - The destination file. - * @returns {string} - A consecutive file name or the original one if - * `dest` file does not exist. - * @private - */ - function getConsecutiveDestName(dest) { - var tmpDest = dest; - var i = 1; - var destDirName = path.dirname(tmpDest); - var ext = path.extname(tmpDest); - var basename = path.basename(tmpDest, ext); - while (fs.existsSync(tmpDest)) { - tmpDest = path.join(destDirName, basename + '(' + i++ + ')' + ext); - } - return tmpDest; + /** + * Turns the destination file name into a name containing a consecutive + * number if it exists. It iterates over the files until it finds a file + * name which does not exist. + * + * @param {string} dest - The destination file. + * @returns {string} - A consecutive file name or the original one if + * `dest` file does not exist. + * @private + */ + function getConsecutiveDestName(dest) { + var tmpDest = dest; + var i = 1; + var destDirName = path.dirname(tmpDest); + var ext = path.extname(tmpDest); + var basename = path.basename(tmpDest, ext); + while (fs.existsSync(tmpDest)) { + tmpDest = path.join(destDirName, basename + '(' + i++ + ')' + ext); } + return tmpDest; + } + + /** + * Writes a serialized object to file. + * + * @param {string} object - The object to write into file. + * @param {string} dest - The file destination path. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @param {function} resolve - The Promise `resolve` callback. + * @param {function} reject - The Promise `reject` callback. + * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. + * @see {@link Constants#YAML} + * @see {@link Constants#JSON} + * @see {@link Constants#JS} + * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). + * @throws {Error} - If serialized JSON file could not be written due to any reason. + * @private + */ + function writeToFile(object, dest, target, resolve, reject, forceOverwrite) { /** - * Writes a serialized object to file. + * Ensures that all dirs exists for `dest` and writes the file. * - * @param {string} object - The object to write into file. - * @param {string} dest - The file destination path. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @param {function} resolve - The Promise `resolve` callback. - * @param {function} reject - The Promise `reject` callback. - * @param {boolean} [forceOverwrite] - Force overwriting the destination file if `true`. - * @see {@link Constants#YAML} - * @see {@link Constants#JSON} - * @see {@link Constants#JS} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @throws {Error} - If serialized JSON file could not be written due to any reason. * @private */ - function writeToFile(object, dest, target, resolve, reject, forceOverwrite) { + function mkdirAndWrite() { + var destDir = path.dirname(dest); + self.logInstance.debug('Destination dir: ' + destDir); + mkdirp(destDir) + .then(function () { + self.logInstance.debug('Destination dir ' + destDir + ' successfully written'); + if (forceOverwrite === undefined || forceOverwrite === false) { + dest = getConsecutiveDestName(dest); + self.logInstance.info('Setting was: do not overwrite, using destination ' + dest + '.'); + } + return fs.writeFileAsync(dest, object, Constants.UTF8); + }) + .then(function () { + resolve('Writing \'' + target + '\' file \'' + dest + '\' successful.'); + }) + .catch(function (err) { + err.message = 'Could not write \'' + target + '\' file \'' + dest + '\', cause: ' + err.message; + reject(err); + }); + } - /** - * Ensures that all dirs exists for dest and writes the file. - * - * @private - */ - function mkdirAndWrite() { - var destDir = path.dirname(dest); - self.logInstance.debug('Destination dir: ' + destDir); - mkdirp(destDir) - .then(function () { - self.logInstance.debug('Destination dir ' + destDir + ' successfully written'); - if (forceOverwrite === undefined || forceOverwrite === false) { - dest = getConsecutiveDestName(dest); - self.logInstance.info('Setting was: do not overwrite, using destination ' + dest + '.'); - } - return fs.writeFileAsync(dest, object, Constants.UTF8); - }) - .then(function () { - resolve('Writing \'' + target + '\' file \'' + dest + '\' successful.'); - }) - .catch(function (err) { - err.message = 'Could not write \'' + target + '\' file \'' + dest + '\', cause: ' + err.message; - reject(err); - }); + return fs.statAsync(dest) + .then(function (stats) { + if (stats.isDirectory()) { + reject(new Error('Destination file is a directory, pls specify a valid file resource!')); + } else { + // file exists + mkdirAndWrite(); } + }) + .catch(function (err) { + // ignore error (because file could possibly not exist at this point of time) + mkdirAndWrite(); + }); + } - return fs.statAsync(dest) - .then(function (stats) { - if (stats.isDirectory()) { - reject(new Error('Destination file is a directory, pls specify a valid file resource!')); - } else { - // file exists - mkdirAndWrite(); - } - }) - .catch(function (err) { - // ignore error (because file could possibly not exist at this point of time) - mkdirAndWrite(); - }); - } + /** + * Writes a string serialized data object to a stream. + * + * @param {string} object - The data to write into stream. + * @param {string} dest - The stream destination. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @param {function} resolve - The Promise `resolve` callback. + * @param {function} reject - The Promise `reject` callback. + * @see {@link Constants#YAML} + * @see {@link Constants#JSON} + * @see {@link Constants#JS} + * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). + * @throws {Error} - If serialized JS object could not be written due to any reason. + * @private + */ + function writeToStream(object, dest, target, resolve, reject) { + dest + .on('error', function (err) { + reject(err); + }) + .on('finish', function () { + resolve('Writing ' + target + ' to stream successful.'); + }); - /** - * Writes a string serialized data object to a stream. - * - * @param {string} object - The data to write into stream. - * @param {string} dest - The stream destination. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @param {function} resolve - The Promise `resolve` callback. - * @param {function} reject - The Promise `reject` callback. - * @see {@link Constants#YAML} - * @see {@link Constants#JSON} - * @see {@link Constants#JS} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @throws {Error} - If serialized JS object could not be written due to any reason. - * @private - */ - function writeToStream(object, dest, target, resolve, reject) { - dest.on('error', function (err) { - reject(err); - }) - .on('finish', function () { - resolve('Writing ' + target + ' to stream successful.'); - }); + // write stringified data + dest.write(object); + dest.end(); + } - // write stringified data - dest.write(object); - dest.end(); - } + /////////////////////////////////////////////////////////////////////////////// + // API METHODS (PUBLIC) + /////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////// - // API METHODS (PUBLIC) - /////////////////////////////////////////////////////////////////////////////// + /** + * Writes a JS object to a YAML destination. + * + * @param {object} object - The JS object to write into YAML destination. + * @param {Options} options - The write destination and indention. + * @see {@link Constants#MIN_INDENT} + * @see {@link Constants#DEFAULT_INDENT} + * @see {@link Constants#MAX_INDENT} + * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). + * @throws {Error} - If YAML destination could not be written due to any reason. + * @public + * @example + * var Writer = require('jy-transform').Writer; + * var logger = ...; + * var writer = new Writer(logger); + * + * // ---- write obj to file + * + * var obj = {...}, + * var options = { + * dest: 'result.yml', + * indent: 2 + * } + * + * writer.writeYaml(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // ---- write obj to Writable + * + * options = { + * dest: fs.createWriteStream('result.yml'), + * indent: 4 + * } + * + * writer.writeYaml(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + */ + this.writeYaml = function (object, options) { + return self.optionsHandler.ensureIndent(options) + .then(function (ensuredOptions) { + return new Promise(function (resolve, reject) { + // complain about missing destination + if (!ensuredOptions.dest) { + reject(new Error('Missing options.dest, pls specify existing destination resource!')); + } else { + var dest = ensuredOptions.dest; + var indent = ensuredOptions.indent; - /** - * Writes a JS object to a YAML destination. - * - * @param {object} object - The JS object to write into YAML destination. - * @param {Options} options - The write destination and indention. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @throws {Error} - If YAML destination could not be written due to any reason. - * @public - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * var writer = new Writer(logger); - * - * // ---- write obj to file - * - * var obj = {...}, - * var options = { - * dest: 'result.yml', - * indent: 2 - * } - * - * writer.writeYaml(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to Writable - * - * options = { - * dest: fs.createWriteStream('result.yml'), - * indent: 4 - * } - * - * writer.writeYaml(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - */ - this.writeYaml = function (object, options) { - return self.optionsHandler.ensureIndent(options) - .then(function (ensuredOptions) { - return new Promise(function (resolve, reject) { - // complain about missing destination - if (!ensuredOptions.dest) { - reject(new Error('Missing options.dest, pls specify existing destination resource!')); - } else { - var dest = ensuredOptions.dest; - var indent = ensuredOptions.indent; + var yaml; + try { + yaml = jsYaml.safeDump(object, {indent: indent, noRefs: true}); + } catch (err) { + err.message = 'Could not write YAML file \'' + dest + '\', cause: ' + err.message; + return reject(err); + } - var yaml; - try { - yaml = jsYaml.safeDump(object, {indent: indent, noRefs: true}); - } catch (err) { - err.message = 'Could not write YAML file \'' + dest + '\', cause: ' + err.message; - return reject(err); - } + if (typeof dest === 'string') { // file + writeToFile(yaml, dest, Constants.YAML, resolve, reject, ensuredOptions.force); + } else if (isStream.writable(dest)) { // stream + writeToStream(yaml, dest, Constants.YAML, resolve, reject); + } else { // object + ensuredOptions.dest = yaml; + resolve('Writing YAML to options.dest successful.'); + } + } + }); + }); + }; - if (typeof dest === 'string') { // file - writeToFile(yaml, dest, Constants.YAML, resolve, reject, ensuredOptions.force); - } else if (isStream.writable(dest)) { // stream - writeToStream(yaml, dest, Constants.YAML, resolve, reject); - } else { // object - ensuredOptions.dest = yaml; - resolve('Writing YAML to options.dest successful.'); - } - } - }); - }); - }; + /** + * Writes a JS object to a JSON destination. + * + * @param {object} object - The JS object to write into JSON destination. + * @param {Options} options - The write destination and indention. + * @see {@link Constants#MIN_INDENT} + * @see {@link Constants#DEFAULT_INDENT} + * @see {@link Constants#MAX_INDENT} + * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). + * @public + * @example + * var Writer = require('jy-transform').Writer; + * var logger = ...; + * var writer = new Writer(logger); + * + * // ---- write obj to file + * + * var obj = {...}; + * var options = { + * dest: 'result.json', + * indent: 2 + * } + * + * writer.writeJson(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // ---- write obj to Writable + * + * options = { + * dest: fs.createWriteStream('result.json'), + * indent: 4 + * } + * + * writer.writeJson(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * // ---- write obj to object + * + * options = { + * dest: {}, + * indent: 4 + * } + * + * writer.writeJson(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + */ + this.writeJson = function (object, options) { + return self.optionsHandler.ensureIndent(options) + .then(function (ensuredOptions) { + return new Promise(function (resolve, reject) { + // complain about missing destination + if (!ensuredOptions.dest) { + reject(new Error('Missing options.dest, pls specify existing destination resource!')); + } else { + var dest = ensuredOptions.dest; + var indent = ensuredOptions.indent; + if (typeof dest === 'string') { // file + writeToFile(serializeJsToJsonString(object, indent), dest, Constants.JSON, resolve, reject, ensuredOptions.force); + } else if (isStream.writable(dest)) { // stream + writeToStream(serializeJsToJsonString(object, indent), dest, Constants.JSON, resolve, reject); + } else { // object + ensuredOptions.dest = serializeJsToJsonString(object, indent); + resolve('Writing JSON to options.dest successful.'); + } + } + }); + }); + }; - /** - * Writes a JS object to a JSON destination. - * - * @param {object} object - The JS object to write into JSON destination. - * @param {Options} options - The write destination and indention. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @public - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * var writer = new Writer(logger); - * - * // ---- write obj to file - * - * var obj = {...}; - * var options = { - * dest: 'result.json', - * indent: 2 - * } - * - * writer.writeJson(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to Writable - * - * options = { - * dest: fs.createWriteStream('result.json'), - * indent: 4 - * } - * - * writer.writeJson(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * // ---- write obj to object - * - * options = { - * dest: {}, - * indent: 4 - * } - * - * writer.writeJson(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - */ - this.writeJson = function (object, options) { - return self.optionsHandler.ensureIndent(options) - .then(function (ensuredOptions) { - return new Promise(function (resolve, reject) { - // complain about missing destination - if (!ensuredOptions.dest) { - reject(new Error('Missing options.dest, pls specify existing destination resource!')); - } else { - var dest = ensuredOptions.dest; - var indent = ensuredOptions.indent; - if (typeof dest === 'string') { // file - writeToFile(serializeJsToJsonString(object, indent), dest, Constants.JSON, resolve, reject, ensuredOptions.force); - } else if (isStream.writable(dest)) { // stream - writeToStream(serializeJsToJsonString(object, indent), dest, Constants.JSON, resolve, reject); - } else { // object - ensuredOptions.dest = serializeJsToJsonString(object, indent); - resolve('Writing JSON to options.dest successful.'); - } - } + /** + * Writes a JS object to a JS destination. The object is prefixed by `module.exports = `. + * + * @param {object} object - The JSON to write into JS destination. + * @param {Options} options - The write destination and indention. + * @see {@link Constants#MIN_INDENT} + * @see {@link Constants#DEFAULT_INDENT} + * @see {@link Constants#MAX_INDENT} + * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). + * @public + * @example + * var Writer = require('jy-transform').Writer; + * var logger = ...; + * var writer = new Writer(logger); + * + * // ---- write obj to file + * + * var obj = {...}; + * var options = { + * dest: 'result.js', + * indent: 2 + * } + * + * writer.writeJs(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // ---- write obj to Writable + * + * options = { + * dest: fs.createWriteStream('result.json'), + * indent: 4 + * } + * + * writer.writeJs(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // ---- write obj to object + * + * options = { + * dest: {}, + * indent: 2 + * } + * + * writer.writeJs(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + */ + this.writeJs = function (object, options) { + return self.optionsHandler.ensureIndent(options) + .then(function (ensuredOptions) { + return new Promise(function (resolve, reject) { + // complain about missing destination + if (!ensuredOptions.dest) { + reject(new Error('Missing options.dest, pls specify existing destination resource!')); + } else { + var dest = ensuredOptions.dest; + var indent = ensuredOptions.indent; + if (typeof dest === 'string') { // file + serializeJsToString(object, indent, ensuredOptions.exports) + .then(function (data) { + return writeToFile(data, dest, Constants.JS, resolve, reject, ensuredOptions.force); + }) + .catch(function (err) { + reject(err); }); - }); - }; - - /** - * Writes a JS object to a JS destination. The object is prefixed by `module.exports = `. - * - * @param {object} object - The JSON to write into JS destination. - * @param {Options} options - The write destination and indention. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @public - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * var writer = new Writer(logger); - * - * // ---- write obj to file - * - * var obj = {...}; - * var options = { - * dest: 'result.js', - * indent: 2 - * } - * - * writer.writeJs(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to Writable - * - * options = { - * dest: fs.createWriteStream('result.json'), - * indent: 4 - * } - * - * writer.writeJs(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to object - * - * options = { - * dest: {}, - * indent: 2 - * } - * - * writer.writeJs(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - */ - this.writeJs = function (object, options) { - return self.optionsHandler.ensureIndent(options) - .then(function (ensuredOptions) { - return new Promise(function (resolve, reject) { - // complain about missing destination - if (!ensuredOptions.dest) { - reject(new Error('Missing options.dest, pls specify existing destination resource!')); - } else { - var dest = ensuredOptions.dest; - var indent = ensuredOptions.indent; - if (typeof dest === 'string') { // file - serializeJsToString(object, indent, ensuredOptions.exports) - .then(function (data) { - return writeToFile(data, dest, Constants.JS, resolve, reject, ensuredOptions.force); - }) - .catch(function (err) { - reject(err); - }); - } else if (isStream.writable(dest)) { // stream - serializeJsToString(object, indent, ensuredOptions.exports) - .then(function (data) { - return writeToStream(data, dest, Constants.JS, resolve, reject); - }) - .catch(function (err) { - reject(err); - }); - } else { // object - var msg; - if (ensuredOptions.exports && ensuredOptions.exports !== '') { - if (!self.validator.validateIdentifier(ensuredOptions.exports)) { - reject(new Error('Found invalid identifier for exports: ' + ensuredOptions.exports)); - } else { - ensuredOptions.dest[ensuredOptions.exports] = object; - msg = 'Writing JS to options.dest.' + ensuredOptions.exports + ' successful.'; - } - } else { - ensuredOptions.dest = object; - msg = 'Writing JS to options.dest successful.'; - } - resolve(msg); - } - } + } else if (isStream.writable(dest)) { // stream + serializeJsToString(object, indent, ensuredOptions.exports) + .then(function (data) { + return writeToStream(data, dest, Constants.JS, resolve, reject); + }) + .catch(function (err) { + reject(err); }); - }); - }; + } else { // object + var msg; + if (ensuredOptions.exports && ensuredOptions.exports !== '') { + if (!self.validator.validateIdentifier(ensuredOptions.exports)) { + reject(new Error('Found invalid identifier for exports: ' + ensuredOptions.exports)); + } else { + ensuredOptions.dest[ensuredOptions.exports] = object; + msg = 'Writing JS to options.dest.' + ensuredOptions.exports + ' successful.'; + } + } else { + ensuredOptions.dest = object; + msg = 'Writing JS to options.dest successful.'; + } + resolve(msg); + } + } + }); + }); + }; } Writer.prototype.constructor = Writer; diff --git a/package.json b/package.json index a87b81d..205f307 100644 --- a/package.json +++ b/package.json @@ -1,100 +1,111 @@ { - "name": "jy-transform", - "description": "This project aims to read, write and transform YAML, JS or JSON objects into each other using CLI or API, while the source and destination resources can be files on CLI and additionally, objects or streams on API level.", - "version": "2.0.1", - "homepage": "https://github.com/deadratfink/jy-transform", - "author": { - "name": "Jens Krefeldt", - "email": "j.krefeldt@gmail.com", - "url": "https://github.com/deadratfink" - }, - "contributors": [ - ], - "license": "SEE LICENSE IN [LICENSE.md](https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md)", - "repository": { - "type": "git", - "url": "https://github.com/deadratfink/jy-transform.git" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/" - }, - "bugs": "https://github.com/deadratfink/jy-transform/issues", - "private": false, - "config": { - "test": { - "mocha": { - "unit": { - "reporter": "spec" - } - } - } - }, - "scripts": { - "docs": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", - "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", - "pretest": "rm -Rf test/tmp", - "test": "istanbul cover _mocha --report lcovonly -- -R $npm_package_config_test_mocha_unit_reporter ./test/test*.js" - }, - "engines": { - "node": ">=0.10.0" - }, - "dependencies": { - "bluebird": "^3.4.6", - "cli": "^1.0.1", - "is-stream": "^1.1.0", - "js-yaml": "^3.6.1", - "json-stringify-safe": "^5.0.1", - "mkdirp-then": "^1.2.0", - "serialize-js": "^1.1.0" - }, - "devDependencies": { - "codeclimate-test-reporter": "^0.4.0", - "codecov": "^1.0.1", - "coveralls": "^2.11.14", - "doctoc": "^1.0.0", - "fs-extra": "^1.0.0", - "istanbul": "^0.4.5", - "jsdoc-parse": "^2.0.5", - "jsdoc-to-markdown": "^2.0.1", - "mocha": "^3.1.2", - "mocha-lcov-reporter": "^1.2.0", - "object-path": "^0.11.2", - "package-json-to-readme": "^1.5.1", - "winston": "^2.3.0" - }, - "preferGlobal": true, - "bin": { - "jyt": "./jyt" - }, - "main": "./index.js", - "keywords": [ - "api", - "cli", - "jy", - "jyt", - "jy-transform", - "transform", - "convert", - "javascript", - "js", - "json", - "yaml", - "yaml2js", - "yaml-2-js", - "yaml2json", - "yaml-2-json", - "js2yaml", - "js-2-yaml", - "json2yaml", - "json-2-yaml", - "yamltojs", - "yaml-to-js", - "yamltojson", - "yaml-to-json", - "jstoyaml", - "js-to-yaml", - "jsontoyaml", - "json-to-yaml", - "promise" - ] + "name": "jy-transform", + "description": "This project aims to read, write and transform YAML, JS or JSON objects into each other using CLI or API, while the source and destination resources can be files on CLI and additionally, objects or streams on API level.", + "version": "3.0.0", + "homepage": "https://github.com/deadratfink/jy-transform", + "author": { + "name": "Jens Krefeldt", + "email": "j.krefeldt@gmail.com", + "url": "https://github.com/deadratfink" + }, + "contributors": [], + "license": "SEE LICENSE IN [LICENSE.md](https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md)", + "repository": { + "type": "git", + "url": "https://github.com/deadratfink/jy-transform.git" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "bugs": "https://github.com/deadratfink/jy-transform/issues", + "private": false, + "scripts": { + "build": "babel src -d lib", + "docs": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", + "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", + "pretestBAK": "rm -Rf test/tmp", + "test": "jest --no-cache --coverage --runInBand --config=./.jestrc.js", + "eslint": "eslint ." + }, + "engines": { + "node": ">=4.0.0" + }, + "dependencies": { + "bluebird": "^3.4.6", + "cli": "^1.0.1", + "is-stream": "^1.1.0", + "js-yaml": "^3.6.1", + "json-stringify-safe": "^5.0.1", + "mkdirp-then": "^1.2.0", + "promisify-es6": "~1.0.2", + "serialize-js": "^1.1.0" + }, + "devDependencies": { + "babel-cli": "~6.24.1", + "babel-core": "~6.24.1", + "babel-eslint": "~7.2.3", + "babel-plugin-transform-builtin-extend": "~1.1.2", + "babel-preset-env": "~1.4.0", + "babel-preset-stage-0": "~6.24.1", + "babel-watch": "~2.0.6", + "chalk": "~1.1.3", + "codeclimate-test-reporter": "^0.4.0", + "codecov": "^1.0.1", + "combarnea-winston-console-formatter": "~1.0.1", + "coveralls": "^2.11.14", + "doctoc": "^1.0.0", + "eslint": "~3.19.0", + "eslint-config-airbnb-base": "~10.0.1", + "eslint-plugin-filenames": "~1.2.0", + "eslint-plugin-import": "~2.2.0", + "eslint-plugin-jest": "~20.0.3", + "eslint-plugin-jest-async": "~1.0.3", + "eslint-plugin-jsdoc": "~2.3.1", + "fs-extra": "^1.0.0", + "istanbul": "^0.4.5", + "jest": "~20.0.4", + "jsdoc-parse": "^2.0.5", + "jsdoc-to-markdown": "^2.0.1", + "mocha": "^3.1.2", + "mocha-lcov-reporter": "^1.2.0", + "object-path": "^0.11.2", + "package-json-to-readme": "^1.5.1", + "winston": "^2.3.0", + "winston-console-formatter": "~0.3.1" + }, + "preferGlobal": true, + "bin": { + "jyt": "./jyt" + }, + "main": "./index.js", + "keywords": [ + "api", + "cli", + "jy", + "jyt", + "jy-transform", + "transform", + "convert", + "javascript", + "js", + "json", + "yaml", + "yaml2js", + "yaml-2-js", + "yaml2json", + "yaml-2-json", + "js2yaml", + "js-2-yaml", + "json2yaml", + "json-2-yaml", + "yamltojs", + "yaml-to-js", + "yamltojson", + "yaml-to-json", + "jstoyaml", + "js-to-yaml", + "jsontoyaml", + "json-to-yaml", + "promise" + ] } diff --git a/test/helper-constants.js b/test/helper-constants.js new file mode 100644 index 0000000..ed0c053 --- /dev/null +++ b/test/helper-constants.js @@ -0,0 +1,36 @@ +// eslint-disable-next-line +import chalk from 'chalk'; +import Package from '../package.json'; + +/** + * @module helper-constants + * @description The test suite constants definitions. + * @public + * @type {Object} + */ + +/** + * The unit test suite description for the plugin. + * @type {string} + * @constant + * @public + */ +export const TEST_SUITE_DESCRIPTION_UNIT = chalk.inverse( + 'The ' + chalk.bold(Package.name) + chalk.blue(' UNIT ') + 'Test Suite' +); + +/** + * The unit test suite description for the plugin. + * @type {string} + * @constant + * @public + */ +export const TEST_SUITE_DESCRIPTION_FUNCTIONAL = chalk.inverse( + 'The ' + chalk.bold(Package.name) + chalk.blue(' FUNCTIONAL ') + 'Test Suite' +); + + +export default { + TEST_SUITE_DESCRIPTION_UNIT, + TEST_SUITE_DESCRIPTION_FUNCTIONAL, +}; diff --git a/test/logger.js b/test/logger.js index fe24881..1f1c510 100644 --- a/test/logger.js +++ b/test/logger.js @@ -1,27 +1,45 @@ -'use strict'; +import winston from 'winston'; +import fsExtra from 'fs-extra'; +// import promisify from 'promisify-es6'; +// // import toYAML from 'combarnea-winston-console-formatter'; +// +// const ensureDir = promisify(fsExtra.ensureDir); +// const emptyDir = promisify(fsExtra.emptyDir); + +// //////////////////////////////////////////////////////////////////////////// +// PRIVATES +// //////////////////////////////////////////////////////////////////////////// + +/** + * An indent of 0 SPACEs. + * + * @type {string} + * @constant + * @private + */ +const INDENT = ''; -var assert = require('assert'); -var winston = require('winston'); -var fs = require('fs-extra'); /** - * An indent of 8 SPACEs. + * A temporary test directory. * * @type {string} + * @constant + * @private */ -var INDENT = ' '; -var TEST_TMP_DIR = './test/tmp'; +const TEST_TMP_DIR = './test/tmp'; /** * This function formats the log string by given options to log. * * @param {{timestamp: function, level: string, [message: string], [meta: object]}} options - The formatter options. - * @returns {string} - The log string. + * @returns {string} The log string. * @private */ -var formatter = function(options) { - // Return string will be passed to logger. - return options.timestamp() +' '+ options.level.toUpperCase() +' '+ (undefined !== options.message ? options.message : '') + - (options.meta && Object.keys(options.meta).length ? '\n\t'+ JSON.stringify(options.meta) : '' ); +const formatter = (options) => { + // Return string will be passed to logger. + return (options.timestamp() !== '' ? '[' + options.timestamp() + '] ' : '') + '[' + options.level.toUpperCase() + '] ' + + (undefined !== options.message ? options.message : '') + + (options.meta && Object.keys(options.meta).length ? '\n\t' + JSON.stringify(options.meta) : ''); }; /** @@ -30,58 +48,78 @@ var formatter = function(options) { * @type {{filename: string, timestamp: winstonFileOptions.timestamp, formatter: formatter, level: string}} * @private */ -var winstonFileOptions = { - filename: TEST_TMP_DIR + '/test.log', - /** - * Formats the timestamp as {@link Date} ISO string prefixed by an indent. - * - * @see #INDENT - * @returns {string} - The {@link Date} ISO string. - */ - timestamp: function() { - return new Date().toISOString(); - }, - json: false, - formatter: formatter, - level: 'debug' +const winstonFileOptions = { + filename: TEST_TMP_DIR + '/test.log', + /** + * Formats the timestamp as {@link Date} ISO string prefixed by an indent. + * + * @see #INDENT + * @returns {string} - The {@link Date} ISO string. + */ + timestamp: () => { + return new Date().toISOString(); + }, + json: false, + formatter, + level: 'info' }; -fs.ensureDirSync(TEST_TMP_DIR); -fs.emptyDirSync(TEST_TMP_DIR); - /** * Options for winston console logging. * * @type {{timestamp: winstonConsoleOptions.timestamp, formatter: formatter, level: string}} * @private */ -var winstonConsoleOptions = { - /** - * Overwrites the timestamp by indent. - * - * @see #INDENT - * @returns {string} - The indent only. - */ - timestamp: function() { - return INDENT; - }, - formatter: formatter, - level: 'info' +const winstonConsoleOptions = { + /** + * Overwrites the timestamp by indent. + * + * @see #INDENT + * @returns {string} - The indent only. + */ + timestamp: () => { + return INDENT; + }, + formatter, + level: 'info' }; +// //////////////////////////////////////////////////////////////////////////// +// PREPARE TMP FOLDER +// //////////////////////////////////////////////////////////////////////////// + +// (async () => { +// await ensureDir(TEST_TMP_DIR); +// await emptyDir(TEST_TMP_DIR); +// })(); +// fsExtra.ensureDirSync(TEST_TMP_DIR); +// fsExtra.emptyDirSync(TEST_TMP_DIR); + +// //////////////////////////////////////////////////////////////////////////// +// PROTECTED EXPORTS +// //////////////////////////////////////////////////////////////////////////// + /** * The winston logger. * - * @public + * @protected */ -var logger = new (winston.Logger)({ - transports: [ - new (winston.transports.File)(winstonFileOptions), - new (winston.transports.Console)(winstonConsoleOptions) - ], - exitOnError: false +export const logger = new (winston.Logger)({ + transports: [ + new (winston.transports.File)(winstonFileOptions), + new (winston.transports.Console)(winstonConsoleOptions) + ], + exitOnError: false }); +// logger.add(winston.transports.Console, toYAML.config({ +// // colors, // colorizer colors array +// noMeta: false, // boolean - not to print metadata, if true metadata will not be printed +// noStack: false // boolean - not to print stack trace, if true stack trace will not be printed +// })); + logger.info('Test-logger initialized, writing to ', winstonFileOptions.filename); -module.exports = logger; +export default { + logger, +}; diff --git a/test/test-log-wrapper.js b/test/test-log-wrapper.js deleted file mode 100644 index 0d91add..0000000 --- a/test/test-log-wrapper.js +++ /dev/null @@ -1,213 +0,0 @@ -'use strict'; - -var LogWrapper = require('../lib/log-wrapper'); -var assert = require('assert'); - -/** - * @classdesc This unit test suite checks the validity and correctness of {@link LogWrapper} class. - */ -describe('Executing \'jy-transform\' project log wrapper test suite.', function () { - - var infoMsg; - var debugMsg; - var traceMsg; - var errorMsg; - var verboseResultArray = []; - var logWrapper; - - var INFO = 'INFO'; - var DEBUG = 'DEBUG'; - var TRACE = 'TRACE'; - var ERROR = 'ERROR'; - - /** - * A mock logger. - * - * @type {{info: mockLogger.info, debug: mockLogger.debug, error: mockLogger.error}} - * @private - */ - var mockLogger = { - info: function (msg) { - infoMsg = msg; - }, - debug: function (msg) { - debugMsg = msg; - }, - trace: function (msg) { - traceMsg = msg; - }, - error: function (msg) { - errorMsg = msg; - } - }; - - var mockLoggerWithoutDebugFunction = { - info: function (msg) { - infoMsg = msg; - }, - trace: function (msg) { - traceMsg = msg; - }, - error: function (msg) { - errorMsg = msg; - } - }; - - var mockLoggerWithoutTraceFunction = { - info: function (msg) { - infoMsg = msg; - }, - debug: function (msg) { - debugMsg = msg; - }, - error: function (msg) { - errorMsg = msg; - } - }; - - var mockLoggerWithVerboseFunction = { - info: function (msg) { - verboseResultArray.push(msg); - } - }; - - describe('Testing LogWrapper with mockLogger', function () { - - /** - * Resets the mock logger message targets. - */ - beforeEach(function () { - infoMsg = undefined; - debugMsg = undefined; - traceMsg = undefined; - errorMsg = undefined; - logWrapper = new LogWrapper(mockLogger); - }); - - var expected = INFO; - it('should log with ' + expected, function (done) { - logWrapper.info(expected); - assert.equal(infoMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - expected = DEBUG; - it('should log with ' + expected, function (done) { - logWrapper.debug(expected); - assert.equal(debugMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - expected = TRACE; - it('should log with ' + expected, function (done) { - logWrapper.trace(expected); - assert.equal(traceMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - expected = ERROR; - it('should log with ' + expected, function (done) { - logWrapper.error(expected); - assert.equal(errorMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - var verboseExpected = { - origin: 'origin', - target: 'target', - src: 'src', - dest: 'dest', - indent: 'indent' - }; - - it('should log options', function (done) { - logWrapper = new LogWrapper(mockLoggerWithVerboseFunction); - logWrapper.verboseOptions(verboseExpected) - .then(function (options) { - assert.equal(options, verboseExpected, 'passed options: ' + JSON.stringify(options, null, 4) + ' should equal logged options: ' + JSON.stringify(verboseExpected, null, 4)); - assert(verboseResultArray.indexOf('origin: ' + verboseExpected.origin) > -1, 'logger verboseResultArray should contain value ' + 'origin: ' + verboseExpected.origin); - assert(verboseResultArray.indexOf('target: ' + verboseExpected.target) > -1, 'logger verboseResultArray should contain value ' + 'target: ' + verboseExpected.target); - assert(verboseResultArray.indexOf('src: ' + verboseExpected.src) > -1, 'logger verboseResultArray should contain value ' + 'src: ' + verboseExpected.src); - assert(verboseResultArray.indexOf('dest: ' + verboseExpected.dest) > -1, 'logger verboseResultArray should contain value ' + 'dest: ' + verboseExpected.dest); - assert(verboseResultArray.indexOf('indent: ' + verboseExpected.indent) > -1, 'logger verboseResultArray should contain value ' + 'indent: ' + verboseExpected.indent); - done(); - }) - .catch(function (err) { - done(err); - }); - }); - - }); - - describe('Testing LogWrapper with mockLoggerWithoutDebugFunction', function () { - - /** - * Resets the mock logger message targets. - */ - beforeEach(function () { - infoMsg = undefined; - debugMsg = undefined; - traceMsg = undefined; - errorMsg = undefined; - logWrapper = new LogWrapper(mockLoggerWithoutDebugFunction); - }); - - var expected = INFO; - it('should log with ' + expected, function (done) { - logWrapper.info(expected); - assert.equal(infoMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - expected = DEBUG; - it('should log with ' + expected, function (done) { - logWrapper.debug(expected); - assert.equal(infoMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - expected = ERROR; - it('should log with ' + expected, function (done) { - logWrapper.error(expected); - assert.equal(errorMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - }); - - describe('Testing LogWrapper with mockLoggerWithoutTraceFunction', function () { - - /** - * Resets the mock logger message targets. - */ - beforeEach(function () { - infoMsg = undefined; - debugMsg = undefined; - errorMsg = undefined; - logWrapper = new LogWrapper(mockLoggerWithoutTraceFunction); - }); - - var expected = INFO; - it('should log with ' + expected, function (done) { - logWrapper.info(expected); - assert.equal(infoMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - expected = TRACE; - it('should log with ' + expected, function (done) { - logWrapper.trace(expected); - assert.equal(debugMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - expected = ERROR; - it('should log with ' + expected, function (done) { - logWrapper.error(expected); - assert.equal(errorMsg, expected, 'logger message should contain value ' + expected); - done(); - }); - - }); - -}); diff --git a/test/test-middleware.js b/test/test-middleware.js deleted file mode 100644 index ee4c67b..0000000 --- a/test/test-middleware.js +++ /dev/null @@ -1,153 +0,0 @@ -'use strict'; - -var Transformer = require('../index'); -var Middleware = require('../index').middleware; -var identityMiddleware = Middleware.identityMiddleware; -var transformer; -var Promise = require('bluebird'); -var logger; -var assert = require('assert'); -var objectPath = require('object-path'); - -/** - * @classdesc This unit test suite checks the validity and correctness of {@link Middleware} class. - */ -describe('Executing \'jy-transform\' project Middleware test suite.', function () { - - /** - * Middleware function for altering JSON. - * - * @param {object} json - The JSON object o alter. - * @private - */ - function middleware(json) { - - function key1(json) { - objectPath.set(json, 'key1', 'value1'); - logger.info('key1 json: ' + JSON.stringify(json)); - return Promise.resolve(json); - } - - function key2(json) { - objectPath.set(json, 'key2', 'value2'); - logger.info('key2 json: ' + JSON.stringify(json)); - return Promise.resolve(json); - } - - function key3(json) { - objectPath.set(json, 'key3', 'value3'); - logger.info('key3 json: ' + JSON.stringify(json)); - return Promise.resolve(json); - } - - return Promise.all([key1(json), key2(json), key3(json)]) - .then(function(result) { - assert.equal(result.length, 3); - logger.info('all the elements were created'); - logger.info('result: ' + JSON.stringify(result[result.length - 1])); - return Promise.resolve(result[result.length - 1]); - }); - } - - - /** - * Helper function to assert the identity Promise. - * - * @param {function} func - The identity Promise function. - * @param {function} done - Test finish callback. - * @private - */ - function assertIdentityPromise(func, done) { - var json = {test: 'value'}; - func(json) - .then(function (jsonResult) { - assert.deepEqual(jsonResult, json, 'JSON passed in should equals JSON put put out from identity Promise'); - done(); - }) - .catch(function(err) { - done(err); - }); - } - - /** - * Init the test logger. - */ - before(function () { - logger = require('./logger.js'); - transformer = new Transformer(logger); - }); - - describe('Testing Transformer middleware', function () { - - it('should alter json', function (done) { - var options = { - src: {}, - dest: {} - }; - transformer.transform(options, middleware) - .then(function (msg){ - logger.info(msg); - logger.info('options.dest: ' + JSON.stringify(options.dest, null, 4)); - assert.equal(options.dest.key1, 'value1', 'options.dest.key1 should have value: value1, but was ' + options.dest.key1); - assert.equal(options.dest.key2, 'value2', 'options.dest.key1 should have value: value2, but was ' + options.dest.key2); - assert.equal(options.dest.key3, 'value3', 'options.dest.key1 should have value: value3, but was ' + options.dest.key3); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - }); - - describe('Testing middleware.identityMiddleware()', function () { - - it('should provide passed function', function (done) { - var func = identityMiddleware; - assert(typeof func === 'function'); - assert(func === identityMiddleware, 'func should be same function identityMiddleware'); - - var json = {}; - identityMiddleware(json) - .then(function (jsonIdentity) { - assert(jsonIdentity === json, 'should return same json'); - done(); - }); - }); - - }); - - describe('Testing middleware.ensureMiddleware()', function () { - - it('should provide passed function', function (done) { - var func = Middleware.ensureMiddleware(identityMiddleware); - assert(typeof func === 'function'); - assert(func === identityMiddleware, 'should return passed function (identityMiddleware)'); - done(); - }); - - it('should reject Promise if middleware passed is not a function type', function (done) { - Middleware.ensureMiddleware({}) - .catch(function (err) { - assert.notEqual(err, null, 'Promise rejection err should not be null'); - assert(err instanceof TypeError); - done(); - }); - }); - - it('should provide identity Promise if middleware passed is null', function (done) { - var func = Middleware.ensureMiddleware(); - assert(typeof func === 'function'); - var json = {test: 'value'}; - assertIdentityPromise(func, done); - }); - - it('should provide identity Promise if middleware passed is undefined', function (done) { - var func = Middleware.ensureMiddleware(undefined); - assert(typeof func === 'function'); - assertIdentityPromise(func, done); - }); - - }); -}); diff --git a/test/test-options-handler.js b/test/test-options-handler.js deleted file mode 100644 index 5aa67bf..0000000 --- a/test/test-options-handler.js +++ /dev/null @@ -1,552 +0,0 @@ -'use strict'; - -var Constants = require('../lib/constants'); -var assert = require('assert'); -//var YAMLException = require('js-yaml/lib/js-yaml/exception'); -var fs = require('fs'); -var path = require('path'); -var OptionsHandler = require('../lib/options-handler'); -var optionsHandler; -var logger; - -/** - * @classdesc This unit test suite checks the validity and correctness of {@link OptionsHandler} class. - */ -describe('Executing \'jy-transform\' project OptionsHandler test suite.', function () { - - /** - * Init the test logger and Writer. - */ - before(function () { - logger = require('./logger.js'); - optionsHandler = new OptionsHandler(logger); - }); - - /** - * Assert an `Error` for a given options function. - * - * @param {object} options - The options which potentially produce the error. - * @param {function} optionsFunc - The function to call for assertion. - * @param {function} done - Test finish callback. - * @param {Error} [errorType=Error] - The error type to assert. - */ - function assertOptionsError(options, optionsFunc, done, errorType) { - optionsFunc(options) - .then(function (resultOptions) { - done(new Error('Error expected when calling options = ' + JSON.stringify(options, null, 4))); - }) - .catch(function (err) { - logger.info('Error is EXPECTED: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - var type = errorType; - if (!type) { - type = Error; - } - logger.debug('ERROR type = ' + (typeof err)); - assert(err instanceof Error); - assert.equal(err.name, type.name, err.name + ' should equal ' + type.name); - done(); - }); - } - - describe('Testing OptionsHandler.validateTransformation(...)', function () { - - it('should reject when options is missing', function (done) { - assertOptionsError(null, optionsHandler.validateTransformation, done); - }); - - it('should reject when options.origin is missing', function (done) { - var options = { - target: Constants.YAML - }; - assertOptionsError(options, optionsHandler.validateTransformation, done); - }); - - it('should reject when options.target is missing', function (done) { - var options = { - origin: Constants.YAML - }; - assertOptionsError(null, optionsHandler.validateTransformation, done); - }); - - it('should resolve transformation correctly from valid origin and target', function (done) { - var options = { - origin: Constants.YAML, - target: Constants.JS - }; - optionsHandler.validateTransformation(options) - .then(function (results) { - assert.equal(results.length, 2, 'result should have length 2'); - assert(results[0] === options); - assert(results[1] === (Constants.YAML + '2' + Constants.JS)); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should reject with Error due to invalid target', function (done) { - var invalidOptions = { - origin: Constants.YAML, - target: 'INVALID_TARGET' - }; - assertOptionsError(invalidOptions, optionsHandler.validateTransformation, done); - }); - - }); - - describe('Testing OptionsHandler.completeOptions(...)', function () { - - it('should reject when options is missing', function (done) { - assertOptionsError(null, optionsHandler.completeOptions, done); - }); - - it('should resolve options.src/origin and options.dest/target with default values (' + Constants.DEFAULT_ORIGIN + '/' + Constants.DEFAULT_TARGET + ')', function (done) { - var PATH_WITH_INVALID_EXT = 'PATH_WITH_INVALID.EXT'; - var options = { - src: PATH_WITH_INVALID_EXT, - dest: PATH_WITH_INVALID_EXT - }; - optionsHandler.completeOptions(options) - .then(function (resultOptions) { - assert.equal(resultOptions.origin, Constants.DEFAULT_ORIGIN, 'options.origin should have value ' + Constants.DEFAULT_ORIGIN); - assert.equal(resultOptions.target, Constants.DEFAULT_TARGET, 'options.target should have value ' + Constants.DEFAULT_TARGET); - assert.equal(resultOptions.dest, PATH_WITH_INVALID_EXT, 'options.dest should have value ' + PATH_WITH_INVALID_EXT); - assert.equal(resultOptions.indent, Constants.DEFAULT_INDENT, 'options.indent should have value ' + Constants.DEFAULT_INDENT); - assert.equal(resultOptions.force, Constants.DEFAULT_FORCE_FILE_OVERWRITE, 'options.indent should have value ' + Constants.DEFAULT_FORCE_FILE_OVERWRITE); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should resolve options.force should result to ' + !Constants.DEFAULT_FORCE_FILE_OVERWRITE, function (done) { - var PATH_WITH_INVALID_EXT = 'PATH_WITH_INVALID.EXT'; - var force = !Constants.DEFAULT_FORCE_FILE_OVERWRITE; - var options = { - src: PATH_WITH_INVALID_EXT, - dest: PATH_WITH_INVALID_EXT, - force: force - }; - optionsHandler.completeOptions(options) - .then(function (resultOptions) { - assert.equal(resultOptions.origin, Constants.DEFAULT_ORIGIN, 'options.origin should have value ' + Constants.DEFAULT_ORIGIN); - assert.equal(resultOptions.target, Constants.DEFAULT_TARGET, 'options.target should have value ' + Constants.DEFAULT_TARGET); - assert.equal(resultOptions.dest, PATH_WITH_INVALID_EXT, 'options.dest should have value ' + PATH_WITH_INVALID_EXT); - assert.equal(resultOptions.indent, Constants.DEFAULT_INDENT, 'options.indent should have value ' + Constants.DEFAULT_INDENT); - assert.equal(resultOptions.force, force, 'options.force should have value ' + force); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - }); - - describe('Testing OptionsHandler.ensureIndent(...)', function () { - - it('should reject when options is missing', function (done) { - assertOptionsError(null, optionsHandler.ensureIndent, done); - }); - - it('should set default indent if indent is missing', function (done) { - var options = {}; - optionsHandler.ensureIndent(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.indent, null, 'options should contain indent but is missing'); - assert.equal(resultOptions.indent, Constants.DEFAULT_INDENT, 'result indent should have length ' + Constants.DEFAULT_INDENT); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should set default indent if indent < minimum indent', function (done) { - var options = { - indent: (Constants.MIN_INDENT - 1), - target: Constants.YAML - }; - optionsHandler.ensureIndent(options) - .then(function (resultOptions) { - assert.equal(resultOptions.target, Constants.YAML, 'result target should have length ' + Constants.YAML); - assert.notEqual(resultOptions.indent, null, 'options should contain indent but is missing'); - assert.equal(resultOptions.indent, Constants.DEFAULT_INDENT, 'result indent should have length ' + Constants.DEFAULT_INDENT); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should set default indent if indent < JS/JSON minimum indent', function (done) { - var options = { - indent: Constants.MIN_INDENT - 1 - }; - optionsHandler.ensureIndent(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.indent, null, 'options should contain indent but is missing'); - assert.equal(resultOptions.indent, Constants.DEFAULT_INDENT, 'result indent should have length ' + Constants.DEFAULT_INDENT); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should set default indent if indent > than maximum indent', function (done) { - var options = { - indent: Constants.MAX_INDENT + 1 - }; - optionsHandler.ensureIndent(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.indent, null, 'options should contain indent but is missing'); - assert.equal(resultOptions.indent, Constants.DEFAULT_INDENT, 'result indent should have length ' + Constants.DEFAULT_INDENT); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - }); - - describe('Testing OptionsHandler.assertOrigin(...)', function () { - - it('should reject when options is missing', function (done) { - assertOptionsError(null, optionsHandler.assertOrigin, done); - }); - - it('should resolve options.origin for valid type YAML', function (done) { - var options = { - origin: Constants.YAML - }; - optionsHandler.assertOrigin(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.origin, null, 'options should contain origin but is missing'); - assert.equal(resultOptions.origin, Constants.YAML, 'result origin should have type ' + Constants.YAML); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should resolve options.origin for valid type JS', function (done) { - var options = { - origin: Constants.JS - }; - optionsHandler.assertOrigin(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.origin, null, 'options should contain origin but is missing'); - assert.equal(resultOptions.origin, Constants.JS, 'result origin should have type ' + Constants.JS); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should resolve options.origin for valid type JSON', function (done) { - var options = { - origin: Constants.JSON - }; - optionsHandler.assertOrigin(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.origin, null, 'options should contain origin but is missing'); - assert.equal(resultOptions.origin, Constants.JSON, 'result origin should have type ' + Constants.JSON); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should reject when options.origin is invalid type', function (done) { - var options = { - origin: 'INVALID_TYPE' - }; - assertOptionsError(options, optionsHandler.assertOrigin, done); - }); - - }); - - describe('Testing OptionsHandler.assertTarget(...)', function () { - - it('should reject when options is missing', function (done) { - assertOptionsError(null, optionsHandler.assertTarget, done); - }); - - it('should resolve options.target for valid type YAML', function (done) { - var options = { - target: Constants.YAML - }; - optionsHandler.assertTarget(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.target, null, 'options should contain target but is missing'); - assert.equal(resultOptions.target, Constants.YAML, 'result target should have type ' + Constants.YAML); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should resolve options.target for valid type JS', function (done) { - var options = { - target: Constants.JS - }; - optionsHandler.assertTarget(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.target, null, 'options should contain target but is missing'); - assert.equal(resultOptions.target, Constants.JS, 'result target should have type ' + Constants.JS); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should resolve options.target for valid type JSON', function (done) { - var options = { - target: Constants.JSON - }; - optionsHandler.assertTarget(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.target, null, 'options should contain target but is missing'); - assert.equal(resultOptions.target, Constants.JSON, 'result target should have type ' + Constants.JSON); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should reject when options.target is invalid type', function (done) { - var options = { - origin: 'INVALID_TYPE' - }; - assertOptionsError(options, optionsHandler.assertTarget, done); - }); - - }); - - describe('Testing OptionsHandler.ensureDest(...)', function () { - - it('should reject when options is missing', function (done) { - assertOptionsError(null, optionsHandler.ensureDest, done); - }); - - it('should resolve options.dest with value \'' + Constants.DEFAULT_OPTIONS.dest + '\' to relative file path to ' + Constants.YAML + ' file', function (done) { - var fileBaseName = 'test'; - var options = { - src: fileBaseName + '.' + Constants.JS, - dest: Constants.DEFAULT_OPTIONS.dest, - target: Constants.YAML - }; - optionsHandler.ensureDest(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.dest, null, 'options should contain dest but is missing'); - assert.equal(resultOptions.dest, fileBaseName + '.' + Constants.YAML, 'result options.dest should have type ' + fileBaseName + '.' + Constants.YAML); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should resolve options.dest with value \'' + Constants.DEFAULT_OPTIONS.dest + '\' to relative file path to ' + Constants.JS + ' file', function (done) { - var fileBaseName = 'test'; - var options = { - src: fileBaseName + '.' + Constants.YAML, - dest: Constants.DEFAULT_OPTIONS.dest, - target: Constants.JS - }; - optionsHandler.ensureDest(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.dest, null, 'options should contain dest but is missing'); - assert.equal(resultOptions.dest, fileBaseName + '.' + Constants.JS, 'result options.dest should have type ' + fileBaseName + '.' + Constants.JS); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should resolve options.dest with value \'' + Constants.DEFAULT_OPTIONS.dest + '\' to relative file path to ' + Constants.JSON + ' file', function (done) { - var fileBaseName = 'test'; - var options = { - src: fileBaseName + '.' + Constants.YAML, - dest: Constants.DEFAULT_OPTIONS.dest, - target: Constants.JSON - }; - optionsHandler.ensureDest(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.dest, null, 'options should contain dest but is missing'); - assert.equal(resultOptions.dest, fileBaseName + '.' + Constants.JSON, 'result options.dest should have type ' + fileBaseName + '.' + Constants.JSON); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should reject options.dest when invalid target type is provided', function (done) { - var fileBaseName = 'test'; - var options = { - src: fileBaseName + '.' + Constants.YAML, - dest: Constants.DEFAULT_OPTIONS.dest, - target: 'INVALID_TARGET' - }; - assertOptionsError(options, optionsHandler.ensureDest, done); - }); - - it('should reject when Writable is given but not target', function (done) { - var options = { - dest: fs.createWriteStream('./test/tmp/myOutput.txt') - }; - assertOptionsError(options, optionsHandler.ensureDest, done); - }); - - it('should resolve original options.dest', function (done) { - var destObj = {}; - var options = { - dest: destObj - }; - optionsHandler.ensureDest(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.dest, null, 'options should contain dest but is missing'); - assert.equal(resultOptions.dest, destObj, 'result options.dest should have type ' + destObj); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should resolve with null value for options.dest', function (done) { - var destObj = {}; - var options = { - }; - optionsHandler.ensureDest(options) - .then(function (resultOptions) { - assert.equal(resultOptions.dest, null, 'options.dest should be null'); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - }); - - describe('Testing OptionsHandler.ensureSrc(...)', function () { - - it('should reject when options is missing', function (done) { - assertOptionsError(null, optionsHandler.ensureSrc, done, TypeError); // TODO heck if this really TypeError!?! - }); - - it('should reject when options.src is not given', function (done) { - var options = {}; - assertOptionsError(options, optionsHandler.ensureSrc, done); - }); - - it('should resolve original options.src', function (done) { - var existingFile = path.resolve('./test/data/test-file.yaml'); - var options = { - src: existingFile - }; - - optionsHandler.ensureSrc(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.src, null, 'options should contain src but is missing'); - assert.equal(resultOptions.src, existingFile, 'result options.src should have file ' + existingFile); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should reject when options.src has value of not existing file', function (done) { - var notExistingFile = 'NON_EXISTING_FILE'; - var options = { - src: notExistingFile - }; - assertOptionsError(options, optionsHandler.ensureSrc, done); - }); - - it('should reject when options.src is a directory', function (done) { - var dir = './test/data'; - var options = { - src: dir - }; - assertOptionsError(options, optionsHandler.ensureSrc, done); - }); - - it('should reject when Readable is given but not origin', function (done) { - var options = { - src: fs.createReadStream('./test/data/readable-test-dummy.txt') - }; - assertOptionsError(options, optionsHandler.ensureSrc, done); - }); - - it('should resolve original options.src Readable', function (done) { - var readable = fs.createReadStream('./test/data/readable-test-dummy.txt'); - var options = { - src: readable, - origin: Constants.JSON - }; - optionsHandler.ensureSrc(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.src, null, 'options should contain src but is missing'); - assert.equal(resultOptions.src, readable, 'result options.src should have type Readable'); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - it('should resolve original options.src object', function (done) { - var srcObj = {}; - var options = { - src: srcObj - }; - optionsHandler.ensureSrc(options) - .then(function (resultOptions) { - assert.notEqual(resultOptions.src, null, 'options should contain src but is missing'); - assert.equal(resultOptions.src, srcObj, 'result options.src should have type ' + srcObj); - done(); - }) - .catch(function (err) { - logger.error('UNEXPECTED ERROR: ' + err.stack); - done(err); - }); - }); - - }); -}); diff --git a/test/test-reader.js b/test/test-reader.js deleted file mode 100644 index da4fe0c..0000000 --- a/test/test-reader.js +++ /dev/null @@ -1,510 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var YAMLException = require('js-yaml/lib/js-yaml/exception'); -var fs = require('fs'); -var Reader = require('../index').Reader; -var Constants = require('../index').constants; -var logger; -var reader; - -/** - * @classdesc This unit test suite checks the validity and correctness of {@link Reader} class. - */ -describe('Executing \'jy-transform\' project Reader test suite.', function () { - - /** - * Init the test logger and Reader. - */ - before(function () { - logger = require('./logger.js'); - reader = new Reader(logger); - }); - - describe('Testing Reader.readJs(...)', function () { - - var exports = 'fooBar'; - var exportsNotExists = 'notFooBar'; - var invalidIdentifier = '#3/-'; - - it('should read JS from file', function (done) { - - var options = { - src: './test/data/test-data.js' - }; - - reader.readJs(options) - .then(function (json) { - assert.notEqual(json, null); - assert.equal(json.myproperty, 'old value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should read JS from file with options.imports == \'\'', function (done) { - - var options = { - src: './test/data/test-data.js', - imports: '' - }; - - reader.readJs(options) - .then(function (json) { - assert.notEqual(json, null); - assert.equal(json.myproperty, 'old value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - - it('should read JS from file with options.imports == \'' + exports + '\'', function (done) { - - var options = { - src: './test/data/test-imports.js', - imports: exports - }; - - reader.readJs(options) - .then(function (json) { - assert.notEqual(json, null, 'json should not be null, was: ' + JSON.stringify(json)); - assert(!json.hasOwnProperty(exports), 'json should not have \'' + exports + '\' property, was: ' + JSON.stringify(json)); - assert(!json.hasOwnProperty('bar'), 'json should not have \'bar\' property, was: ' + JSON.stringify(json[exports])); - assert(json.hasOwnProperty('foo'), 'json should have \'foo\' property, was: ' + JSON.stringify(json[exports])); - assert.equal(json.foo, 'bar'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should read JS from file with options.imports == \'' + exports + '\' and given origin for unsupported file extension', function (done) { - - var options = { - src: './test/data/test-imports.txt', - imports: exports, - origin: Constants.JS - }; - - reader.readJs(options) - .then(function (json) { - assert.notEqual(json, null, 'json should not be null, was: ' + JSON.stringify(json)); - assert(!json.hasOwnProperty(exports), 'json should not have \'' + exports + '\' property, was: ' + JSON.stringify(json)); - assert(!json.hasOwnProperty('bar'), 'json should not have \'bar\' property, was: ' + JSON.stringify(json[exports])); - assert(json.hasOwnProperty('foo'), 'json should have \'foo\' property, was: ' + JSON.stringify(json[exports])); - assert.equal(json.foo, 'bar'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should reject read JS from file with Error on invalid identifier for options.imports: ' + invalidIdentifier, function (done) { - - var options = { - src: './test/data/test-imports.js', - imports: invalidIdentifier - }; - - reader.readJs(options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should reject read JS from file with Error on non-existent identifier for options.imports: ' + exportsNotExists, function (done) { - - var options = { - src: './test/data/test-imports.js', - imports: exportsNotExists - }; - - reader.readJs(options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should read JSON from file', function (done) { - var options = { - src: './test/data/test-data.json' - }; - reader.readJs(options) - .then(function (json) { - assert.notEqual(json, null); - assert.equal(json.myproperty, 'old value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should read JS from object', function (done) { - var options = { - src: { - test: 'value' - } - }; - reader.readJs(options) - .then(function (json) { - assert.notEqual(json, null); - assert.equal(json.test, 'value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should read JS from object with options.imports == \'\'', function (done) { - - var options = { - src: { - foo: 'bar' - }, - imports: '' - }; - - reader.readJs(options) - .then(function (json) { - assert.notEqual(json, null); - assert.equal(json.foo, 'bar'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should read JS from object with options.imports == \'' + exports + '\'', function (done) { - - var options = { - src: { - fooBar: { - bar: 'foo', - foo: 'bar' - } - }, - imports: exports - }; - - reader.readJs(options) - .then(function (json) { - assert.notEqual(json, null, 'json should not be null, was: ' + JSON.stringify(json)); - assert(!json.hasOwnProperty(exports), 'json should not have \'' + exports + '\' property, was: ' + JSON.stringify(json)); - assert(json.hasOwnProperty('bar'), 'json should have \'bar\' property, was: ' + JSON.stringify(json[exports])); - assert(json.hasOwnProperty('foo'), 'json should have \'foo\' property, was: ' + JSON.stringify(json[exports])); - assert.equal(json.bar, 'foo'); - assert.equal(json.foo, 'bar'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should reject read JS from object with Error on invalid identifier for options.imports: ' + invalidIdentifier, function (done) { - - var options = { - src: { - fooBar: { - bar: 'foo', - foo: 'bar' - } - }, - imports: invalidIdentifier - }; - - reader.readJs(options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should reject read JS from file with Error on non-existent identifier for options.imports: ' + exportsNotExists, function (done) { - - var options = { - src: { - fooBar: { - bar: 'foo', - foo: 'bar' - } - }, - imports: exportsNotExists - }; - - reader.readJs(options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should read JSON from stream', function (done) { - var options = { - src: fs.createReadStream('./test/data/test-data.json') - }; - reader.readJs(options) - .then(function (json) { - assert.notEqual(json, null); - assert.equal(json.myproperty, 'old value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should read corrupted JSON from file path and fail by SyntaxError', function (done) { - var options = { - src: './test/data/test-data-corrupted.json' - }; - reader.readJs(options) - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('SyntaxError expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof SyntaxError, 'expected Error should equal SyntaxError, was: ' + (typeof err)); - done(); - }); - }); - - it('should read invalid JSON from file path and fail by SyntaxError', function (done) { - var options = { - src: './test/data/test-data-wrong-syntax.json' - }; - reader.readJs(options) - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('SyntaxError expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof SyntaxError, 'expected Error should equal SyntaxError, was: ' + (typeof err)); - done(); - }); - }); - - it('should read corrupted JSON from stream and fail by SyntaxError', function (done) { - var options = { - src: fs.createReadStream('./test/data/test-data-corrupted.json') - }; - reader.readJs(options) - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('SyntaxError expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof SyntaxError, 'expected Error message should equal SyntaxError, was: ' + (typeof err)); - done(); - }); - }); - - it('should read invalid JSON from stream and fail by SyntaxError', function (done) { - var options = { - src: fs.createReadStream('./test/data/test-data-wrong-syntax.json') - }; - reader.readJs(options) - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('SyntaxError expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof SyntaxError, 'expected Error should equal SyntaxError, was: ' + (typeof err)); - done(); - }); - }); - - it('should fail JS(ON) read by missing options', function (done) { - reader.readJs() - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should fail JS(ON) read by missing options.src', function (done) { - reader.readJs({}) - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - }); - - describe('Testing Reader.readYaml(...)', function () { - - it('should read YAML from file', function (done) { - var options = { - src: './test/data/test-data.yaml' - }; - reader.readYaml(options) - .then(function (json) { - assert.notEqual(json, null, 'resulting json should not be null'); - assert.equal(json.myproperty, 'old value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should read JS from object', function (done) { - var options = { - src: { - test: 'value' - } - }; - reader.readYaml(options) - .then(function (json) { - assert.notEqual(json, null, 'resulting json should not be null'); - assert.equal(json.test, 'value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should read YAML from stream', function (done) { - var options = { - src: fs.createReadStream('./test/data/test-data.yaml') - }; - reader.readYaml(options) - .then(function (json) { - assert.notEqual(json, null, 'resulting json should not be null'); - assert.equal(json.myproperty, 'old value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should read invalid YAML from file path and fail by YAMLException', function (done) { - var options = { - src: './test/data/test-data-wrong-syntax.yaml' - }; - reader.readYaml(options) - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('YAMLException expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof YAMLException, 'expected Error should equal YAMLException, was: ' + (typeof err)); - done(); - }); - }); - - it('should read invalid YAML from stream and fail by YAMLException', function (done) { - var options = { - src: fs.createReadStream('./test/data/test-data-wrong-syntax.yaml') - }; - reader.readYaml(options) - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('YAMLException expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof YAMLException, 'expected Error message should equal YAMLException, was: ' + (typeof err)); - done(); - }); - }); - - it('should fail YAML read by missing input options', function (done) { - reader.readYaml() - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should fail YAML read by missing options.src', function (done) { - reader.readYaml({}) - .then(function (json) { - assert.equal(json, null, 'json should be null due to expected exception, was: ' + JSON.stringify(json)); - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - }); -}); diff --git a/test/test-transformer.js b/test/test-transformer.js deleted file mode 100644 index 7a8342d..0000000 --- a/test/test-transformer.js +++ /dev/null @@ -1,332 +0,0 @@ -'use strict'; - -var Transformer = require('../index'); -var transformer; -var Constants = require('../lib/constants'); -var logger; -var jsYaml = require('js-yaml'); -var assert = require('assert'); -var Promise = require('bluebird'); -var fs = require('fs-extra'); -var fsPromised = Promise.promisifyAll(require('fs')); -var path = require('path'); - -/** - * @classdesc This unit test suite checks the correct trnasfomration behaviour of {@link Transformer} class. - */ -describe('Executing \'jy-transform\' project\'s Transformer test suite.', function () { - - var TEST_TMP_DIR = './test/tmp'; - var TEST_DATA_DIR = './test/data'; - var SRC = TEST_DATA_DIR + '/test-file.yaml'; - var EXPECTED_VALUE = 5000.00; - - /** - * Init the test logger. - */ - before(function () { - logger = require('./logger.js'); - transformer = new Transformer(logger); - }); - - /** - * Prepare test data. - */ - before(function () { - try { - fs.copySync(SRC, './test/tmp/test-data.yaml'); - logger.info('copied ' + SRC + ' to ' + TEST_TMP_DIR); - } catch (err) { - logger.error('could not copy ' + SRC + ' to ' + TEST_TMP_DIR + err.stack); - throw err; - } - }); - - /** - * Transformation middleware changing value for `total` property. - * - * @param {object} json - To transform. - */ - function middleware(json) { - json.total = EXPECTED_VALUE; - return Promise.resolve(json); - } - - /** - * Helper method which asserts the successful transformation. - * - * @param {object} options - The transformation options. - * @param {function} middleware - The transformation middleware. - * @param {function} done - Test finished callback; - */ - function assertTransformSuccess(options, middleware, done) { - return transformer.transform(options, middleware) - .then(function (msg) { - logger.info(msg); - var stats = fs.statSync(options.dest); - assert(stats.isFile()); - var json = require(path.resolve(options.dest)); - assert.equal(json.total, EXPECTED_VALUE, 'property \'total\' should have new value \'' + EXPECTED_VALUE + '\'.'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - } - - /** - * Helper method which asserts the successful transformation. - * - * @param {object} options - The transformation options. - * @param {function} middleware - The transformation middleware. - * @param {function} done - Test finished callback; - */ - function assertYamlTransformSuccess(options, middleware, done) { - return transformer.transform(options, middleware) - .then(function (msg) { - logger.info(msg); - var stats = fs.statSync(options.dest); - assert(stats.isFile()); - return fs.readFileAsync(options.dest, Constants.UTF8) - .then(function (yaml) { - try { - var json = jsYaml.safeLoad(yaml); - assert.equal(json.total, EXPECTED_VALUE, 'property \'total\' should have new value \'' + EXPECTED_VALUE + '\'.'); - done(); - } catch (err) { // probably a YAMLException - logger.error(err.stack); - return done(err); - } - }); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - } - - - describe('Testing Transformer transforming from YAML to JS to relative path', function () { - - var DEST = TEST_TMP_DIR + '/test-data.js'; - - it('should store ' + DEST + ' file relative to ./test/tmp/test-data.yaml', function (done) { - - var options = { - src: path.resolve('./test/tmp/test-data.yaml') - }; - - transformer.transform(options, middleware) - .then(function (msg) { - logger.info(msg); - var stats = fs.statSync(DEST); - assert(stats.isFile()); - var json = require('./tmp/test-data.js'); - assert.equal(json.total, EXPECTED_VALUE, 'property \'total\' should have new value \'' + EXPECTED_VALUE + '\'.'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - - }); - }); - - describe('Testing Transformer transforming from YAML to JS', function () { - - var SRC = './test/data/test-file.yaml'; - var DEST = TEST_TMP_DIR + '/test-data-transform-yaml-js.js'; - - it('should store ' + SRC + ' file to ' + DEST, function (done) { - - var options = { - src: path.resolve(SRC), - dest: path.resolve(DEST) - }; - - assertTransformSuccess(options, middleware, done); - - }); - }); - - describe('Testing Transformer transforming from YAML to JSON', function () { - - var SRC = './test/data/test-file.yaml'; - var DEST = TEST_TMP_DIR + '/test-data-transform-yaml-json.json'; - - it('should store ' + SRC + ' file to ' + DEST, function (done) { - - var options = { - src: path.resolve(SRC), - dest: path.resolve(DEST) - }; - - assertTransformSuccess(options, middleware, done); - - }); - }); - - describe('Testing Transformer transforming from JSON to JS', function () { - - var SRC = './test/data/test-file.json'; - var DEST = TEST_TMP_DIR + '/test-data-transform-json-js.js'; - - it('should store ' + SRC + ' file to ' + DEST, function (done) { - - var options = { - src: path.resolve(SRC), - dest: path.resolve(DEST) - }; - - assertTransformSuccess(options, middleware, done); - - }); - }); - - describe('Testing Transformer transforming from JS to JSON', function () { - - var SRC = './test/data/test-file.js'; - var DEST = TEST_TMP_DIR + '/test-data-transform-js-json.json'; - - it('should store ' + SRC + ' file to ' + DEST, function (done) { - - var options = { - src: path.resolve(SRC), - dest: path.resolve(DEST) - }; - - assertTransformSuccess(options, middleware, done); - - }); - }); - - describe('Testing Transformer transforming from JS to YAML', function () { - - var SRC = './test/data/test-file.js'; - var DEST = TEST_TMP_DIR + '/test-data-transform-js-yaml.yaml'; - - it('should store ' + SRC + ' file to ' + DEST, function (done) { - - var options = { - src: path.resolve(SRC), - dest: path.resolve(DEST) - }; - - transformer.transform(options, middleware) - .then(function (msg) { - logger.info(msg); - var stats = fs.statSync(options.dest); - assert(stats.isFile()); - - fsPromised.readFileAsync(options.dest, Constants.UTF8) - .then(function (yaml) { - logger.debug('YAML loaded from file ' + options.dest); - try { - var resultJson = jsYaml.safeLoad(yaml); - assert.equal(resultJson.total, EXPECTED_VALUE, 'property \'total\' should have new value \'' + EXPECTED_VALUE + '\'.'); - done(); - } catch (err) { // probably a YAMLException - logger.error('Unexpected error: ' + err.stack); - done(err); - } - }); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - }); - - describe('Testing Transformer transforming from YAML to YAML', function () { - - var SRC = './test/data/test-file.yaml'; - var DEST = TEST_TMP_DIR + '/test-data-transform-yaml-yaml.yaml'; - - it('should store ' + SRC + ' file to ' + DEST, function (done) { - - var options = { - src: path.resolve(SRC), - dest: path.resolve(DEST) - }; - - transformer.transform(options, middleware) - .then(function (msg) { - logger.info(msg); - var stats = fs.statSync(options.dest); - assert(stats.isFile()); - - fsPromised.readFileAsync(options.dest, Constants.UTF8) - .then(function (yaml) { - logger.debug('YAML loaded from file ' + options.dest); - try { - var resultJson = jsYaml.safeLoad(yaml); - assert.equal(resultJson.total, EXPECTED_VALUE, 'property \'total\' should have new value \'' + EXPECTED_VALUE + '\'.'); - done(); - } catch (err) { // probably a YAMLException - logger.error('Unexpected error: ' + err.stack); - done(err); - } - }); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - }); - - describe('Testing Transformer transforming from JSON to JSON', function () { - - var SRC = './test/data/test-file.json'; - var DEST = TEST_TMP_DIR + '/test-data-transform-json-json.json'; - - it('should store ' + SRC + ' file to ' + DEST, function (done) { - - var options = { - src: path.resolve(SRC), - dest: path.resolve(DEST) - }; - - assertTransformSuccess(options, middleware, done); - - }); - }); - - describe('Testing Transformer transforming from JSON to YAML', function () { - - var SRC = './test/data/test-file.json'; - var DEST = TEST_TMP_DIR + '/test-data-transform-json-yaml.yaml'; - - it('should store ' + SRC + ' file to ' + DEST, function (done) { - - var options = { - src: path.resolve(SRC), - dest: path.resolve(DEST) - }; - - assertYamlTransformSuccess(options, middleware, done); - - }); - }); - - describe('Testing Transformer transforming from JS to JS', function () { - - var SRC = './test/data/test-file.js'; - var DEST = TEST_TMP_DIR + '/test-data-transform-js-js.js'; - - it('should store ' + SRC + ' file to ' + DEST, function (done) { - - var options = { - src: path.resolve(SRC), - dest: path.resolve(DEST) - }; - - assertTransformSuccess(options, middleware, done); - - }); - }); - -}); diff --git a/test/test-validator.js b/test/test-validator.js deleted file mode 100644 index d0a0725..0000000 --- a/test/test-validator.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var Promise = require('bluebird'); -var fs = require('fs'); -var os = require('os'); -var stringify = require('json-stringify-safe'); -var stream = require('stream'); -var Validator = require('../lib/validator'); -var logger; -var validator; - -/** - * @classdesc This unit test suite checks validity and correctness. - */ -describe('Executing \'jy-transform\' project Writer test suite.', function () { - - /** - * Init the test logger and Writer. - */ - before(function () { - logger = require('./logger.js'); - validator = new Validator(logger); - }); - - var nonStringIdentifier = {}; - it('should validate non-string identifier to false', function (done) { - assert.equal(validator.validateIdentifier(nonStringIdentifier), false, 'validator should validate non-string identifier \'' + stringify(nonStringIdentifier) + '\' to false'); - done(); - }); - - var invalidIdentifier = '#3/-'; - it('should validate invalid identifier \'' + invalidIdentifier + '\' to false', function (done) { - assert.equal(validator.validateIdentifier(invalidIdentifier), false, 'validator should validate \'' + invalidIdentifier + '\' identifier to false'); - done(); - }); - - var validIdentifier = 'bar'; - it('should validate \'' + validIdentifier + '\' identifier to true', function (done) { - assert.equal(validator.validateIdentifier(validIdentifier), true, 'validator should validate \'' + validIdentifier + '\' identifier to true'); - done(); - }); - -}); diff --git a/test/test-writer.js b/test/test-writer.js deleted file mode 100644 index 2b7a88c..0000000 --- a/test/test-writer.js +++ /dev/null @@ -1,636 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var Promise = require('bluebird'); -var YAMLException = require('js-yaml/lib/js-yaml/exception'); -var fs = require('fs'); -var os = require('os'); -var stream = require('stream'); -var Writer = require('../index').Writer; -var logger; -var writer; - -/** - * @classdesc This unit test suite checks the validity and correctness of {@link Writer} class. - */ -describe('Executing \'jy-transform\' project Writer test suite.', function () { - - /** - * Init the test logger and Writer. - */ - before(function () { - logger = require('./logger.js'); - writer = new Writer(logger); - }); - - /** - * Asserts that the given `dest` is a file. - * - * @param {string} dest - File destination to assert. - * @param {function} [done] - Test's `done` callback. - * @returns {Error} - If dest not exists and `done` is not passed. - * @private - */ - function assertDestFile(dest, done) { - // check for existing source file - try { - var stats = fs.statSync(dest); // TODO could we check this in Async mode? - assert(stats.isFile(), 'write destination ' + dest + ' should be file'); - if (done) { - return done(); - } - } catch (err) { - if (err.code === 'ENOENT') { - err.message = 'The input file \'' + dest + '\' does not exists or is not accessible, cause: ' + err.message; - } else { - err.message = 'Some error occurred while accessing input file \'' + dest + '\': ' + err.code + ', ' + err.message; - } - if (done) { - return done(err); - } - return err; - } - } - - /** - * Asserts that the given `dest` does not exist. - * - * @param {string} dest - File destination to assert. - * @param {function} [done] - Test's `done` callback. - * @returns {Error} - If dest not exists and `done` is not passed. - * @private - */ - function assertNotDestFile(dest, done) { - // check for existing source file - try { - fs.statSync(dest); // TODO could we check this in Async mode? - if (done) { - return done(new Error('Error expected when checking file = ' + dest)); - } - } catch (err) { - logger.info('Error is EXPECTED: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert.equal(err.code, 'ENOENT', 'err.code should equal \'ENOENT\''); - if (done) { - return done(); - } - } - } - - var json = { - test: 'value' - }; - - var errorThrowingStream = new stream.Writable(); - errorThrowingStream._write = function (chunk, encoding, done) { - logger.info('stream emitting Error now'); - this.emit('error', new Error('Dummy Error')); - done(); - }; - - describe('Testing Writer.writeJs(...)', function () { - - it('should write JS to file', function (done) { - - var options = { - dest: './test/tmp/test-data-by-js-to-file.js' - }; - - writer.writeJs(json, options) - .then(function (msg) { - assert.notEqual(msg, null); - assertDestFile(options.dest, done); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should write JS to stream', function (done) { - - var file = './test/tmp/test-data-by-js-stream.js'; - var dest = fs.createWriteStream(file); - - var options = { - dest: dest - }; - - writer.writeJs(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assertDestFile(file, done); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should write JS to stream with exports identifier', function (done) { - - var file = './test/tmp/test-data-by-js-stream-with-exports-identifier.js'; - var dest = fs.createWriteStream(file); - - var options = { - dest: dest, - exports: 'test' - }; - - writer.writeJs(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assertDestFile(file, done); - var json = require('./tmp/test-data-by-js-stream-with-exports-identifier.js').test; - assert.notEqual(json, null, 'json from test identifier should not be null'); - assert.equal(json.test, 'value', 'json from test identifier should have a \'test\' property with value \'value\''); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should write JS to file and fail by invalid exports identifier (\'#3/-\')', function (done) { - var dest = './test/tmp/test-data-by-js-stream-with-invalid-exports-identifier.js'; - - var options = { - dest: dest, - exports: '#3/-' - }; - - writer.writeJs(json, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should write JS to stream and fail by invalid exports identifier (\'#3/-\')', function (done) { - var file = './test/tmp/test-data-by-js-stream-with-invalid-exports-identifier.js'; - var dest = fs.createWriteStream(file); - - var options = { - dest: dest, - exports: '#3/-' - }; - - writer.writeJs(json, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should write JS to stream and fail by invalid exports identifier (\'if\')', function (done) { - var file = './test/tmp/test-data-by-js-stream-with-invalid-exports-identifier.js'; - var dest = fs.createWriteStream(file); - - var options = { - dest: dest, - exports: 'if' - }; - - writer.writeJs(json, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - - it('should write JS to stream and fail by provoked error', function (done) { - - var options = { - dest: errorThrowingStream - }; - - writer.writeJs(json, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should write JS to JS object', function (done) { - - var options = { - dest: {} - }; - - writer.writeJs(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assert.notEqual(options.dest, null, 'options.dest should not be null, was: ' + JSON.stringify(options.dest)); - assert.notEqual(options.dest.hasOwnProperty('test'), null, 'options.dest should have \'test\' property, was: ' + JSON.stringify(options.dest)); - assert.equal(options.dest.test, 'value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should write JS to JS object with options.exports == \'\'', function (done) { - - var options = { - dest: {}, - exports: '' - }; - - writer.writeJs(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assert.notEqual(options.dest, null, 'options.dest should not be null, was: ' + JSON.stringify(options.dest)); - assert.notEqual(options.dest.hasOwnProperty('test'), null, 'options.dest should have \'test\' property, was: ' + JSON.stringify(options.dest)); - assert.equal(options.dest.test, 'value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - var exports = 'foo'; - it('should write JS to JS object with options.exports == \'' + exports + '\'', function (done) { - - var options = { - dest: {}, - exports: exports - }; - - writer.writeJs(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assert.notEqual(options.dest, null, 'options.dest should not be null, was: ' + JSON.stringify(options.dest)); - assert(options.dest.hasOwnProperty(exports), 'options.dest should have \'' + exports + '\' property, was: ' + JSON.stringify(options.dest)); - assert(options.dest[exports].hasOwnProperty('test'), 'options.dest.' + exports + ' should have \'test\' property, was: ' + JSON.stringify(options.dest[exports])); - assert.equal(options.dest[exports].test, 'value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - var invalidIdentifier = '#3/-'; - it('should reject write JS with Error on invalid identifier for options.exports: ' + invalidIdentifier, function (done) { - - var options = { - dest: {}, - exports: invalidIdentifier - }; - - writer.writeJs(json, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should reject write JS with Error on missing destination', function (done) { - - var options = { - }; - - writer.writeJs(json, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should reject write JS to file by invalid file path', function (done) { - - var options = { - dest: './test/tmp/<>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/test-data-by-js-to-file.js' - }; - - writer.writeJs(json, options) - .then(function (msg) { - assert.notEqual(msg, null); - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - }); - - describe('Testing Writer.writeJson(...)', function () { - - it('should write JSON to file', function (done) { - - var options = { - src: json, - dest: './test/tmp/test-data-by-json-to-file.json' - }; - - writer.writeJson(json, options) - .then(function (msg) { - assert.notEqual(msg, null); - assertDestFile(options.dest, done); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should write JSON to stream', function (done) { - - var file = './test/tmp/test-data-by-json-stream.json'; - var dest = fs.createWriteStream(file); - - var options = { - dest: dest - }; - - writer.writeJson(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assertDestFile(file, done); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should write JS to JS object', function (done) { - - var options = { - dest: {} - }; - - writer.writeJson(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assert.notEqual(options.dest, null, 'options.dest should not be null, was: ' + JSON.stringify(options.dest)); - var result = JSON.parse(options.dest); - assert.notEqual(result.hasOwnProperty('test'), null, 'options.dest should have \'test\' property, was: ' + JSON.stringify(options.dest)); - assert.equal(result.test, 'value'); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should reject with Error on missing destination', function (done) { - - var options = { - }; - - writer.writeJson(json, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - }); - - - describe('Testing Writer.writeYaml(...)', function () { - - it('should write YAML to file', function (done) { - - var options = { - dest: './test/tmp/test-data-by-js-to-file.yaml' - }; - - writer.writeYaml(json, options) - .then(function (msg) { - assert.notEqual(msg, null); - assertDestFile(options.dest, done); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should write YAML to stream', function (done) { - - var file = './test/tmp/test-data-by-js-stream.yaml'; - var dest = fs.createWriteStream(file); - - var options = { - dest: dest - }; - - writer.writeYaml(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assertDestFile(file, done); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should write stringified YAML to JS object', function (done) { - - var options = { - dest: {} - }; - - writer.writeYaml(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assert.notEqual(options.dest, null, 'options.dest should not be null, was: ' + JSON.stringify(options.dest)); - assert(typeof options.dest === 'string'); - var key = Object.keys(json)[0]; - assert.equal(options.dest, key + ': ' + json[key] + os.EOL, 'options.dest should contain YAML string, was: ' + JSON.stringify(options.dest)); - done(); - }) - .catch(function (err) { - logger.error(err.stack); - done(err); - }); - }); - - it('should reject with Error by invalid src object', function (done) { - - var options = { - dest: './test/tmp/test-data-by-js-to-file-invalid.yaml' - }; - - var invalidYamlJson = function() {}; - - writer.writeYaml(invalidYamlJson, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof YAMLException, 'expected Error should equal YAMLException, was: ' + (typeof err)); - done(); - }); - }); - - it('should reject with Error on missing destination', function (done) { - - var options = { - }; - - writer.writeYaml(json, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - }); - - describe('Testing force overwrite file', function () { - - it('should reject when options.dest is a directory', function (done) { - var dir = './test/data'; - var options = { - dest: dir - }; - writer.writeYaml(json, options) - .then(function (msg) { - done(new Error('Error expected')); - }) - .catch(function (err) { - logger.info('EXPECTED ERROR: ' + err.stack); - assert.notEqual(err, null, 'err should not be null'); - assert(err instanceof Error, 'expected Error should equal Error, was: ' + (typeof err)); - done(); - }); - }); - - it('should write YAML to stream, overwrite on 2nd write, don\'t overwrite on 3rd write and overwrite on 4th write', function (done) { - - // we have to set higher timeout here because some travis jobs failed due to 2 sec timeout - this.timeout(10000); - - var dest = './test/tmp/test-data-file-overwriting.yaml'; - - var options = { - indent: 4, - dest: dest - }; - Promise.each([ - function () { - return writer.writeYaml(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assertDestFile(dest); - return Promise.resolve('overwrite test #1 should initially write YAML to file \'' + dest + '\''); - }); - }, - function () { - options = { - indent: 4, - dest: dest, - force: true - }; - return writer.writeYaml(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assertDestFile(dest); - assertNotDestFile('./test/tmp/test-data-file-overwriting(1).yaml'); - return Promise.resolve('overwrite test #2 should overwrite existing YAML file \'' + dest + '\''); - }); - }, - function () { - return writer.writeYaml(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assertDestFile('./test/tmp/test-data-file-overwriting(1).yaml'); - return Promise.resolve('overwrite test #3 shouldn\'t overwrite existing YAML file \'' + dest + '\', but write new file \'./test/tmp/test-data-file-overwriting(1).yaml\''); - }); - }, - function () { - options = { - indent: 4, - dest: dest, - force: false - }; - return writer.writeYaml(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assertNotDestFile('./test/tmp/test-data-file-overwriting(2).yaml'); - return Promise.resolve('overwrite test #4 should overwrite existing YAML file \'' + dest + '\''); - }); - }, - function () { - options = { - indent: 4, - dest: dest - }; - return writer.writeYaml(json, options) - .then(function (msg) { - assert.notEqual(msg, null, 'msg should not be null, was: ' + msg); - assertDestFile('./test/tmp/test-data-file-overwriting(1).yaml'); - return Promise.resolve('overwrite test #5 shouldn\'t overwrite existing YAML file \'' + dest + '\' and \'./test/tmp/test-data-file-overwriting(1).yaml\', but write new file \'./test/tmp/test-data-file-overwriting(2).yaml\''); - }); - } - ], function(value, index, length) { - return value().then(function (msg) { - logger.info('testing overwrite #' + (index + 1) + '/' + length + ': ' + msg); - }); - }).then(function () { - done(); - }).catch(function (err) { - done(err); - }); - }); - - }); - -}); diff --git a/test/unit/test-index.js b/test/unit/test-index.js new file mode 100644 index 0000000..28853a7 --- /dev/null +++ b/test/unit/test-index.js @@ -0,0 +1,60 @@ +import index from '../../index'; +import Transformer from '../../lib/transformer'; +import Reader from '../../lib/reader'; +import Writer from '../../lib/writer'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +/** + * @module test-unit:index + * @description This unit test module tests the correct exporting from _./index.js_. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - index - ', () => { + describe('Exports Check Unit Tests', () => { + describe('Exports', () => + it('should be an existing Object', () => { + expect(index).toBeDefined(); + expect(index).toBeInstanceOf(Object); + expect(Object.keys(index)).toHaveLength(5); + }) + ); + + describe('Exported Transformer', () => + it('should be an existing Transformer class', () => { + expect(index.Transformer).toBeDefined(); + expect(index.Transformer).toBeInstanceOf(Function); + expect(new index.Transformer()).toBeInstanceOf(Transformer); + }) + ); + + describe('Exported Reader', () => + it('should be an existing Reader class', () => { + expect(index.Reader).toBeDefined(); + expect(index.Reader).toBeInstanceOf(Function); + expect(new index.Reader()).toBeInstanceOf(Reader); + }) + ); + + describe('Exported Writer', () => + it('should be an existing Writer class', () => { + expect(index.Writer).toBeDefined(); + expect(index.Writer).toBeInstanceOf(Function); + expect(new index.Writer()).toBeInstanceOf(Writer); + }) + ); + + describe('Exported Constants', () => + it('should be an existing Constants instance', () => { + expect(index.Constants).toBeDefined(); + expect(index.Constants).toBeInstanceOf(Object); + }) + ); + + describe('Exported Middleware', () => + it('should be an existing Middleware instance', () => { + expect(index.Middleware).toBeDefined(); + expect(index.Middleware).toBeInstanceOf(Object); + }) + ); + }); +}); diff --git a/test/unit/test-log-wrapper.js b/test/unit/test-log-wrapper.js new file mode 100644 index 0000000..38ba97c --- /dev/null +++ b/test/unit/test-log-wrapper.js @@ -0,0 +1,236 @@ +import LogWrapper from '../../lib/log-wrapper'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +/** + * @classdesc This unit test suite checks the validity and correctness of {@link LogWrapper} class. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - log-wrapper - ', () => { + /** + * `INFO` message buffer. + * @private + */ + let infoMsg; + + /** + * `DEBUG` message buffer. + * @private + */ + let debugMsg; + + /** + * `TRACE` message buffer. + * @private + */ + let traceMsg; + + /** + * `ERROR` message buffer. + * @private + */ + let errorMsg; + + /** + * The log wrapper testee. + * @private + */ + let logWrapper; + + /** + * `INFO` message constant. + * @constant + * @private + */ + const INFO = 'INFO'; + + /** + * `DEBUG` message constant. + * @constant + * @private + */ + const DEBUG = 'DEBUG'; + + /** + * `TRACE` message constant. + * @constant + * @private + */ + const TRACE = 'TRACE'; + + /** + * `ERROR` message constant. + * @constant + * @private + */ + const ERROR = 'ERROR'; + + /** + * A verbose result buffer. + * @type {Array} + * @private + */ + const verboseResultArray = []; + + /** + * A mock logger writing to message buffers. + * + * @type {{info: ((p1:*)=>*), debug: ((p1:*)=>*), trace: ((p1:*)=>*), error: ((p1:*)=>*)}} + * @private + */ + const mockLogger = { + info: msg => (infoMsg = msg), + debug: msg => (debugMsg = msg), + trace: msg => (traceMsg = msg), + error: msg => (errorMsg = msg) + }; + + /** + * A mock logger without `debug` function writing to message buffers. + * + * @type {{info: ((p1:*)=>*), trace: ((p1:*)=>*), error: ((p1:*)=>*)}} + * @private + */ + const mockLoggerWithoutDebugFunction = { + info: msg => (infoMsg = msg), + trace: msg => (traceMsg = msg), + error: msg => (errorMsg = msg), + }; + + /** + * A mock logger without `trace` function writing to message buffers. + * + * @type {{info: ((p1:*)=>*), debug: ((p1:*)=>*), error: ((p1:*)=>*)}} + * @private + */ + const mockLoggerWithoutTraceFunction = { + info: msg => (infoMsg = msg), + debug: msg => (debugMsg = msg), + error: msg => (errorMsg = msg), + }; + + /** + * A mock logger function writing to verbose result array. + * @type {{info: ((p1?:*)=>Number)}} + * @private + */ + const mockLoggerWithVerboseFunction = { + info: msg => verboseResultArray.push(msg) + }; + + describe('Testing LogWrapper with mockLogger', () => { + /** + * Resets the log wrapper with mock logger and all message buffers with `undefined`. + */ + beforeEach(() => { + infoMsg = undefined; + debugMsg = undefined; + traceMsg = undefined; + errorMsg = undefined; + logWrapper = new LogWrapper(mockLogger); + }); + + let expected = INFO; + it('should log with ' + expected, () => { + logWrapper.info(expected); + expect(infoMsg).toBe(expected); + }); + + expected = DEBUG; + it('should log with ' + expected, () => { + logWrapper.debug(expected); + expect(debugMsg).toBe(expected); + }); + + expected = TRACE; + it('should log with ' + expected, () => { + logWrapper.trace(expected); + expect(traceMsg).toBe(expected); + }); + + expected = ERROR; + it('should log with ' + expected, () => { + logWrapper.error(expected); + expect(errorMsg).toBe(expected); + }); + + const verboseExpected = { + origin: 'origin', + target: 'target', + src: 'src', + dest: 'dest', + indent: 'indent' + }; + + it('should log options', async () => { + logWrapper = new LogWrapper(mockLoggerWithVerboseFunction); + const options = await logWrapper.verboseOptions(verboseExpected); + expect(options).toBe(verboseExpected); + expect(verboseResultArray.indexOf('origin: ' + verboseExpected.origin)).toBeGreaterThan(-1); + expect(verboseResultArray.indexOf('target: ' + verboseExpected.target)).toBeGreaterThan(-1); + expect(verboseResultArray.indexOf('src: ' + verboseExpected.src)).toBeGreaterThan(-1); + expect(verboseResultArray.indexOf('dest: ' + verboseExpected.dest)).toBeGreaterThan(-1); + expect(verboseResultArray.indexOf('indent: ' + verboseExpected.indent)).toBeGreaterThan(-1); + }); + }); + + describe('Testing LogWrapper with mockLoggerWithoutDebugFunction', () => { + /** + * Resets the mock logger message targets. + */ + beforeEach(() => { + infoMsg = undefined; + debugMsg = undefined; + traceMsg = undefined; + errorMsg = undefined; + logWrapper = new LogWrapper(mockLoggerWithoutDebugFunction); + }); + + let expected = INFO; + it('should log with ' + expected, () => { + logWrapper.info(expected); + expect(infoMsg).toBe(expected); + }); + + expected = DEBUG; + it('should log with ' + expected, () => { + logWrapper.debug(expected); + expect(infoMsg).toBe(expected); + }); + + expected = ERROR; + it('should log with ' + expected, () => { + logWrapper.error(expected); + expect(errorMsg).toBe(expected); + }); + }); + + describe('Testing LogWrapper with mockLoggerWithoutTraceFunction', () => { + /** + * Resets the mock logger message targets. + */ + beforeEach(() => { + infoMsg = undefined; + debugMsg = undefined; + errorMsg = undefined; + logWrapper = new LogWrapper(mockLoggerWithoutTraceFunction); + }); + + let expected = INFO; + it('should log with ' + expected, () => { + logWrapper.info(expected); + expect(infoMsg).toBe(expected); + }); + + expected = TRACE; + it('should log with ' + expected, () => { + logWrapper.trace(expected); + expect(debugMsg).toBe(expected); + }); + + expected = ERROR; + it('should log with ' + expected, () => { + logWrapper.error(expected); + expect(errorMsg).toBe(expected); + }); + }); +}); diff --git a/test/unit/test-middleware.js b/test/unit/test-middleware.js new file mode 100644 index 0000000..78faf68 --- /dev/null +++ b/test/unit/test-middleware.js @@ -0,0 +1,124 @@ +import objectPath from 'object-path'; +import { logger } from '../logger'; +import { Transformer, Middleware } from '../../index'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +/** + * @classdesc This unit test suite checks the validity and correctness of {@link Middleware} class. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - middleware - ', () => { + const identityMiddleware = Middleware.identityMiddleware; + let transformer; + + /** + * Middleware function for altering JSON. + * + * @param {Object} json - The JSON object to alter. + * @private + */ + const middleware = (json) => { + const key1 = async (jsonToAlter) => { + objectPath.set(jsonToAlter, 'key1', 'value1'); + logger.info('key1 json: ' + JSON.stringify(jsonToAlter)); + return jsonToAlter; + }; + + const key2 = async (jsonToAlter) => { + objectPath.set(jsonToAlter, 'key2', 'value2'); + logger.info('key2 json: ' + JSON.stringify(jsonToAlter)); + return jsonToAlter; + }; + + const key3 = async (jsonToAlter) => { + objectPath.set(jsonToAlter, 'key3', 'value3'); + logger.info('key3 json: ' + JSON.stringify(jsonToAlter)); + return jsonToAlter; + }; + + return Promise.all([key1(json), key2(json), key3(json)]) + .then((result) => { + expect(result).toHaveLength(3); + logger.info('all the elements were created'); + logger.info('result: ' + JSON.stringify(result[result.length - 1])); + return result[result.length - 1]; + }); + }; + + /** + * Helper function to assert the identity Promise. + * + * @param {Function} func - The identity Promise function. + * @returns {Promise} A promise. + * @private + */ + const assertIdentityPromise = (func) => { + const json = { test: 'identity' }; + return func(json).then(jsonResult => expect(jsonResult).toBe(json)); + }; + + /** + * Init the test logger. + */ + beforeAll(() => { + transformer = new Transformer(logger); + }); + + describe('Testing Transformer middleware', () => { + it('should alter json', () => { + expect.assertions(4); + const options = { + src: {}, + dest: {} + }; + return transformer.transform(options, middleware) + .then((msg) => { + logger.info(msg); + logger.info('options.dest: ' + JSON.stringify(options.dest, null, 4)); + expect(options.dest.key1).toBe('value1'); + expect(options.dest.key2).toBe('value2'); + expect(options.dest.key3).toBe('value3'); + }); + }); + }); + + describe('Testing middleware.identityMiddleware()', () => { + it('should provide passed function', async () => { + const func = identityMiddleware; + expect(func).toBeInstanceOf(Function); + expect(func).toBe(identityMiddleware); + + const json = {}; + const jsonIdentity = await identityMiddleware(json); + expect(jsonIdentity).toBe(json); + }); + }); + + describe('Testing middleware.ensureMiddleware()', () => { + it('should provide passed function', () => { + expect.assertions(2); + const func = Middleware.ensureMiddleware(identityMiddleware); + expect(func).toBeInstanceOf(Function); + expect(func).toBe(identityMiddleware); + }); + + it('should throw TypeError if middleware passed is not a function type', () => { + expect.assertions(1); + expect(() => Middleware.ensureMiddleware('not a function')).toThrow(TypeError); + }); + + it('should provide identity Promise if middleware passed is null', async () => { + expect.assertions(2); + const func = Middleware.ensureMiddleware(); + expect(func).toBeInstanceOf(Function); + await assertIdentityPromise(func); + }); + + it('should provide identity Promise if middleware passed is undefined', async () => { + expect.assertions(2); + const func = Middleware.ensureMiddleware(undefined); + expect(func).toBeInstanceOf(Function); + await assertIdentityPromise(func); + }); + }); +}); diff --git a/test/unit/test-options-handler.js b/test/unit/test-options-handler.js new file mode 100644 index 0000000..689642e --- /dev/null +++ b/test/unit/test-options-handler.js @@ -0,0 +1,338 @@ +import fs from 'fs'; +import fsExtra from 'fs-extra'; +import path from 'path'; +import stream from 'stream'; +import OptionsHandler from '../../lib/options-handler'; +import { logger } from '../logger'; +import Constants from '../../lib/constants'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +/** + * @classdesc This unit test suite checks the validity and correctness of {@link OptionsHandler} class. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-handler - ', () => { + /** + * Temporary base dir for writer test output. + * @type {string} + * @constant + * @private + */ + const OPTIONSHANDLER_TEST_BASE_DIR = './test/tmp/options-handler'; + + /** + * The testee. + * @type {OptionsHandler} + * @private + */ + let optionsHandler; + + /** + * Init the test logger and Writer. + */ + beforeAll(() => { + fsExtra.ensureDirSync(OPTIONSHANDLER_TEST_BASE_DIR); + fsExtra.emptyDirSync(OPTIONSHANDLER_TEST_BASE_DIR); + optionsHandler = new OptionsHandler(logger); + }); + + /** + * Assert an `Error` for a given options function. + * + * @param {Object} options - The options which potentially produce the error. + * @param {Function} optionsFunc - The function to call for assertion. + * @param {Error} [errorType=Error] - The error type to expect. + * @private + */ + function expectOptionsError(options, optionsFunc, errorType = Error) { + expect.assertions(1); + return expect(optionsFunc(options)).rejects.toBeInstanceOf(errorType); + } + + describe('Testing OptionsHandler.validateTransformation(...)', () => { + it('should reject when options is missing', async () => + await expectOptionsError(null, optionsHandler.validateTransformation) + ); + + it('should reject when options.origin is missing', async () => + await expectOptionsError({ target: Constants.YAML }, optionsHandler.validateTransformation) + ); + + it('should reject when options.target is missing', async () => + await expectOptionsError(null, optionsHandler.validateTransformation) + ); + + it('should resolve transformation correctly from valid origin and target', async () => { + const options = { + origin: Constants.YAML, + target: Constants.JS, + }; + const results = await optionsHandler.validateTransformation(options); + expect(results).toHaveLength(2); + expect(results[0]).toBe(options); + expect(results[1]).toBe(Constants.YAML + '2' + Constants.JS); + }); + + it('should reject with Error due to invalid target', async () => { + const invalidOptions = { + origin: Constants.YAML, + target: 'INVALID_TARGET', + }; + await expectOptionsError(invalidOptions, optionsHandler.validateTransformation); + }); + }); + + describe('Testing OptionsHandler.completeOptions(...)', () => { + it('should reject when options is missing', async () => + await expectOptionsError(null, optionsHandler.completeOptions) + ); + + it('should resolve options.src/origin and options.dest/target with default values (' + Constants.DEFAULT_ORIGIN + + '/' + Constants.DEFAULT_TARGET + ')', async () => { + const PATH_WITH_INVALID_EXT = 'PATH_WITH_INVALID.EXT'; + const options = { + src: PATH_WITH_INVALID_EXT, + dest: PATH_WITH_INVALID_EXT, + }; + const resultOptions = await optionsHandler.completeOptions(options); + expect(resultOptions.origin).toBe(Constants.DEFAULT_ORIGIN); + expect(resultOptions.target).toBe(Constants.DEFAULT_TARGET); + expect(resultOptions.dest).toBe(PATH_WITH_INVALID_EXT); + expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); + expect(resultOptions.force).toBe(Constants.DEFAULT_FORCE_FILE_OVERWRITE); + }); + + it('should resolve options.force should result to ' + !Constants.DEFAULT_FORCE_FILE_OVERWRITE, async () => { + const PATH_WITH_INVALID_EXT = 'PATH_WITH_INVALID.EXT'; + const force = !Constants.DEFAULT_FORCE_FILE_OVERWRITE; + const options = { + src: PATH_WITH_INVALID_EXT, + dest: PATH_WITH_INVALID_EXT, + force, + }; + const resultOptions = await optionsHandler.completeOptions(options); + expect(resultOptions.origin).toBe(Constants.DEFAULT_ORIGIN); + expect(resultOptions.target).toBe(Constants.DEFAULT_TARGET); + expect(resultOptions.dest).toBe(PATH_WITH_INVALID_EXT); + expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); + expect(resultOptions.force).toBe(force); + }); + }); + + describe('Testing OptionsHandler.ensureIndent(...)', () => { + it('should reject when options is missing', async () => + await expectOptionsError(null, optionsHandler.ensureIndent) + ); + + it('should set default indent if indent is missing', async () => { + const resultOptions = await optionsHandler.ensureIndent({}); + expect(resultOptions.indent).toBeDefined(); + expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); + }); + + it('should set default indent if indent < minimum indent', async () => { + const options = { + indent: (Constants.MIN_INDENT - 1), + target: Constants.YAML, + }; + const resultOptions = await optionsHandler.ensureIndent(options); + expect(resultOptions.target).toBe(Constants.YAML); + expect(resultOptions.indent).toBeDefined(); + expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); + }); + + it('should set default indent if indent < JS/JSON minimum indent', async () => { + const resultOptions = await optionsHandler.ensureIndent({ indent: Constants.MIN_INDENT - 1 }); + expect(resultOptions.indent).toBeDefined(); + expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); + }); + + it('should set default indent if indent > than maximum indent', async () => { + const resultOptions = await optionsHandler.ensureIndent({ indent: Constants.MAX_INDENT + 1 }); + expect(resultOptions.indent).toBeDefined(); + expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); + }); + }); + + describe('Testing OptionsHandler.assertOrigin(...)', () => { + it('should reject when options is missing', async () => + await expectOptionsError(null, optionsHandler.assertOrigin) + ); + + it('should resolve options.origin for valid type YAML', async () => { + const options = { origin: Constants.YAML }; + const resultOptions = await optionsHandler.assertOrigin(options); + expect(resultOptions.origin).toBeDefined(); + expect(resultOptions.origin).toBe(Constants.YAML, 'result origin should have type ' + Constants.YAML); + }); + + it('should resolve options.origin for valid type JS', async () => { + const resultOptions = await optionsHandler.assertOrigin({ origin: Constants.JS }); + expect(resultOptions.origin).toBeDefined(); + expect(resultOptions.origin).toBe(Constants.JS); + }); + + it('should resolve options.origin for valid type JSON', async () => { + const resultOptions = await optionsHandler.assertOrigin({ origin: Constants.JSON }); + expect(resultOptions.origin).toBeDefined(); + expect(resultOptions.origin).toBe(Constants.JSON); + }); + + it('should reject when options.origin is invalid type', async () => + await expectOptionsError({ origin: 'INVALID_TYPE' }, optionsHandler.assertOrigin) + ); + }); + + describe('Testing OptionsHandler.assertTarget(...)', () => { + it('should reject when options is missing', async () => + await expectOptionsError(null, optionsHandler.assertTarget) + ); + + it('should resolve options.target for valid type YAML', async () => { + const resultOptions = await optionsHandler.assertTarget({ target: Constants.YAML }); + expect(resultOptions.target).toBeDefined(); + expect(resultOptions.target).toBe(Constants.YAML); + }); + + it('should resolve options.target for valid type JS', async () => { + const resultOptions = await optionsHandler.assertTarget({ target: Constants.JS }); + expect(resultOptions.target).toBeDefined(); + expect(resultOptions.target).toBe(Constants.JS); + }); + + it('should resolve options.target for valid type JSON', async () => { + const resultOptions = await optionsHandler.assertTarget({ target: Constants.JSON }); + expect(resultOptions.target).toBeDefined(); + expect(resultOptions.target).toBe(Constants.JSON); + }); + + it('should reject when options.target is invalid type', async () => + await expectOptionsError({ origin: 'INVALID_TYPE' }, optionsHandler.assertTarget) + ); + }); + + describe('Testing OptionsHandler.ensureDest(...)', () => { + it('should reject when options is missing', async () => + await expectOptionsError(null, optionsHandler.ensureDest) + ); + + it('should reject when options.src is stream but options.target is missing', async () => + await expectOptionsError({ dest: new stream.Writable() }, optionsHandler.ensureDest) + ); + + it('should resolve options.dest with value \'' + Constants.DEFAULT_OPTIONS.dest + '\' to relative file path to ' + + Constants.YAML + ' file', async () => { + const fileBaseName = 'test'; + const options = { + src: fileBaseName + '.' + Constants.JS, + dest: Constants.DEFAULT_OPTIONS.dest, + target: Constants.YAML + }; + const resultOptions = await optionsHandler.ensureDest(options); + expect(resultOptions.dest).toBeDefined(); + expect(resultOptions.dest).toBe(fileBaseName + '.' + Constants.YAML); + }); + + it('should resolve options.dest with value \'' + Constants.DEFAULT_OPTIONS.dest + '\' to relative file path to ' + + Constants.JS + ' file', async () => { + const fileBaseName = 'test'; + const options = { + src: fileBaseName + '.' + Constants.YAML, + dest: Constants.DEFAULT_OPTIONS.dest, + target: Constants.JS + }; + const resultOptions = await optionsHandler.ensureDest(options); + expect(resultOptions.dest).toBeDefined(); + expect(resultOptions.dest).toBe(fileBaseName + '.' + Constants.JS); + }); + + it('should resolve options.dest with value \'' + Constants.DEFAULT_OPTIONS.dest + '\' to relative file path to ' + + Constants.JSON + ' file', async () => { + const fileBaseName = 'test'; + const options = { + src: fileBaseName + '.' + Constants.YAML, + dest: Constants.DEFAULT_OPTIONS.dest, + target: Constants.JSON + }; + const resultOptions = await optionsHandler.ensureDest(options); + expect(resultOptions.dest).toBeDefined(); + expect(resultOptions.dest).toBe(fileBaseName + '.' + Constants.JSON); + }); + + it('should reject options.dest when invalid target type is provided', async () => { + const options = { + src: 'test.' + Constants.YAML, + dest: Constants.DEFAULT_OPTIONS.dest, + target: 'INVALID_TARGET' + }; + await expectOptionsError(options, optionsHandler.ensureDest); + }); + + it('should reject when Writable is given but not target', async () => + await expectOptionsError({ dest: fs.createWriteStream(OPTIONSHANDLER_TEST_BASE_DIR + '/myOutput.txt') }, + optionsHandler.ensureDest) + ); + + it('should resolve original options.dest', async () => { + const destObj = {}; + const resultOptions = await optionsHandler.ensureDest({ dest: destObj }); + expect(resultOptions.dest).toBeDefined(); + expect(resultOptions.dest).toBe(destObj); + }); + + it('should resolve with undefined for options.dest', async () => { + const resultOptions = await optionsHandler.ensureDest({}); + expect(resultOptions.dest).toBeUndefined(); + }); + }); + + describe('Testing OptionsHandler.ensureSrc(...)', () => { + it('should reject when options is missing', async () => + await expectOptionsError(null, optionsHandler.ensureSrc, TypeError) // TODO check if this really TypeError!?! + ); + + it('should reject when options.src is not given', async () => + await expectOptionsError({}, optionsHandler.ensureSrc) + ); + + it('should resolve original options.src', async () => { + const existingFile = path.resolve('./test/data/test-file.yaml'); + const resultOptions = await optionsHandler.ensureSrc({ src: existingFile }); + expect(resultOptions.src).toBeDefined(); + expect(resultOptions.src).toBe(existingFile); + }); + + it('should reject when options.src has value of not existing file', async () => + // HINT: this gives no Error type but plain object: + // {"errno":-2,"code":"ENOENT","syscall":"stat","path":"NON_EXISTING_FILE"} + await expectOptionsError({ src: 'NON_EXISTING_FILE' }, optionsHandler.ensureSrc) + ); + + it('should reject when options.src is a directory', async () => + await expectOptionsError({ src: './test/data' }, optionsHandler.ensureSrc) + ); + + it('should reject when Readable is given but not origin', async () => + await expectOptionsError({ src: fs.createReadStream('./test/data/readable-test-dummy.txt') }, + optionsHandler.ensureSrc) + ); + + it('should resolve original options.src Readable', async () => { + const readable = fs.createReadStream('./test/data/readable-test-dummy.txt'); + const options = { + src: readable, + origin: Constants.JSON + }; + const resultOptions = await optionsHandler.ensureSrc(options); + expect(resultOptions.src).toBeDefined(); + expect(resultOptions.src).toBe(readable); + }); + + it('should resolve original options.src object', async () => { + const srcObj = {}; + const resultOptions = await optionsHandler.ensureSrc({ src: srcObj }); + expect(resultOptions.src).toBeDefined(); + expect(resultOptions.src).toBe(srcObj); + }); + }); +}); diff --git a/test/unit/test-reader.js b/test/unit/test-reader.js new file mode 100644 index 0000000..d517e2a --- /dev/null +++ b/test/unit/test-reader.js @@ -0,0 +1,251 @@ +import YAMLException from 'js-yaml/lib/js-yaml/exception'; +import fs from 'fs'; +import { Reader, Constants } from '../../index'; +import { logger } from '../logger'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +/** + * @classdesc This unit test suite checks the validity and correctness of {@link Reader} class. + */ +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { + /** + * The testee. + * @type {Reader} + */ + let reader; + + /** + * Assert an `Error` for a given reader function. + * + * @param {Object} options - The options which potentially produce the error. + * @param {Function} readerFunc - The function to call for assertion. + * @param {Error} [expectedErrorType=Error] - The error type to expect. + * @private + */ + const expectReaderError = (options, readerFunc, expectedErrorType = Error) => { + expect.assertions(1); + return expect(readerFunc(options)).rejects.toBeInstanceOf(expectedErrorType); + }; + + /** + * Assert a successful reading of input. + * + * @param {Object} options - The options. + * @param {Function} readerFunc - The function to call for assertion. + * @param {string} key - The function to call for assertion. + * @param {*} expectedValue - The expected value for `key`. + * @private + */ + const expectReaderSuccess = async (options, readerFunc, key, expectedValue) => { + expect.assertions(2); + const json = await readerFunc(options); + expect(json).toBeDefined(); + expect(json[key]).toBe(expectedValue); + }; + + beforeAll(() => { + reader = new Reader(logger); + }); + + describe('Testing Reader.readJs(...)', () => { + const exports = 'fooBar'; + const exportsNotExists = 'notFooBar'; + const invalidIdentifier = '#3/-'; + + it('should read JS from file', async () => + await expectReaderSuccess({ src: './test/data/test-data.js' }, reader.readJs, 'myproperty', 'old value') + ); + + it('should read JS from file with options.imports == \'\'', async () => { + const options = { + src: './test/data/test-data.js', + imports: '', + }; + await expectReaderSuccess(options, reader.readJs, 'myproperty', 'old value'); + }); + + + it('should read JS from file with options.imports == \'' + exports + '\'', async () => { + expect.assertions(5); + const options = { + src: './test/data/test-imports.js', + imports: exports, + }; + const json = await reader.readJs(options); + expect(json).toBeDefined(); + expect(Object.prototype.hasOwnProperty.call(json, exports)).toBeFalsy(); + expect(Object.prototype.hasOwnProperty.call(json, 'bar')).toBeFalsy(); + expect(Object.prototype.hasOwnProperty.call(json, 'foo')).toBeTruthy(); + expect(json.foo).toBe('bar'); + }); + + it('should read JS from file with options.imports == \'' + exports + + '\' and given origin for unsupported file extension', async () => { + expect.assertions(5); + const options = { + src: './test/data/test-imports.txt', + imports: exports, + origin: Constants.JS, + }; + const json = await reader.readJs(options); + expect(json).toBeDefined(); + expect(Object.prototype.hasOwnProperty.call(json, exports)).toBeFalsy(); + expect(Object.prototype.hasOwnProperty.call(json, 'bar')).toBeFalsy(); + expect(Object.prototype.hasOwnProperty.call(json, 'foo')).toBeTruthy(); + expect(json.foo).toBe('bar'); + }); + + it('should reject read JS from file with Error on invalid identifier for options.imports: ' + + invalidIdentifier, () => { + const options = { + src: './test/data/test-imports.js', + imports: invalidIdentifier, + }; + return expectReaderError(options, reader.readJs); + }); + + it('should reject read JS from file with Error on non-existent identifier for options.imports: ' + + exportsNotExists, () => { + const options = { + src: './test/data/test-imports.js', + imports: exportsNotExists, + }; + return expectReaderError(options, reader.readJs); + }); + + it('should read JSON from file', async () => + await expectReaderSuccess({ src: './test/data/test-data.json' }, reader.readJs, 'myproperty', 'old value') + ); + + it('should read JS from object', async () => { + const options = { + src: { + test: 'value', + }, + }; + await expectReaderSuccess(options, reader.readJs, 'test', 'value'); + }); + + it('should read JS from object with options.imports == \'\'', async () => { + const options = { + src: { + foo: 'bar', + }, + imports: '', + }; + await expectReaderSuccess(options, reader.readJs, 'foo', 'bar'); + }); + + it('should read JS from object with options.imports == \'' + exports + '\'', async () => { + expect.assertions(6); + const options = { + src: { + fooBar: { + bar: 'foo', + foo: 'bar', + }, + }, + imports: exports, + }; + const json = await reader.readJs(options); + expect(json).toBeDefined(); + expect(Object.prototype.hasOwnProperty.call(json, exports)).toBeFalsy(); + expect(Object.prototype.hasOwnProperty.call(json, 'bar')).toBeTruthy(); + expect(Object.prototype.hasOwnProperty.call(json, 'foo')).toBeTruthy(); + expect(json.bar).toBe('foo'); + expect(json.foo).toBe('bar'); + }); + + it('should reject read JS from object with Error on invalid identifier for options.imports: ' + + invalidIdentifier, () => { + const options = { + src: { + fooBar: { + bar: 'foo', + foo: 'bar', + }, + }, + imports: invalidIdentifier, + }; + return expectReaderError(options, reader.readJs); + }); + + it('should reject read JS from file with Error on non-existent identifier for options.imports: ' + + exportsNotExists, () => { + const options = { + src: { + fooBar: { + bar: 'foo', + foo: 'bar', + }, + }, + imports: exportsNotExists, + }; + return expectReaderError(options, reader.readJs); + }); + + it('should read JSON from stream', async () => + await expectReaderSuccess({ src: fs.createReadStream('./test/data/test-data.json') }, + reader.readJs, 'myproperty', 'old value') + ); + + it('should read corrupted JSON from file path and fail by SyntaxError', () => { + return expectReaderError({ src: './test/data/test-data-corrupted.json' }, reader.readJs, SyntaxError); + }); + + it('should read invalid JSON from file path and fail by SyntaxError', () => { + const options = { src: './test/data/test-data-wrong-syntax.json' }; + return expectReaderError(options, reader.readJs, SyntaxError); + }); + + it('should read corrupted JSON from stream and fail by SyntaxError', () => { + const options = { src: fs.createReadStream('./test/data/test-data-corrupted.json') }; + return expectReaderError(options, reader.readJs, SyntaxError); + }); + + it('should read invalid JSON from stream and fail by SyntaxError', () => { + return expectReaderError({ src: fs.createReadStream('./test/data/test-data-wrong-syntax.json') }, + reader.readJs, SyntaxError); + }); + + it('should fail JS(ON) read by missing options', () => { + return expectReaderError(null, reader.readJs); + }); + + it('should fail JS(ON) read by missing options.src', () => { + return expectReaderError({}, reader.readJs); + }); + }); + + describe('Testing Reader.readYaml(...)', () => { + it('should read YAML from file', async () => + await expectReaderSuccess({ src: './test/data/test-data.yaml' }, reader.readYaml, 'myproperty', 'old value') + ); + + it('should read JS from object', async () => + await expectReaderSuccess({ src: { test: 'value' } }, reader.readYaml, 'test', 'value') + ); + + it('should read YAML from stream', async () => + await expectReaderSuccess({ src: fs.createReadStream('./test/data/test-data.yaml') }, + reader.readYaml, 'myproperty', 'old value') + ); + + it('should read invalid YAML from file path and fail by YAMLException', () => { + return expectReaderError({ src: './test/data/test-data-wrong-syntax.yaml' }, reader.readYaml, YAMLException); + }); + + it('should read invalid YAML from stream and fail by YAMLException', () => { + return expectReaderError({ src: fs.createReadStream('./test/data/test-data-wrong-syntax.yaml') }, + reader.readYaml); + }); + + it('should fail YAML read by missing input options', () => { + return expectReaderError(null, reader.readYaml); + }); + + it('should fail YAML read by missing options.src', () => { + return expectReaderError({}, reader.readYaml); + }); + }); +}); diff --git a/test/unit/test-transformer.js b/test/unit/test-transformer.js new file mode 100644 index 0000000..60ccbcf --- /dev/null +++ b/test/unit/test-transformer.js @@ -0,0 +1,301 @@ +import jsYaml from 'js-yaml'; +import promisify from 'promisify-es6'; +import fsExtra from 'fs-extra'; +import fs from 'fs'; +import path from 'path'; +import { logger } from '../logger'; +import { Transformer, Constants } from '../../index'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +const fsPromised = promisify(fs); + +/** + * @classdesc This unit test suite checks the correct transformation behaviour of {@link Transformer} class. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { + const TEST_DATA_DIR = './test/data'; + const SRC_YAML = TEST_DATA_DIR + '/test-file.yaml'; + const EXPECTED_VALUE = 'bar'; + + /** + * Temporary base dir for writer test output. + * @type {string} + * @constant + * @private + */ + const TRANSFORMER_TEST_BASE_DIR = './test/tmp/transformer'; + + /** + * The testee. + * @type {Transformer} + */ + let transformer; + + beforeAll(() => { + fsExtra.ensureDirSync(TRANSFORMER_TEST_BASE_DIR); + fsExtra.emptyDirSync(TRANSFORMER_TEST_BASE_DIR); + transformer = new Transformer(logger); + }); + + /** + * Prepare test data. + */ + beforeEach(() => { + try { + fsExtra.copySync(SRC_YAML, TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml'); + logger.info('copied ' + SRC_YAML + ' to ' + TRANSFORMER_TEST_BASE_DIR); + } catch (err) { + logger.error('could not copy ' + SRC_YAML + ' to ' + TRANSFORMER_TEST_BASE_DIR + err.stack); + throw err; + } + }); + + /** + * Transformation middleware changing value for `foo` property. + * + * @param {Object} json - To transform. + */ + const middlewareFunc = (json) => { + json.foo = EXPECTED_VALUE; + return Promise.resolve(json); + }; + + /** + * Helper method which asserts the successful transformation. + * + * @param {Object} options - The transformation options. + * @param {Function} middleware - The transformation middleware. + */ + function assertTransformSuccess(options, middleware) { + return transformer.transform(options, middleware) + .then((msg) => { + logger.info(msg); + const stats = fsExtra.statSync(options.dest); + expect(stats.isFile()).toBeTruthy(); + // eslint-disable-next-line import/no-dynamic-require, global-require + const json = require(path.resolve(options.dest)); + expect(json.foo).toBe(EXPECTED_VALUE); + }); + } + + /** + * Helper method which asserts the successful transformation. + * + * @param {Object} options - The transformation options. + * @param {Function} middleware - The transformation middleware. + * @param {Function} done - Test finished callback. + */ + function assertYamlTransformSuccess(options, middleware, done) { + return transformer.transform(options, middleware) + .then((msg) => { + logger.info(msg); + const stats = fsExtra.statSync(options.dest); + expect(stats.isFile()).toBeTruthy(); + return fsExtra.readFileAsync(options.dest, Constants.UTF8) + .then((yaml) => { + try { + const json = jsYaml.safeLoad(yaml); + expect(json.foo).toBe(EXPECTED_VALUE); + return done(); + } catch (err) { // probably a YAMLException + logger.error(err.stack); + return done(err); + } + }); + }) + .catch((err) => { + logger.error(err.stack); + done(err); + }); + } + + describe('Testing Transformer transforming from YAML to JS to relative path', () => { + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data.js'; + + it('should store ' + DEST + ' file relative to ' + TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml', async () => { + expect.assertions(2); + const msg = await transformer.transform({ src: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml') }, + middlewareFunc); + logger.info(msg); + const stats = fsExtra.statSync(DEST); + expect(stats.isFile()).toBeTruthy(); + // eslint-disable-next-line import/no-unresolved, global-require, import/no-dynamic-require + const json = require('../tmp/transformer/test-data.js'); + expect(json.foo).toBe(EXPECTED_VALUE); + }); + }); + + describe('Testing Transformer transforming from YAML to JS', () => { + const SRC = './test/data/test-file.yaml'; + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-yaml-js.js'; + + it('should store ' + SRC + ' file to ' + DEST, async () => { + expect.assertions(2); + const options = { + src: path.resolve(SRC), + dest: path.resolve(DEST), + }; + await assertTransformSuccess(options, middlewareFunc); + }); + }); + + describe('Testing Transformer transforming from YAML to JSON', () => { + const SRC = './test/data/test-file.yaml'; + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-yaml-json.json'; + + it('should store ' + SRC + ' file to ' + DEST, async () => { + expect.assertions(2); + const options = { + src: path.resolve(SRC), + dest: path.resolve(DEST), + }; + await assertTransformSuccess(options, middlewareFunc); + }); + }); + + describe('Testing Transformer transforming from JSON to JS', () => { + const SRC = './test/data/test-file.json'; + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-json-js.js'; + + it('should store ' + SRC + ' file to ' + DEST, async () => { + expect.assertions(2); + const options = { + src: path.resolve(SRC), + dest: path.resolve(DEST), + }; + await assertTransformSuccess(options, middlewareFunc); + }); + }); + + describe('Testing Transformer transforming from JS to JSON', () => { + const SRC = './test/data/test-file.js'; + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-js-json.json'; + + it('should store ' + SRC + ' file to ' + DEST, async () => { + expect.assertions(2); + const options = { + src: path.resolve(SRC), + dest: path.resolve(DEST), + }; + await assertTransformSuccess(options, middlewareFunc); + }); + }); + + describe('Testing Transformer transforming from JS to YAML', () => { + expect.assertions(2); + const SRC = './test/data/test-file.js'; + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-js-yaml.yaml'; + + it('should store ' + SRC + ' file to ' + DEST, (done) => { + expect.assertions(2); + const options = { + src: path.resolve(SRC), + dest: path.resolve(DEST), + }; + + transformer.transform(options, middlewareFunc) + .then((msg) => { + logger.info(msg); + const stats = fsExtra.statSync(options.dest); + expect(stats.isFile()).toBeTruthy(); + + fsPromised.readFile(options.dest, Constants.UTF8) + .then((yaml) => { + logger.debug('YAML loaded from file ' + options.dest); + try { + const resultJson = jsYaml.safeLoad(yaml); + expect(resultJson.foo).toBe(EXPECTED_VALUE); + done(); + } catch (err) { // probably a YAMLException + logger.error('Unexpected error: ' + err.stack); + done(err); + } + }); + }) + .catch((err) => { + logger.error(err.stack); + done(err); + }); + }); + }); + + describe('Testing Transformer transforming from YAML to YAML', () => { + const SRC = './test/data/test-file.yaml'; + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-yaml-yaml.yaml'; + + it('should store ' + SRC + ' file to ' + DEST, (done) => { + expect.assertions(2); + const options = { + src: path.resolve(SRC), + dest: path.resolve(DEST), + }; + + transformer.transform(options, middlewareFunc) + .then((msg) => { + logger.info(msg); + const stats = fsExtra.statSync(options.dest); + expect(stats.isFile()).toBeTruthy(); + + fsPromised.readFile(options.dest, Constants.UTF8) + .then((yaml) => { + logger.debug('YAML loaded from file ' + options.dest); + try { + const resultJson = jsYaml.safeLoad(yaml); + expect(resultJson.foo).toBe(EXPECTED_VALUE); + done(); + } catch (err) { // probably a YAMLException + logger.error('Unexpected error: ' + err.stack); + done(err); + } + }); + }) + .catch((err) => { + logger.error(err.stack); + done(err); + }); + }); + }); + + describe('Testing Transformer transforming from JSON to JSON', () => { + const SRC = './test/data/test-file.json'; + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-json-json.json'; + + it('should store ' + SRC + ' file to ' + DEST, async () => { + expect.assertions(2); + const options = { + src: path.resolve(SRC), + dest: path.resolve(DEST), + }; + await assertTransformSuccess(options, middlewareFunc); + }); + }); + + describe('Testing Transformer transforming from JSON to YAML', () => { + const SRC = './test/data/test-file.json'; + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-json-yaml.yaml'; + + it('should store ' + SRC + ' file to ' + DEST, (done) => { + expect.assertions(2); + const options = { + src: path.resolve(SRC), + dest: path.resolve(DEST), + }; + assertYamlTransformSuccess(options, middlewareFunc, done); + }); + }); + + describe('Testing Transformer transforming from JS to JS', () => { + const SRC = './test/data/test-file.js'; + const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-js-js.js'; + + it('should store ' + SRC + ' file to ' + DEST, async () => { + expect.assertions(2); + const options = { + src: path.resolve(SRC), + dest: path.resolve(DEST), + }; + await assertTransformSuccess(options, middlewareFunc); + }); + }); +}); diff --git a/test/unit/test-validator.js b/test/unit/test-validator.js new file mode 100644 index 0000000..2879303 --- /dev/null +++ b/test/unit/test-validator.js @@ -0,0 +1,34 @@ +import Validator from '../../lib/validator'; +import { logger } from '../logger'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +/** + * @classdesc This unit test suite checks validity and correctness. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - validator - ', () => { + /** + * The testee. + * @type {Validator} + */ + let validator; + + beforeAll(() => { + validator = new Validator(logger); + }); + + const nonStringIdentifier = {}; + it('should validate non-string identifier \'' + JSON.toString(nonStringIdentifier) + '\' to false', () => + expect(validator.validateIdentifier(nonStringIdentifier)).toBeFalsy() + ); + + const invalidIdentifier = '#3/-'; + it('should validate invalid identifier \'' + invalidIdentifier + '\' to false', () => + expect(validator.validateIdentifier(invalidIdentifier)).toBeFalsy() + ); + + const validIdentifier = 'bar'; + it('should validate \'' + validIdentifier + '\' identifier to true', () => + expect(validator.validateIdentifier(validIdentifier)).toBeTruthy() + ); +}); diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js new file mode 100644 index 0000000..286991f --- /dev/null +++ b/test/unit/test-writer.js @@ -0,0 +1,423 @@ +import promisify from 'promisify-es6'; +import fsExtra from 'fs-extra'; +import YAMLException from 'js-yaml/lib/js-yaml/exception'; +import fs from 'fs'; +import os from 'os'; +import stream from 'stream'; +import { Writer } from '../../index'; +import { logger } from '../logger'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +const fsPromised = promisify(fs); + +/** + * @classdesc This unit test suite checks the validity and correctness of {@link Writer} class. + */ +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { + /** + * Samlpe JSON content used in tests. + * + * @type {{test: string}} + * @constant + * @private + */ + const JSON_CONTENT = { test: 'value' }; + + /** + * Temporary base dir for writer test output. + * @type {string} + * @constant + * @private + */ + const WRITER_TEST_BASE_DIR = './test/tmp/writer'; + + /** + * The testee. + * @type {Writer} + */ + let writer; + + beforeAll(() => { + fsExtra.ensureDirSync(WRITER_TEST_BASE_DIR); + fsExtra.emptyDirSync(WRITER_TEST_BASE_DIR); + writer = new Writer(logger); + }); + + /** + * Asserts that the given `dest` is an existing file. + * + * @param {string} dest - File destination to assert. + * @returns {Error} If `dest` does not exist and `done` is not passed. + * @private + */ + const expectDestFileExists = async (dest) => { + try { + const stats = await fsPromised.stat(dest); + expect(stats.isFile()).toBeTruthy(); + } catch (err) { + if (err.code === 'ENOENT') { + err.message = 'The input file \'' + dest + '\' does not exists or is not accessible, cause: ' + err.message; + } else { + err.message = 'Some error occurred while accessing input file \'' + dest + '\': ' + + err.code + ', ' + err.message; + } + throw err; + } + }; + + /** + * Asserts that the given file `dest` does not exist. + * + * @param {string} dest - File destination to assert. + * @returns {Error} If `dest` does not exist and `done` is not passed. + * @private + */ + const expectDestFileDoesNotExist = async (dest) => { + // check for existing source file + let statErr; + try { + await fsPromised.stat(dest); // TODO could we check this in Async mode? + statErr = new Error('Error expected when checking file = ' + dest); + } catch (err) { + logger.info('Error is EXPECTED: ' + err.stack); + expect(err).toBeDefined(); + expect(err.code).toBe('ENOENT'); + } + if (statErr) { + throw statErr; + } + }; + + /** + * Assert an `Error` for a given writer function. + * + * @param {Object} json - The json to write. + * @param {Object} options - The options which potentially produce the error. + * @param {Function} writerFunc - The function to call for assertion. + * @param {Error} [expectedErrorType=Error] - The error type to expect. + * @private + */ + const expectWriterError = (json, options, writerFunc, expectedErrorType = Error) => { + expect.assertions(1); + return expect(writerFunc(json, options)).rejects.toBeInstanceOf(expectedErrorType); + }; + + describe('Testing Writer.writeJs(...)', () => { + it('should write JS to file', async () => { + expect.assertions(2); + const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.js'; + const msg = await writer.writeJs(JSON_CONTENT, { dest: file }); + expect(msg).toBeDefined(); + await expectDestFileExists(file); + }); + + it('should write JS to stream', async () => { + expect.assertions(2); + const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.js'; + const msg = writer.writeJs(JSON_CONTENT, { dest: fs.createWriteStream(file) }); + expect(msg).toBeDefined(); + await expectDestFileExists(file); + }); + + it('should write JS to stream with exports identifier', async () => { + expect.assertions(4); + const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-exports-identifier.js'; + const options = { + dest: fs.createWriteStream(file), + exports: 'test', + }; + const msg = writer.writeJs(JSON_CONTENT, options); + expect(msg).toBeDefined(); + await expectDestFileExists(file); + // eslint-disable-next-line import/no-unresolved, global-require + const json = require('../tmp/writer/test-data-by-js-stream-with-exports-identifier.js').test; + expect(json.test).toBeDefined(); + expect(json.test).toBe('value'); + }); + + it('should write JS to file and fail by invalid exports identifier (\'#3/-\')', () => { + const options = { + dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js', + exports: '#3/-', + }; + return expectWriterError(JSON_CONTENT, options, writer.writeJs); + }); + + it('should write JS to stream and fail by invalid exports identifier (\'#3/-\')', () => { + const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js'; + const options = { + dest: fs.createWriteStream(file), + exports: '#3/-' + }; + return expectWriterError(JSON_CONTENT, options, writer.writeJs); + }); + + it('should write JS to stream and fail by invalid exports identifier (\'if\')', () => { + const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js'; + const options = { + dest: fs.createWriteStream(file), + exports: 'if' + }; + return expectWriterError(JSON_CONTENT, options, writer.writeJs); + }); + + it('should write JS to stream and fail by provoked error', () => { + const errorThrowingStream = new stream.Writable(); + // eslint-disable-next-line no-underscore-dangle + errorThrowingStream._write = (chunk, encoding, done) => { + logger.info('stream emitting Error now'); + this.emit('error', new Error('Dummy Error')); + done(); + }; + return expectWriterError(JSON_CONTENT, { dest: errorThrowingStream }, writer.writeJs); + }); + + it('should write JS to JS object', async () => { + expect.assertions(4); + const options = { dest: {} }; + const msg = await writer.writeJs(JSON_CONTENT, options); + expect(msg).toBeDefined(); + expect(options.dest).toBeDefined(); + expect(Object.prototype.hasOwnProperty.call(options.dest, 'test')).toBeTruthy(); + expect(options.dest.test).toBe('value'); + }); + + it('should write JS to JS object with options.exports == \'\'', async () => { + expect.assertions(4); + const options = { + dest: {}, + exports: '' + }; + const msg = await writer.writeJs(JSON_CONTENT, options); + expect(msg).toBeDefined(); + expect(options.dest).toBeDefined(); + expect(Object.prototype.hasOwnProperty.call(options.dest, 'test')).toBeTruthy(); + expect(options.dest.test).toBe('value'); + }); + + const exports = 'foo'; + it('should write JS to JS object with options.exports == \'' + exports + '\'', async () => { + expect.assertions(5); + const options = { + dest: {}, + exports + }; + const msg = await writer.writeJs(JSON_CONTENT, options); + expect(msg).toBeDefined(); + expect(options.dest).toBeDefined(); + expect(Object.prototype.hasOwnProperty.call(options.dest, exports)).toBeTruthy(); + expect(Object.prototype.hasOwnProperty.call(options.dest[exports], 'test')).toBeTruthy(); + expect(options.dest[exports].test).toBe('value'); + }); + + const invalidIdentifier = '#3/-'; + it('should reject write JS with Error on invalid identifier for options.exports: ' + invalidIdentifier, () => { + const options = { + dest: {}, + exports: invalidIdentifier + }; + return expectWriterError(JSON_CONTENT, options, writer.writeJs); + }); + + it('should reject write JS with Error on missing destination', () => { + return expectWriterError(JSON_CONTENT, {}, writer.writeJs); + }); + + it('should reject write JS to file by invalid file path', (done) => { + const options = { + dest: WRITER_TEST_BASE_DIR + '/<>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/test-data-by-js-to-file.js' + }; + + writer.writeJs(JSON_CONTENT, options) + .then((msg) => { + expect(msg).toBeDefined(); + done(new Error('Error expected')); + }) + .catch((err) => { + logger.info('EXPECTED ERROR: ' + (err.stack ? err.stack : err)); + expect(err).toBeDefined(); + // NOTE: here wo do not get an Error type but simply an Object: + // { + // "errno": -63, + // "code": "ENAMETOOLONG", + // "syscall": "mkdir", + // "path": "..." + // } + expect(typeof err === 'object').toBeTruthy(); // TODO instanceOf ? + expect(err.code).toBe('ENAMETOOLONG'); + done(); + }); + }, 10000); // TODO timeout needed? + }); + + describe('Testing Writer.writeJson(...)', () => { + it('should write JSON to file', async () => { + expect.assertions(2); + const options = { + src: JSON_CONTENT, + dest: WRITER_TEST_BASE_DIR + '/test-data-by-json-to-file.json' + }; + const msg = await writer.writeJson(JSON_CONTENT, options); + expect(msg).toBeDefined(); + await expectDestFileExists(options.dest); + }); + + it('should write JSON to stream', async () => { + expect.assertions(2); + const file = WRITER_TEST_BASE_DIR + '/test-data-by-json-stream.json'; + const msg = await writer.writeJson(JSON_CONTENT, { dest: fs.createWriteStream(file) }); + expect(msg).toBeDefined(); + await expectDestFileExists(file); + }); + + it('should write JS to JS object', async () => { + expect.assertions(4); + const options = { dest: {} }; + const msg = await writer.writeJson(JSON_CONTENT, options); + expect(msg).toBeDefined(); + expect(options.dest).toBeDefined(); + const result = JSON.parse(options.dest); + expect(Object.prototype.hasOwnProperty.call(result, 'test')).toBeDefined(); + expect(result.test).toBe('value'); + }); + + it('should reject with Error on missing destination', () => { + return expectWriterError(JSON_CONTENT, {}, writer.writeJson); + }); + }); + + describe('Testing Writer.writeYaml(...)', () => { + it('should write YAML to file', async () => { + expect.assertions(2); + const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.yaml'; + const msg = await writer.writeYaml(JSON_CONTENT, { dest: file }); + expect(msg).toBeDefined(); + await expectDestFileExists(file); + }); + + it('should write YAML to stream', async () => { + expect.assertions(2); + const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.yaml'; + const msg = await writer.writeYaml(JSON_CONTENT, { dest: fs.createWriteStream(file) }); + expect(msg).toBeDefined(); + await expectDestFileExists(file); + }); + + it('should write stringified YAML to JS object', async () => { + expect.assertions(3); + const options = { dest: {} }; + const msg = await writer.writeYaml(JSON_CONTENT, options); + expect(msg).toBeDefined(); + expect(options.dest).toBeDefined(); + const key = Object.keys(JSON_CONTENT)[0]; + expect(options.dest).toBe(key + ': ' + JSON_CONTENT[key] + os.EOL); + }); + + it('should reject with Error by invalid src object', () => { + const invalidYamlJson = () => {}; + return expectWriterError(invalidYamlJson, + { dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file-invalid.yaml' }, + writer.writeYaml, YAMLException); + }); + + it('should reject with Error on missing destination', () => { + return expectWriterError(JSON_CONTENT, { }, writer.writeYaml); + }); + }); + + describe('Testing force overwrite file', () => { + it('should reject when options.dest is a directory', () => { + return expectWriterError(JSON_CONTENT, { dest: './test/data' }, writer.writeYaml); + }); + + it('should write YAML to stream, overwrite on 2nd write, ' + + 'don\'t overwrite on 3rd write and overwrite on 4th write', async () => { + // expect.assertions(11); + const dest = WRITER_TEST_BASE_DIR + '/test-data-file-overwriting.yaml'; + let options = { + indent: 4, + dest, + }; + + let index = 1; + const asyncFunctions = [ + async () => { + const msg = await writer.writeYaml(JSON_CONTENT, options); + expect(msg).toBeDefined(); + await expectDestFileExists(dest); + return 'overwrite test #1 should initially write YAML to file \'' + dest + '\''; + }, + async () => { + options = { + indent: 4, + dest, + force: true + }; + const msg = await writer.writeYaml(JSON_CONTENT, options); + expect(msg).toBeDefined(); + await expectDestFileExists(dest); + await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); + return 'overwrite test #2 should overwrite existing YAML file \'' + dest + '\''; + }, + async () => { + const msg = await writer.writeYaml(JSON_CONTENT, options); + expect(msg).toBeDefined(); + await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); + return 'overwrite test #3 shouldn\'t overwrite existing YAML file \'' + dest + + '\', but write new file \'' + WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml\''; + }, + async () => { + options = { + indent: 4, + dest, + force: false + }; + const msg = await writer.writeYaml(JSON_CONTENT, options); + expect(msg).toBeDefined(); + await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); + //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); + return 'overwrite test #4 should overwrite existing YAML file \'' + dest + '\''; + }, + async () => { + options = { + indent: 4, + dest, + }; + const msg = await writer.writeYaml(JSON_CONTENT, options); + expect(msg).toBeDefined(); + await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); + //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); + return 'overwrite test #5 shouldn\'t overwrite existing YAML file \'' + dest + + '\' and \'' + WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml\', but write ' + + 'new file \'' + WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml\''; + } + ]; + await asyncFunctions.reduce((p, fn, idx) => { + return p.then((msg) => { + if (msg) { + logger.info('testing overwrite #' + (idx + 1) + '/' + asyncFunctions.length + ': ' + msg); + } else { + logger.info('testing overwrite #' + (idx) + '/' + asyncFunctions.length + ': started!'); + } + return fn().then(result => result).catch(err => err.message); + }); + }, Promise.resolve()); + + // .reduce((p, fn) => { + // return p.then(async (msg) => { + // if (msg) { + // logger.info('testing overwrite #' + (index + 1) + '/' + asyncFunctions.length + ': ' + msg); + // } + // return await fn(); + // }); + // }, Promise.resolve()); + + // await Promise.all(, (value, index, length) => { + // return value().then(msg => logger.info('testing overwrite #' + (index + 1) + '/' + asyncFunctions.length + ': ' + msg)); + // }).then(); + }, 5000); // we have to set higher timeout here because some travis jobs failed due to 2 sec timeout! + }); +}); From e05625d460b34555d52da658973f9c4c640a8c93 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 2 Jun 2017 09:31:57 +0200 Subject: [PATCH 02/58] Intermediate impl state --- .gitignore | 1 + .jestrc.js | 29 +- docs/CHANGELOG.md | 33 +- docs/CONTRIBUTING.md | 4 +- docs/USAGE.md | 166 +++-- index.js | 9 +- lib/log-wrapper.js | 138 ----- lib/middleware.js | 83 --- lib/options-handler.js | 565 ------------------ lib/options-schema.js | 0 lib/reader.js | 323 ---------- lib/transformer.js | 421 ------------- lib/type-definitions.js | 45 -- lib/validator.js | 82 --- lib/writer.js | 509 ---------------- package.json | 6 +- {lib => src}/constants.js | 152 ++--- src/debug-log.js | 6 + src/middleware.js | 71 +++ src/reader.js | 273 +++++++++ src/transformer.js | 389 ++++++++++++ src/type-definitions.js | 62 ++ src/validation/joi-extensions-file-helper.js | 28 + .../joi-extensions-identifier-helper.js | 32 + src/validation/joi-extensions.js | 54 ++ src/validation/options-schema-helper.js | 108 ++++ src/validation/options-schema.js | 145 +++++ src/writer.js | 441 ++++++++++++++ test-data-by-js-to-file.json | 3 + test.js | 36 ++ test/data/test-data | 3 + test/data/test-data-json | 3 + test/data/test-data-yaml | 1 + test/unit/test-index.js | 58 +- test/unit/test-log-wrapper.js | 236 -------- test/unit/test-middleware.js | 26 +- test/unit/test-options-handler.js | 338 ----------- test/unit/test-reader.js | 142 +++-- test/unit/test-transformer.js | 40 +- test/unit/test-validator.js | 34 -- test/unit/test-writer.js | 118 ++-- .../test-joi-extensions-file-helper.js | 27 + .../test-joi-extensions-identifier-helper.js | 24 + .../validation/test-options-schema-helper.js | 35 ++ test/unit/validation/test-options-schema.js | 423 +++++++++++++ 45 files changed, 2585 insertions(+), 3137 deletions(-) delete mode 100644 lib/log-wrapper.js delete mode 100644 lib/middleware.js delete mode 100644 lib/options-handler.js delete mode 100644 lib/options-schema.js delete mode 100644 lib/reader.js delete mode 100644 lib/transformer.js delete mode 100644 lib/type-definitions.js delete mode 100644 lib/validator.js delete mode 100644 lib/writer.js rename {lib => src}/constants.js (54%) create mode 100644 src/debug-log.js create mode 100644 src/middleware.js create mode 100644 src/reader.js create mode 100644 src/transformer.js create mode 100644 src/type-definitions.js create mode 100644 src/validation/joi-extensions-file-helper.js create mode 100644 src/validation/joi-extensions-identifier-helper.js create mode 100644 src/validation/joi-extensions.js create mode 100644 src/validation/options-schema-helper.js create mode 100644 src/validation/options-schema.js create mode 100644 src/writer.js create mode 100644 test-data-by-js-to-file.json create mode 100644 test.js create mode 100644 test/data/test-data create mode 100644 test/data/test-data-json create mode 100644 test/data/test-data-yaml delete mode 100644 test/unit/test-log-wrapper.js delete mode 100644 test/unit/test-options-handler.js delete mode 100644 test/unit/test-validator.js create mode 100644 test/unit/validation/test-joi-extensions-file-helper.js create mode 100644 test/unit/validation/test-joi-extensions-identifier-helper.js create mode 100644 test/unit/validation/test-options-schema-helper.js create mode 100644 test/unit/validation/test-options-schema.js diff --git a/.gitignore b/.gitignore index a93d8c0..42aa08e 100755 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ npm-debug.log coverage nohup.out /jy-transform.wiki/ +/lib diff --git a/.jestrc.js b/.jestrc.js index 7eb6f79..511463b 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -2,7 +2,7 @@ module.exports = { // this is a workaround that jest does not create a jest_0/ folder in the project root dir! cacheDirectory: '/tmp/jest-cache', collectCoverageFrom: [ - 'lib/**/*.js', + //'lib/**/*.js', 'src/**/*.js', 'index.js' ], @@ -17,16 +17,23 @@ module.exports = { } }, mapCoverage: true, - // testMatch: [ - // // '**/test/**/*.js!**/test/functional/util/**', - // '**/test/unit/*.js', - // // '**/test/test-log-wrapper.js', - // // '**/test/test-middleware.js', - // // '**/test/test-index.js', - // //'/*.js!**/test/functional/util/**', - // // '!**/test/*.js', - // ], - testRegex: '\\/test\\/unit\\/.*|\/\\.js?$/', + testMatch: [ + '**/test/unit/validation/test-joi-extensions-file-helper.js', + '**/test/unit/validation/test-joi-extensions-identifier-helper.js', + //'**/test/unit/test-transformer.js', + // '**/test/unit/test-index.js', + '**/test/unit/test-reader.js', + // '**/test/unit/test-writer.js', + '**/test/unit/validation/test-options-schema.js', + //'**/test/unit/**/*.js', + + // '**/test/test-log-wrapper.js', + // '**/test/test-middleware.js', + // '**/test/test-index.js', + //'/*.js!**/test/functional/util/**', + // '!**/test/*.js', + ], + // testRegex: '\\/test\\/unit\\/.*|\/\\.js?$/', testEnvironment: 'node', testPathIgnorePatterns: [ // '/test/data/.*', diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4304e90..bd9a7c1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,10 +1,33 @@ #### v3.0.0 -- Removal of _development_ branch -- Switch to babel -- Use native promises instead of bluebird -- Use named import only for all classes (i.e. also for `Transformer`) -- Tests written in Jest (got rid of _assert_, _mocha_ and _istanbul_) +- **CLI & API Changes (Backwards Imcompatible!):** + - Removed support for Node.js < v4.0 + - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community + +- **API Changes only (Backwards Imcompatible!):** + - Prototype removal from `Transformer`, `Reader` and `Writer`, turning it to simple exports of functions + - Easier usage by using named imports only for all classes (i.e. also for `Transformer`) + - The formerly exported `middleware` is not public anymore + - The `options.imports/exports` are not allowed to be empty strings anymore (just leave it out) + - The exported constants `YAML`, `JS` and `JSON` (usable for `options.origin/target`) are renamed respectively to `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` + - `options.dest` is required for `Transfomer` and `Writer` on API usage + - Removal of `LogWrapper` prevents from injecting a logger into `Transformer`, `Reader` and `Writer` + - Instead of a message success string the `Transformer.transform` and all `Writer.writeXXX` functions return now + the `dest` result object passed in with `options.dest` because during + the validation process the framework will decouple `dest` from the reference of the `options` by creating a + new options object (in case of `Stream.Writable` and it is the same object as passed in as options.dest but it + matters in case of `Object` where the altered object is returned) + +- Internal Changes & Improvements: + - Removal of _development_ branch + - Usage of [babel](https://babeljs.io/) and therefore most modern language features + - Code base could be shrinked and readabilty improved + - Usage of _native promises_ instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html) + - Tests re-written in [Jest](https://facebook.github.io/jest) (could get rid of [assert](https://github.com/defunctzombie/commonjs-assert), + [mocha](https://mochajs.org/) and [istanbul](https://github.com/gotwarlost/istanbul)) + - Add travis build for Node.js v8.x + - Remove travis build for Node.js < v4.x + - Removal of `OptionsHandler` and `Validator` (replaced validation stuff by [joi](https://github.com/hapijs/joi/tree/v10.5.0)) #### v2.0.1 diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 90c33fe..a74979e 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -16,10 +16,10 @@ When contributing as coder, please take care of the following conventions: ```$ git checkout -b 'feature/#19_...'``` when using git shell command. - Keep code coverage high (> 95%). -- Doc everything with [JSDoc](http://usejsdoc.org/) and document concepts in +- Document everything with [JSDoc](http://usejsdoc.org/) and describe concepts in [README.md](https://github.com/deadratfink/jy-transform/blob/development/README.md) or [Wiki](https://github.com/deadratfink/jy-transform/wiki). -- Coding style is defined by _.eslintrc.js_. +- Coding style is defined by _.eslintrc.js_ and _.editorconfig_. - File names should be lower-case with hyphens as divider, e.g. _options-handler.js_. - Markdown documentation files should be upper-case with _.md_ as extension, placed in _./docs_, e.g. _USAGE.md_. The _README.md_ is build up by these files concatenated diff --git a/docs/USAGE.md b/docs/USAGE.md index 2bbc223..e97a99b 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -172,7 +172,7 @@ The OPTIONS are more formally defined in the following table: | --- | --- | --- | --- | --- | --- | | `-o` | `--origin` | string of: [ _js_ | _json_ | _yaml_ ] | The transformation origin type. | if not given, the type is tried to be inferred from the extension of source path, else it is _yaml_ | no | | `-t` | `--target` | string of: [ _js_ | _json_ | _yaml_ ] | The transformation target type. | if not given, the type is tried to be inferred from the extension of destination path, else it is _js_ | no | -| `-i` | `--indent` | integer
[ 1 - 8 ]
| The code indention used in destination files. | 4 | no | +| `-i` | `--indent` | integer
[ 1 - 8 ]
| The code indention used in destination files. | 2 | no | | `-f` | `--force` | n/a | Force overwriting of existing output files on write phase. When files are not overwritten (which is default), then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | | `-m` | `--imports` | string | Define a 'module.exports[.identifier] = ' identifier (to read from JS _source_ file only, must be a valid JS identifier!) | _undefined_ | no | | `-x` | `--exports` | string | Define a 'module.exports[.identifier] = ' identifier (for usage in JS _destination_ file only, must be a valid JS identifier!) | _undefined_ | no | @@ -205,11 +205,11 @@ Then we can transform it to a JSON content as _foo.json_ file: simply by using this command: ``` -$ jyt foo.yaml -t json -i 2 +$ jyt foo.yaml -t json -i 4 ``` In this example we have overwritten the standard target type (which is `js`) -and applying an indent of 2 SPACEs instead of the default (4). As default the output +and applying an indent of 4 SPACEs instead of the default (2). As default the output file _foo.json_ is written relative to the input file (by omitting the `dest` option here). @@ -218,7 +218,7 @@ default `js` would have been applied! If the source would have been a `js` type like ``` -$ jyt foo.js -t json -i 2 +$ jyt foo.js -t json -i 4 ``` then the `js` value for `origin` is automatically inferred from file extension. @@ -227,12 +227,12 @@ Accordingly, this is also true for the `target` option. #### Example: JSON ⇒ JS The command ``` -$ jyt foo.json -i 2 +$ jyt foo.json -i 4 ``` results in _foo.js_: ```javascript module.exports = { - foo: "bar" + foo: "bar" } ``` @@ -272,11 +272,11 @@ In this this example we have a _foo.js_ file exporting _two_ objects: ```javascript module.exports.foo = { - foo: 'bar' + foo: 'bar' }; module.exports.bar = { - bar: 'foo' + bar: 'foo' }; ``` but you want to convert only `bar` object, then call: @@ -292,14 +292,14 @@ bar: foo ```javascript var fooBar = { - foo: 'bar', - bar: 'foo' + foo: 'bar', + bar: 'foo' }; var options = { - src: fooBar, - dest: {}, - exports: 'bar' + src: fooBar, + dest: {}, + exports: 'bar' }; //...transform @@ -309,8 +309,8 @@ The transformation will result in this in-memory object: ```javascript bar: { - foo: 'bar', - bar: 'foo' + foo: 'bar', + bar: 'foo' } ``` Of course, as sub-node of `options.dest`. @@ -329,7 +329,7 @@ $ jyt foo.yaml foobar.js -x foobar This generates the following output in JS file using `foobar` as identifier: ```javascript module.exports.foobar = { - foo: "bar" + foo: "bar" } ``` @@ -375,16 +375,19 @@ specify the origin or target type! Since the usage on CLI is a 2-step process: - 1. Read from source file to JS object ⇒ 2. Write out (maybe to other type) + 1. Read from source file to JS object ⇒ + 2. Write out (maybe to other type) the direct API calls additionally provide the usage of a _middleware_ function where you can alter the input JS object before it is written and therefore, which turns this into a 3-step process: - 1. Read from source ⇒ 2. Alter the JS object ⇒ 3. Write out (maybe to other type) + 1. Read from source ⇒ + 2. Alter the JS object ⇒ + 3. Write out (maybe to other type) For more details about this and all the functions provided by this module please refer to the -[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v2). +[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v3). The `origin` and `target` type inference is also standard for the API level. @@ -394,52 +397,52 @@ The `Transformer` exposes the following function which takes besides an (optiona `middleware` function the necessary `options` for the transformation: ```javascript -function transform(options, middleware) { - //... -} +async function transform(options, middleware) ``` +#### Transformer Options + The `options` object has to follow this key-values table: | Option | Type | Description | Default | Required | | --- | --- | --- | --- | --- | -| origin | string | The origin type. | If not given, the type is tried to be inferred from the extension of source path, else it is _yaml_. | no | -| target | string | The target type. | If not given, the type is tried to be inferred from the extension of destination path, else it is _js_ | no | -| src | string | Readable | object | The source information object: `string` is used as file path, `Readable` stream provides a stringified source and `object` is used as direct JS source. | - | yes | -| dest | string | Writable | object | The destination information object: `string` is used as file path, `Writable` stream writes a stringified source and `object` is used as direct JS object for assignment. | The output file is stored relative to the input file (same base name but with another extension if type differs). If input and output type are the same then the file overwriting is handled depending on the 'force' value! | no | -| indent | number | The indention in files. | 4 | no | -| force | boolean | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | -| imports | string | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | -| exports | string | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | +| `origin` | _String_ | The origin type. | If not given, the type is tried to be inferred from the extension of source path, else it is _yaml_. | no | +| `target` | _String_ | The target type. | If not given, the type is tried to be inferred from the extension of destination path, else it is _js_. | no | +| `src` | [ _String_ | _Stream.Readable_ | _Object_ ] | The source information object: `String` is used as file path, `Stream.Readable` provides a stringified source and `object` is used as direct JS source. | - | yes | +| `dest` | [ _String_ | _Stream.Writable_ | _Object_ ] | The destination information object: `String` is used as file path, `Stream.Writable` writes a stringified source and `object` is used for direct JS object assignment of the (stringified or JS object) source. If a string is set as file path then the output and if input and output file path are the same, then the file overwriting is handled depending on the `force` value! | - | yes | +| `indent` | _Number_ | The indention in files. | 2 | no | +| `force` | _Boolean_ | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | +| `imports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | +| `exports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | -**NOTE:** an invalid indention setting (1 > indent > 8) does not raise an error but a default of 4 SPACEs is applied instead. +**NOTE:** an invalid indention setting (1 > indent > 8) does not raise an error but a default of 2 SPACEs is applied instead. #### Example ```javascript var options = { - origin: 'json', - target: 'yaml', - src: 'foo.json', - dest: './foo/bar.yaml', - indent: 2 + origin: 'json', + target: 'yaml', + src: 'foo.json', + dest: './foo/bar.yaml', + indent: 4 } ``` ### Using Middleware The `middleware` is optional but if provided it must be of type `Function` and -a [Promise](http://bluebirdjs.com/docs/api-reference.html). One of the easiest -ones is the identity function +a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). +One of the easiest ones is the identity function _f(data) → data_ -which could be expressed as -[Promise](http://bluebirdjs.com/docs/api-reference.html) function as follows: +which could be expressed as [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) +function as follows: ```javascript -var identity = function (data) { - return Promise.resolve(data); +const identity = async (data) => { + return data; } ``` @@ -455,28 +458,29 @@ object as input: foo: old bar ``` -Applying this [Promise](http://bluebirdjs.com/docs/api-reference.html) as middleware +Applying this [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) +as middleware ```javascript -var middleware = function (data) { - data.foo = 'new bar'; - return Promise.resolve(data); -} +const middleware = async (data) => { + data.foo = 'new bar'; + return data; +}; -transformer.transform(options, middleware) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); +transform(options, middleware) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); ``` will result in such JSON file: ```json { - "foo": "new bar" + "foo": "new bar" } ``` @@ -492,35 +496,31 @@ given (initially empty) JS object. **NOTE:** each of them has to resolve with the `data` object! - ```javascript -function key1(data) { - objectPath.set(data, 'key1', 'value1'); - return Promise.resolve(data); -} +const key1 = async (data) => { + objectPath.set(data, 'key1', 'value1'); + return data; +}; -function key2(data) { - objectPath.set(data, 'key2', 'value2'); - return Promise.resolve(data); -} +const key2 = async (data) => { + objectPath.set(data, 'key2', 'value2'); + return data; +}; -function key3(data) { +const key3 = async (data) => { objectPath.set(data, 'key3', 'value3'); - return Promise.resolve(data); -} + return data; +}; ``` These can be collected by different aggregation or composition functions of the underlying -Promise framework, e.g. using the [`Promise.all([...])`](http://bluebirdjs.com/docs/api/promise.all.html) +Promise framework, e.g. using the [`Promise.all([...])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) function. This one can collect all three functions above and ensure their proper subsequent execution: - ```javascript -var middleware = function (data) { - return Promise.all([key1(data), key2(data), key3(data)]) - .then(function(result) { - return Promise.resolve(result[result.length - 1]); - }); +const middleware = (data) => { + return Promise.all([key1(data), key2(data), key3(data)]) + .then(result => result[result.length - 1]); }; var logger = new Logger(); @@ -529,13 +529,9 @@ var options = { src: {} }; -return transformer.transform(options, middleware) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); +return transform(options, middleware) + .then(msg => logger.info(msg)) + .catch(err => logger.error(err.stack)); ``` Then the result in the `middleware` function can be retrieved from the returned @@ -546,9 +542,9 @@ From our example above it would be result in this object ```javascript { - key1: 'value1', - key2: 'value2', - key3: 'value3' + key1: 'value1', + key2: 'value2', + key3: 'value3' } ``` @@ -594,7 +590,7 @@ Anyway, there are some fallbacks if a level is not supported: # API Reference For more details on how to use the API, please refer to the -[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v2) +[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v3) wiki which describes the full API and provides more examples. # Contributing diff --git a/index.js b/index.js index c0df3b8..b318f5e 100755 --- a/index.js +++ b/index.js @@ -1,8 +1,7 @@ module.exports = { - Transformer: require('./lib/transformer.js'), - Reader: require('./lib/reader.js'), - Writer: require('./lib/writer.js'), - Constants: require('./lib/constants.js'), - Middleware: require('./lib/middleware.js'), + Transformer: require('./src/transformer.js').default, + Reader: require('./src/reader.js').default, + Writer: require('./src/writer.js').default, + Constants: require('./src/constants.js'), }; diff --git a/lib/log-wrapper.js b/lib/log-wrapper.js deleted file mode 100644 index 909b690..0000000 --- a/lib/log-wrapper.js +++ /dev/null @@ -1,138 +0,0 @@ -var Promise = require('bluebird'); - -// //////////////////////////////////////////////////////////////////////////// -// CONSTRUCTOR -// //////////////////////////////////////////////////////////////////////////// - -/** - * Constructs the `LogWrapper`. - * - * @param {(logger|cli|console)} [logger=console] - Logger object. - * @returns {LogWrapper} - The instance. - * @constructor - * @example - * var logger = ...; - * var logWrapper = new LogWrapper(logger); - * @class Class which defines a `logger` wrapper usable in this module. - *

- * **NOTE:** This class is not to be intended to be called from - * outside this module! - */ -function LogWrapper(logger) { - - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = logger || console; -} - -LogWrapper.prototype = {}; -LogWrapper.prototype.constructor = LogWrapper; - -// //////////////////////////////////////////////////////////////////////////// -// LOGGER METHODS -// //////////////////////////////////////////////////////////////////////////// - -/** - * Log the options with INFO level. - * - * @param {string} msg - The message to log. - * @example - * var logger = ...; - * var logWrapper = new LogWrapper(logger); - * var msg = '...'; - * logWrapper.info(msg); - * @public - */ -LogWrapper.prototype.info = function (msg) { - this.logInstance.info(msg); -}; - -/** - * Log the options with DEBUG level (if logger supports it, else with INFO). - * - * @param {string} msg - The message to log. - * @example - * var logger = ...; - * var logWrapper = new LogWrapper(logger); - * var msg = '...'; - * logWrapper.debug(msg); - * @public - */ -LogWrapper.prototype.debug = function (msg) { - if (this.logInstance.debug && typeof this.logInstance.debug === 'function') { - this.logInstance.debug(msg); - } else { - this.info(msg); - } -}; - -/** - * Log the options with TRACE level (if logger supports it, else with DEBUG). - * - * @param {string} msg - The message to log. - * @example - * var logger = ...; - * var logWrapper = new LogWrapper(logger); - * var msg = '...'; - * logWrapper.trace(msg); - * @public - * @see {@link #debug} - */ -LogWrapper.prototype.trace = function (msg) { - if (this.logInstance.trace && typeof this.logInstance.trace === 'function') { - this.logInstance.trace(msg); - } else { - this.debug(msg); - } -}; - -/** - * Log the options with ERROR level. - * - * @param {string} msg - The message to log. - * @example - * var logger = ...; - * var logWrapper = new LogWrapper(logger); - * var msg = '...'; - * logWrapper.error(msg); - * @public - */ -LogWrapper.prototype.error = function (msg) { - this.logInstance.error(msg); -}; - -/** - * Log the options with INFO level. - * - * @param {Options} options - The properties to log with INFO. - * @returns A Promise containing the passed `options` object. - * @example - * var logger = ...; - * var logWrapper = new LogWrapper(logger); - * var options = { - * ... - * }; - * logWrapper.verboseOptions(options) - * .then(function (options) { - * ... - * }); - * @public - */ -LogWrapper.prototype.verboseOptions = function (options) { - var self = this; - return Promise.resolve() - .then(function () { - self.info('origin: ' + options.origin); - self.info('target: ' + options.target); - self.info('src: ' + options.src); - self.info('dest: ' + options.dest); - self.info('indent: ' + options.indent); - return options; - }); -}; - -exports = module.exports = LogWrapper; diff --git a/lib/middleware.js b/lib/middleware.js deleted file mode 100644 index 2cbc39d..0000000 --- a/lib/middleware.js +++ /dev/null @@ -1,83 +0,0 @@ -var Promise = require('bluebird'); - -// //////////////////////////////////////////////////////////////////////////// -// CONSTRUCTOR -// //////////////////////////////////////////////////////////////////////////// - -/** - * Constructs the `Middleware`. - * - * @returns {Middleware} - The instance. - * @constructor - * @class Class which defines middleware Promises usable in or with this module. - * @example - * var middleware = require('./lib/middleware.js'); - */ -function Middleware() { -} - -Middleware.prototype.constructor = Middleware; -module.exports = new Middleware(); - -/** - * Promise which reflects the identity of passed JSON: `f(object) → object`. - * - * @param {object} object - The JS object which is returned in Promise. - * @returns {Promise.} - A Promise resolving the passed `json` object. - * @private - */ -function identity(object) { - return Promise.resolve(object); -} - -/** - * Middleware Promise which reflects the identity of passed JSON: `f(object) → object`. - * - * @param {object} object - The object which is returned in Promise. - * @returns {Promise.} - A Promise resolving the passed `json` object. - * @example - * var middleware = require('./lib/middleware.js'); - * var identityMiddleware = middleware.identityMiddleware; - * transformer.transform(options, identityMiddleware) - * .then(function(object) { - * ... - * }): - * @public - */ -Middleware.prototype.identityMiddleware = identity; - -/** - * Ensure that the given middleware Promise is a function if set. - * If not set a new JSON 'identity' Promise is returned which simply passes - * a JSON object. - * - * @param {Function} middleware - This middleware Promise can be used to intercept - * the JSON object for altering he passed JSON, the function signature is: - * - * ``` - * function(object) - * ``` - * - * **NOTE:** the Promise has to return the processed JSON! - * @returns {Function} - The given middleware Promise or a new JSON 'identity' middleware Promise function. - * @throws {TypeError} - Will throw this error when the passed `middleware` is not type of `Function`. - * @example - * var middleware = require('./lib/middleware.js'); - * var myMiddleware = function(object) { - * //... - * }; - * transformer.transform(options, middleware.ensureMiddleware(myMiddleware)) - * .then(function(object) { - * //... - * }): - * @public - */ -Middleware.prototype.ensureMiddleware = function (middleware) { - if (middleware !== undefined && (typeof middleware !== 'function')) { - throw new TypeError('The provided middleware is not a Function type'); - } - if (!middleware) { - middleware = identity; - } - return middleware; -}; diff --git a/lib/options-handler.js b/lib/options-handler.js deleted file mode 100644 index 637be22..0000000 --- a/lib/options-handler.js +++ /dev/null @@ -1,565 +0,0 @@ -var Constants = require('./constants'); -var LogWrapper = require('./log-wrapper'); -var Promise = require('bluebird'); -var path = require('path'); -var fs = require('fs'); -var isStream = require('is-stream'); - -// TODO use joi for validation -// TODO turn into class - -// //////////////////////////////////////////////////////////////////////////// -// CONSTRUCTOR -// //////////////////////////////////////////////////////////////////////////// - -/** - * Constructs the `OptionsHandler` with an (optional) logger. - * - * @param {(logger|cli|console)} [logger=console] - Logger instance. - * @returns {OptionsHandler} The instance. - * @constructor - * @class Class which defines some useful methods to initialize and prepare the - * transformation options used in this module. - *

- * **NOTE:** This class is not to be intended to be called from - * outside this module! - * @example - * var OptionsHandler = require('./options-handler'); - * var logger = ...; - * - * var optionsHandler = new OptionsHandler(logger); - */ -function OptionsHandler(logger) { - - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); - - var self = this; - - /** - * Get a file extension for a given output target. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing a proper file extension (including '.', e.g. _'.yaml'_). - * @private - */ - function getDestFileExt(options) { - return new Promise(function (resolve, reject) { - var dot = '.'; - switch (options.target) { - case Constants.YAML: - return resolve(dot + Constants.YAML); - case Constants.JS: - return resolve(dot + Constants.JS); - case Constants.JSON: - return resolve(dot + Constants.JSON); - default: - reject(new Error('Invalid target option found while creating destination file extension: ' + options.target)); - } - }); - } - - /** - * Checks if the given type is a valid one. - * - * @param {string} type - One of `[ 'yaml' | 'json' | 'js']`. - * @returns {boolean} - `true` if is valid type, else `false`. - * @see {@link Constants#TYPES} - * @private - */ - function isValidType(type) { - return (Constants.TYPES.indexOf(type) >= 0); - } - - /** - * Promise which asserts that an origin or target is correct. If correct it - * resolved the passed `options` object, else if rejects with a `Error`. - * - * @param {Options} options - The configuration for a transformation. - * @param {string} typeName - The type name. - * @returns {Promise} - A Promise containing the passed `options` object. - * @private - */ - function assertType(options, typeName) { - return assertOptions(options, [typeName]) - .then(function (assertedOptions) { - if (isValidType(assertedOptions[typeName])) { - return assertedOptions; - } else { - return Promise.reject(new Error('Invalid ' + typeName + ' \'' + assertedOptions[typeName] + '\' found, must be one of ' + JSON.stringify(Constants.TYPES))); - } - }); - } - - /** - * A simple map for extensions to type. - * - * @type {{yml: string, yaml: string, js: string, json: string}} - * @private - */ - var typeMap = { - yml: Constants.YAML, - yaml: Constants.YAML, - js: Constants.JS, - json: Constants.JSON - }; - - /** - * Infer from path extension to a type using default value as fallback. - * - * @param {string} pathStr - The file path with or without extension. - * @param {boolean} origin - If the type is origin (true) or target (false) - * @param {string} defaultValue - The default value to use if type cannot be inferred from path. - * @returns {string} - A type value. - * @private - */ - function getTypeFromFilePath(pathStr, origin, defaultValue) { - var ext = path.extname(pathStr); - self.logInstance.debug('extension: ' + ext); - if (ext.charAt(0) === '.') { - ext = ext.substr(1); - } - - var type = typeMap[ext]; - if (!type) { - self.logInstance.debug('cannot resolve ' + (origin ? 'origin' : 'target') + ' type from file ' + pathStr + ', using default: ' + defaultValue); - type = defaultValue; - } - return type; - } - - /////////////////////////////////////////////////////////////////////////////// - // OPTIONS INIT & VALIDATION METHODS - /////////////////////////////////////////////////////////////////////////////// - - /** - * Completes the given `options` object by enriching from default values or using - * type inference if something required is "missing" (a missing `options.src` cannot - * be completed becaue this is mandatory). - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @throws {Error} - If `options` or `options.src` not passed. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.completeOptions(options) - * .then(function (copiedOptions) { - * ... - * }); - * @public - */ - this.completeOptions = function (options) { - return assertOptions(options, ['src']) - .then(function (assertedOptions) { - var srcType; - var destType; - if (assertedOptions.src) { - if (typeof assertedOptions.src === 'string') { - srcType = getTypeFromFilePath(assertedOptions.src, true, Constants.YAML); - } else if (typeof assertedOptions.src === 'object') { - srcType = Constants.JS; - } // TODO: what about stream? - } - self.logInstance.debug('srcType: ' + srcType); - if (assertedOptions.dest && (typeof assertedOptions.dest === 'string')) { - self.logInstance.debug('options.dest: ' + assertedOptions.dest); - destType = getTypeFromFilePath(assertedOptions.dest, false, Constants.JS); // TODO: what about stream? - } - self.logInstance.debug('destType: ' + destType); - - assertedOptions.origin = (assertedOptions.origin && (assertedOptions.origin !== Constants.ORIGIN_DESCRIPTION)) ? assertedOptions.origin : (srcType || Constants.DEFAULT_ORIGIN); - assertedOptions.target = (assertedOptions.target && (assertedOptions.target !== Constants.TARGET_DESCRIPTION)) ? assertedOptions.target : (destType || Constants.DEFAULT_TARGET); - assertedOptions.dest = assertedOptions.dest || Constants.DEFAULT_OPTIONS.dest; - assertedOptions.indent = assertedOptions.indent || Constants.DEFAULT_OPTIONS.indent; - assertedOptions.force = assertedOptions.force || Constants.DEFAULT_OPTIONS.force; - assertedOptions.imports = assertedOptions.imports || Constants.DEFAULT_OPTIONS.imports; - assertedOptions.exports = assertedOptions.exports || Constants.DEFAULT_OPTIONS.exports; - return assertedOptions; - }); - }; - - /** - * Ensures that the given input source is valid. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @throws {Error} - If the `options.src` is not defined or the file represented by `options.src` does not exist. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.ensureSrc(options) - * .then(function (ensuredOptions) { - * ... - * }); - * @public - */ - this.ensureSrc = function (options) { - //return assertOptions(options, ['src']) - // .then(function (assertedOptions) { - // if (typeof assertedOptions.src === 'string') { - // self.logInstance.debug('options.src is to be verfied as File path: ' + assertedOptions.src); - // // check for existing source file - // try { - // var stats = fs.statSync(assertedOptions.src); - // if (stats.isDirectory()) { - // return Promise.reject(new Error('Source file (options.src) is a directory, pls specify a valid file resource!')); - // } - // } catch (err) { - // err.message = 'Error occurred while checking input file \'' + assertedOptions.src + '\' which should be existing and accessible, code: ' + err.code + ', cause: ' + err.message; - // return Promise.reject(err); - // } - // } else if (isStream.readable(assertedOptions.src)) { - // self.logInstance.debug('options.src is Readable stream'); - // if (!options.origin) { - // return Promise.reject(new Error('When options.src is a Readable stream then setting options.origin is mandatory!')); - // } - // } else { - // self.logInstance.debug('options.src is JSON Object'); - // } - // return assertedOptions; - //}); - - return assertOptions(options, ['src']) - .then(function (options) { - return assertFileSrc(options); - }) - .spread(function (checked, options) { - if (!checked) { - return assertStreamSrc(options); - } - return [checked, options]; - }) - .spread(function (checked, options) { - if (!checked) { - self.logInstance.debug('options.src is JSON Object'); - } - return Promise.resolve(options); - }) - .catch(function (err) { - self.logInstance.error('options.src is unknown or invalid object: ' + options.src); - return Promise.reject(err); - }); - }; - - function assertFileSrc(options) { - return new Promise(function (resolve, reject) { - if (typeof options.src === 'string') { - self.logInstance.debug('options.src is to be verified as File path: ' + options.src); - // check for existing source file - try { - var stats = fs.statSync(options.src); - if (stats.isDirectory()) { - return reject(new Error('Source file (options.src) is a directory, pls specify a valid file resource!')); - } - return resolve([true, options]); - } catch (err) { - // here we get a "native" error which seems to be not type of Error but simple Object! - // For the sake of consistency we turn it into real Error type. - const realError = new Error('Error occurred while checking input file \'' + options.src + '\' which should be existing and accessible, code: ' + err.code + ', cause: ' + err.message); - realError.code = err.code; - realError.errno = err.errno; - realError.syscall = err.syscall; - realError.path = err.path; - return reject(realError); - } - } else { - return resolve([false, options]); - } - }); - } - - function assertStreamSrc(options) { - return new Promise(function (resolve, reject) { - if (isStream.readable(options.src)) { - self.logInstance.debug('options.src is Readable stream'); - if (!options.origin) { - return reject(new Error('when options.src is a Readable stream, then setting options.origin is mandatory!')); - } - return resolve([true, options]); - } else { - return resolve([false, options]); - } - }); - } - - /** - * This method ensures that destination file path is created if not set in - * options. If not, then it creates the path relative to the source file using - * its name and appending a proper extension depending on the `json` - * property of `options` (if `true` then '.js', else '.json'). - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.ensureDest(options) - * .then(function (ensuredOptions) { - * ... - * }); - * @public - */ - this.ensureDest = function (options) { - return assertOptions(options) - .then(function (assertedOptions) { - if (assertedOptions.dest === Constants.DEFAULT_OPTIONS.dest) { - return getDestFileExt(assertedOptions) - .then(function (destExt) { - var destDirName = path.dirname(assertedOptions.src); - var srcExt = path.extname(assertedOptions.src); - var destName = path.basename(assertedOptions.src, srcExt); - assertedOptions.dest = path.join(destDirName, destName + destExt); - self.logInstance.debug('Destination file: ' + assertedOptions.dest); - return assertedOptions; - }); - } else if (isStream.writable(assertedOptions.dest)) { - self.logInstance.debug('options.dest is Writable stream'); - if (!assertedOptions.target) { - return Promise.reject(new Error('When options.dest is a Writable stream then setting options.target is mandatory!')); - } - } else { - self.logInstance.debug('Destination file: ' + assertedOptions.dest); - } - return assertedOptions; - }); - }; - - /** - * Checks if the given origin is valid. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.assertOrigin(options) - * .then(function (ensuredOptions) { - * ... - * }); - * @public - */ - this.assertOrigin = function (options) { - return assertType(options, 'origin'); - }; - - /** - * Checks if the given target is valid. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.assertTarget(options) - * .then(function (ensuredOptions) { - * ... - * }); - * @public - */ - this.assertTarget = function (options) { - return assertType(options, 'target'); - }; - - /** - * Checks if a valid indention value is given and corrects values if invalid (with default value: 4 SPACEs). - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.ensureIndent(options) - * .then(function (ensuredOptions) { - * ... - * }); - * @public - */ - this.ensureIndent = function (options) { - return assertOptions(options) - .then(function (assertedOptions) { - self.logInstance.trace('options before ensureIndent():: ' + JSON.stringify(assertedOptions, null, 4)); - if (assertedOptions.indent === undefined) { - self.logInstance.info('Missing indention, reset to default: ' + Constants.DEFAULT_INDENT); - assertedOptions.indent = Constants.DEFAULT_INDENT; - } else if (assertedOptions.indent < Constants.MIN_INDENT) { - self.logInstance.info('Indention \'' + assertedOptions.indent + '\' is too narrow, reset to default: ' + Constants.DEFAULT_INDENT); - assertedOptions.indent = Constants.DEFAULT_INDENT; - } else if (assertedOptions.indent > Constants.MAX_INDENT) { - self.logInstance.info('Indention \'' + assertedOptions.indent + '\' is too wide, reset to default: ' + Constants.DEFAULT_INDENT); - assertedOptions.indent = Constants.DEFAULT_INDENT; - } - self.logInstance.trace('options after ensureIndent():: ' + JSON.stringify(assertedOptions, null, 4)); - return assertedOptions; - }); - }; - - /** - * Log the options with INFO level. - * - * @param {Options} options - The configuration for a transformation. The properties to log with INFO. - * @returns {Promise} - A Promise containing the passed `options` object. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.verboseOptions(options) - * .then(function (loggedOptions) { - * ... - * }); - * @private - */ - this.verboseOptions = function (options) { - return assertOptions(options) - .then(function (assertedOptions) { - return self.logInstance.verboseOptions(assertedOptions); - }); - }; -} - -OptionsHandler.prototype = {}; -OptionsHandler.prototype.constructor = OptionsHandler; -module.exports = OptionsHandler; - -/** - * This method ensures that the options object is set with all necessary and - * correct values. The method does not alter the given object, but creates - * and fills a new instance from the given values and/or default ones. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing a new and complete `options` object. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var options = {...}; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.ensureOptions(options) - * .then(function (ensuredOptions) { - * ... - * }); - * @public - */ -OptionsHandler.prototype.ensureOptions = function (options) { - return this.completeOptions(options) - .then(this.ensureSrc) - .then(this.ensureDest) - .then(this.assertOrigin) - .then(this.assertTarget) - .then(this.ensureIndent) - .then(this.verboseOptions); -}; - -/** - * This method validates the transformation process described by the given - * options and provides the validate and enriched options and according name - * to resolve a proper function. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object and a 'transformation' string in an array. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.validateTransformation(options) - * .spread(function (validatedOptions, transformation) { - * ... - * )): - * @see {@link transformations} - * @public - */ -OptionsHandler.prototype.validateTransformation = function (options) { - return assertOptions(options, ['origin', 'target']) - .then(function (assertedOptions) { - return new Promise(function (resolve, reject) { - var transformation = assertedOptions.origin + '2' + assertedOptions.target; - if (Constants.TRANSFORMATIONS.indexOf(transformation) < 0) { - reject(new Error('Unsupported target type transformation \'' + assertedOptions.origin + ' -> ' + assertedOptions.target + '\' configured in options.')); - } else { - resolve([assertedOptions, transformation]); - } - }); - }); -}; - -/** - * Asserts that the given `options` and (optionally) the given properties are - * inside the options. If not, the Promise rejects with proper error message. - * - * @param {object} options - The objects which should be set. - * @param {string[]} [properties] - Properties which should exist in `options`. - * @returns {Promise} - Promise which contains the `options` as result. - * @example - * var options = {...}; - * - * assertOptions(options, ['src', 'origin']) - * .then(function (assertedOptions) { - * ... - * }); - * @public - */ -OptionsHandler.prototype.assertOptions = assertOptions; - -/** - * Asserts that the given `options` and (optionally) the given properties are - * inside the options. If not, the Promise rejects with proper error message. - * - * @param {object} options - The objects which should be set. - * @param {string[]} [properties] - Properties which should exist in `options`. - * @returns {Promise} - Promise which contains the `options` as result. - * @example - * var options = {...}; - * - * assertOptions(options, ['src', 'origin']) - * .then(function (assertedOptions) { - * ... - * }); - * @private - */ -function assertOptions(options, properties) { - return new Promise(function (resolve, reject) { - if (!options) { - return reject(new Error('missing options object!')); - } - if (properties && properties.length > 0) { - var missing = []; - properties.forEach(function (p) { - if (!options[p]) { - missing.push('options.' + p); - } - }); - if (missing.length > 0) { - return reject(new Error('missing options property(s): ' + JSON.stringify(missing) + '!')); - } - } - resolve(options); - }); -} diff --git a/lib/options-schema.js b/lib/options-schema.js deleted file mode 100644 index e69de29..0000000 diff --git a/lib/reader.js b/lib/reader.js deleted file mode 100644 index 5ba4deb..0000000 --- a/lib/reader.js +++ /dev/null @@ -1,323 +0,0 @@ -var Constants = require('./constants'); -var LogWrapper = require('./log-wrapper'); -var OptionsHandler = require('./options-handler'); -var Validator = require('./validator'); -var jsYaml = require('js-yaml'); -var Promise = require('bluebird'); -var fs = Promise.promisifyAll(require('fs')); -var Buffer = require('buffer').Buffer; -var path = require('path'); -var isStream = require('is-stream'); -var stringify = require('json-stringify-safe'); - -// //////////////////////////////////////////////////////////////////////////// -// CONSTRUCTOR -// //////////////////////////////////////////////////////////////////////////// -// TODO turn into class -/** - * Constructs the `Reader` with an (optional) logger. - * - * @param {(logger|cli|console)} [logger=console] - Logger instance. - * @returns {Reader} The instance. - * @constructor - * @class This class provides utility methods usable to read YAML, JSON or JS - * from a source (file, {object} or {@link stream.Readable}) to JS memory objects. - * @example - * var Reader = require('jy-transform').Reader; - * var logger = ...; - * - * var reader = new Reader(logger); - */ -function Reader(logger) { - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); - - /** - * The options handler. - * - * @type {OptionsHandler} - * @private - */ - this.optionsHandler = new OptionsHandler(logger); - - /** - * The validator. - * - * @type {Validator} - */ - this.validator = new Validator(logger); - - var self = this; - - /** - * Creates a function to read from the passed source in to the given buffer array. - * - * @param {stream.Readable} readable - The source to read from. - * @param {array} bufs - The temporary buffer array. - * @returns {Function} - The function which reads and buffers. - * @private - */ - function createReadableFunction(readable, bufs) { - return function () { - var chunk; - while (null !== (chunk = readable.read())) { - self.logInstance.trace('JSON chunk: ', chunk); - bufs.push(chunk); - } - }; - } - - /** - * Reads from a passed stream and resolves by callback. - * - * @param {stream.Readable} readable - The source to read from. - * @param {function} resolve - Callback for success case. - * @param {function} reject - Callback for Error case. - * @param {string} origin - Origin type, must be 'yaml' or 'json'/'js'. - * @private - */ - function readFromStream(readable, resolve, reject, origin) { - var bufs = []; - readable - .on('readable', createReadableFunction(readable, bufs)) - .on('error', function (err) { - reject(err); - }) - .on('end', function () { - var buffer = Buffer.concat(bufs); - try { - self.logInstance.debug(origin + ' reading from Readable'); - if (origin === Constants.JSON || origin === Constants.JS) { - resolve(JSON.parse(buffer.toString(Constants.UTF8))); - } else {// HINT: commented (see below): if (origin === Constants.YAML) { - resolve(jsYaml.safeLoad(buffer.toString(Constants.UTF8))); - } - // HINT: for the sake of test coverage it's commented, since this is a private method we have control over options.origin inside this class! - //else { - // reject(new Error('Unsupported type: ' + origin + ' to read from Readable')); - //} - } catch (err) { // probably a SyntaxError for JSON or a YAMLException - self.logInstance.error('Unexpected error: ' + err.stack); - readable.emit('error', err); // send to .on('error',... - } - }); - } - - /////////////////////////////////////////////////////////////////////////////// - // API METHODS (PUBLIC) - /////////////////////////////////////////////////////////////////////////////// - - /** - * Reads the data from a given JS or JSON source. - * - * @param {Options} options - Contains the JS/JSON source reference to read from. - * @returns {Promise} - Contains the read JS object. - * @public - * @example - * var Reader = require('jy-transform').Reader; - * var logger = ...; - * var reader = new Reader(logger); - * - * // --- from file path - * - * var options = { - * src: 'foo.js' - * }; - * - * reader.readJs(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // --- from Readable - * - * options = { - * src: fs.createReadStream('foo.js') - * }; - * - * reader.readJs(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // --- from object - * - * options = { - * src: { - * foo: 'bar' - * } - * }; - * - * reader.readJs(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - */ - this.readJs = function (options) { - self.logInstance.trace('OPTIONS BEFORE ASSERTING IN readJs:::' + JSON.stringify(options)); - return self.optionsHandler.assertOptions(options, ['src']) - .then(function (assertedOptions) { - return new Promise(function (resolve, reject) { - if (typeof assertedOptions.src === 'string') { - try { - var resolvedPath = path.resolve('', assertedOptions.src); - - if ((path.extname(assertedOptions.src) === '.js' || options.origin === Constants.JS) && options.imports && options.imports !== '') { - - if (!self.validator.validateIdentifier(options.imports)) { - reject(new Error('found invalid identifier for reading from exports: ' + stringify(options.imports, null, 4))); - } else { - var json = require(resolvedPath)[options.imports]; - self.logInstance.trace('LOADED JSON object (' + options.imports + '):: ' + stringify(json, null, 4)); - if (!json) { - reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object but could not find this object, pls ensure that file ' + assertedOptions.src + ' contains it.')); - } else { - resolve(json); - } - } - - } else { - resolve(require(resolvedPath)); - } - } catch (err) { // probably a SyntaxError - self.logInstance.error('Unexpected error: ' + err.stack); - reject(err); - } - } else if (isStream.readable(assertedOptions.src)) { - readFromStream(assertedOptions.src, resolve, reject, Constants.JSON); - } else if (options.imports && options.imports !== '') { - - if (!self.validator.validateIdentifier(options.imports)) { - reject(new Error('found invalid identifier for reading from object: ' + stringify(options.imports, null, 4))); - } else { - var obj = assertedOptions.src[options.imports]; - self.logInstance.trace('LOADED JSON object (' + options.imports + '):: ' + stringify(obj, null, 4)); - if (!obj) { - reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object but could not find this object, pls ensure that object source contains it.')); - } else { - resolve(obj); - } - } - } else { - resolve(assertedOptions.src); - } - }); - }); - }; - - /** - * Loads a single YAML source containing document and returns a JS object. - * - * *NOTE:* This function does not understand multi-document sources, it throws - * exception on those. - * - * @param {Options} options - Contains the YAML source reference to read from. - * @returns {Promise} - Contains the read JS object. - * @public - * @example - * var Reader = require('jy-transform').Reader; - * var logger = ...; - * var reader = new Reader(logger); - * - * // --- from file path - * - * options = { - * src: 'foo.yml' - * }; - * - * reader.readYaml(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // --- from Readable - * - * options = { - * src: fs.createReadStream('foo.yml') - * }; - * - * reader.readJs(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - */ - this.readYaml = function (options) { - self.logInstance.trace('OPTIONS BEFORE ASSERTING IN readYaml::: ' + JSON.stringify(options)); - return self.optionsHandler.assertOptions(options, ['src']) - .then(function (assertedOptions) { - return new Promise(function (resolve, reject) { - if (typeof assertedOptions.src === 'string') { - // load source from YAML file - fs.readFileAsync(assertedOptions.src, Constants.UTF8) - .then(function (yaml) { - self.logInstance.debug('YAML loaded from file ' + assertedOptions.src); - try { - resolve(jsYaml.safeLoad(yaml)); - } catch (err) { // probably a YAMLException - self.logInstance.error('Unexpected error: ' + err.stack); - reject(err); - } - }) - .catch((err) => { - // TODO error handling for reading here? - }); - } else if (isStream.readable(assertedOptions.src)) { - readFromStream(assertedOptions.src, resolve, reject, Constants.YAML); - } else { - resolve(assertedOptions.src); - } - }); - }); - }; - -///** -// * Parses string as single YAML source containing multiple YAML document and turns a JS objects array. -// * -// * NOTE: This function does not understand multi-document sources, it throws exception on those. -// * -// * @param src {string} The YAML source to read. -// * @returns {Promise} Containing an array holding the multiple JSON objects. -// * @public -// */ -//Reader.prototype.readYamls = function (src) { -// // load source from YAML source -// return fs.readFileAsync(src, 'utf8') -// .then(function(yaml) { -// self.logger.debug('YAML documents loaded from ' + src); // TOD: can this be shortened? -> return Promise.resolve(jsYaml.safeLoadAll(yaml)); -// return Promise.resolve().then(function () { -// var jsDocs = []; -// return jsYaml.safeLoadAll(yaml, function (doc) { // TOD this will not work in Promise environment!!! -// self.logger.trace(doc); -// jsDocs.push(doc); -// }); -// }); -// }); -//}; - -} - -Reader.prototype.constructor = Reader; -module.exports = Reader; diff --git a/lib/transformer.js b/lib/transformer.js deleted file mode 100644 index 4e1b594..0000000 --- a/lib/transformer.js +++ /dev/null @@ -1,421 +0,0 @@ -var Writer = require('./writer'); -var Reader = require('./reader'); -var LogWrapper = require('./log-wrapper'); -var OptionsHandler = require('./options-handler'); -var middleware = require('./middleware'); -var path = require('path'); - -// //////////////////////////////////////////////////////////////////////////// -// CONSTRUCTOR -// //////////////////////////////////////////////////////////////////////////// -// TODO turn into class -/** - * Constructs the `Transformer` with options and an (optional) logger. - * - * @param {(logger|cli|console)} [logger=console] - Logger instance. - * @returns {Transformer} - The instance. - * @constructor - * @class This class provides all methods usable to handle YAML, JSON and JS and - * their transformations. - * @example - * var logger = ...; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - */ -function Transformer(logger) { - - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); - - /** - * The options handler. - * - * @type {OptionsHandler} - * @private - */ - this.optionsHandler = new OptionsHandler(logger); - - /** - * The internal `Writer` instance. - * - * @type {Writer} - * @private - */ - var writer = new Writer(logger); - - /** - * The internal `Reader` instance. - * - * @type {Reader} - * @private - */ - var reader = new Reader(logger); - - /** - * Ensures that basic middleware is set. - */ - var ensureMiddleware = middleware.ensureMiddleware; - - /** - * Internal delegate function to execute transformation logic (ITMO): - * - Input (read) - * - Transform + Middleware - * - Output (write) - * - * @param {Options} options - The configuration for a transformation. - * @param {function} read - The reader function. - * @param {function} [middleware] - The middleware to apply. - * @param {function} write - The writer functions. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.itmo(options, reader.readYaml, middleware, writer.writeJson); - * @private - */ - function itmo(options, read, middleware, write) { - return read(options) - .then(ensureMiddleware(middleware)) - .then(function (json) { - return write(json, options); - }); - } - - /////////////////////////////////////////////////////////////////////////////// - // TRANSFORMATION METHODS (DELEGATES) - /////////////////////////////////////////////////////////////////////////////// - - /** - * Convert YAML to JS. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.yamlToJs(options, middleware); - * @see itmo - * @private - */ - this.yamlToJs = function (options, middleware) { - return itmo(options, reader.readYaml, middleware, writer.writeJs); - }; - - /** - * Convert YAML to JSON. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.yamlToJson(options, middleware); - * @see itmo - * @private - */ - this.yamlToJson = function (options, middleware) { - return itmo(options, reader.readYaml, middleware, writer.writeJson); - }; - - /** - * Convert JS to YAML. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsToYaml(options, middleware); - * @see itmo - * @private - */ - this.jsToYaml = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeYaml); - }; - - /** - * Convert JSON to JS. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsonToJs(options, middleware); - * @see itmo - * @private - */ - this.jsonToJs = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeJs); - }; - - /** - * Convert JS to JSON. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsToJson(options, middleware); - * @see itmo - * @private - */ - this.jsToJson = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeJson); - }; - - /** - * Convert YAML to YAML. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.yamlToYaml(options, middleware); - * @see itmo - * @private - */ - this.yamlToYaml = function (options, middleware) { - return itmo(options, reader.readYaml, middleware, writer.writeYaml); - }; - - /** - * Convert JSON to JSON. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsonToJson(options, middleware); - * @see itmo - * @private - */ - this.jsonToJson = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeJson); - }; - - /** - * Convert JS to JS. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (json) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsToJs(options, middleware); - * @see itmo - * @private - */ - this.jsToJs = function (options, middleware) { - return itmo(options, reader.readJs, middleware, writer.writeJs); - }; - - /** - * A transformation name to internal function mapping. - * - * @namespace - * @property {function} yaml2js - The transformation function for YAML -> JS. - * @property {function} yaml2json - The transformation function for YAML -> JSON. - * @property {function} yaml2yaml - The transformation function for YAML -> YAML. - * @property {function} json2yaml - The transformation function for JSON -> YAML. - * @property {function} json2js - The transformation function for JSON -> JS. - * @property {function} json2json - The transformation function for JSON -> JSON. - * @property {function} js2yaml - The transformation function for JS -> YAML. - * @property {function} js2json - The transformation function for JS -> JSON. - * @property {function} js2js - The transformation function for JS -> JS. - * @private - */ - this.transformations = { - yaml2js: this.yamlToJs, - yaml2json: this.yamlToJson, - yaml2yaml: this.yamlToYaml, - json2yaml: this.jsToYaml, - json2js: this.jsonToJs, - json2json: this.jsonToJson, - js2yaml: this.jsToYaml, - js2json: this.jsToJson, - js2js: this.jsToJs - }; -} - -/////////////////////////////////////////////////////////////////////////////// -// PROTOTYPE -/////////////////////////////////////////////////////////////////////////////// - -Transformer.prototype.constructor = Transformer; -exports = module.exports = Transformer; - -/////////////////////////////////////////////////////////////////////////////// -// API METHOD (PUBLIC) -/////////////////////////////////////////////////////////////////////////////// - -/** - * The entry method for all transformation accepting a configuration object and - * an (optional) middleware function. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JSON object for altering the passed JSON, the function signature is: - * ``` - * function(json) - * ``` - * **NOTE:** the Promise has to return the processed JSON! - * @returns {Promise} - Containing the transformation result as message (e.g. - * to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to file failed due to any reason. - * @public - * @example - * var logger = ...; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * var Promise = require('bluebird'); - * var options = {...}; - * var middleware = function (json) { - * json.myproperty = 'new value'; - * return Promise.resolve(json); - * }; - * - * transformer.transform(options, middleware) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - */ -Transformer.prototype.transform = function (options, middleware) { - var self = this; - this.logInstance.debug('transform'); - return this.optionsHandler.ensureOptions(options) - .then(this.optionsHandler.validateTransformation) - .spread(function (ensuredOptions, transformation) { - self.logInstance.info('Calling transformation: ' + transformation); - return self.transformations[transformation](ensuredOptions, middleware); - }); -}; diff --git a/lib/type-definitions.js b/lib/type-definitions.js deleted file mode 100644 index bcfe2cf..0000000 --- a/lib/type-definitions.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @module type-definitions - * @description The type definitions for this module. - */ - -// ///////////////////////////////////////////////////////////////////////////// -// EXTERNAL DEFINITIONS -// ///////////////////////////////////////////////////////////////////////////// - -/** - * Hapi.js Joi. - * @external joi - * @see {@link https://github.com/hapijs/joi Hapi Joi} - */ - -/** - * Joi validation error. - * @typedef ValidationError - * @memberof external:joi - * @see {@link hhttps://github.com/hapijs/joi/blob/v10.2.0/API.md#errors Joi errors} - */ - -/** - * Hapi.js Joi schema. - * @typedef Schema - * @memberof external:joi - * @see {@link https://github.com/hapijs/joi#usage Hapi Joi schema} - */ - -// ///////////////////////////////////////////////////////////////////////////// -// INTERNAL DEFINITIONS -// ///////////////////////////////////////////////////////////////////////////// - -/** - * The configuration properties provided to the framework functions. - * @typedef {object} Options - * @property {string} [origin=yaml] - The origin type. - * @property {string} [target=js] - The target type. - * @property {(string|Readable|object)} src - The source (`string` type is treated as a file path). - * @property {(string|Writable|object)} [dest] - The destination (`string` type is treated as a file path). - * @property {number} [indent=4] - The indention in files. - * @property {string} [imports=undefined] - The exports name for reading from JS source file or objects only. - * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. - * @public - */ diff --git a/lib/validator.js b/lib/validator.js deleted file mode 100644 index efa165a..0000000 --- a/lib/validator.js +++ /dev/null @@ -1,82 +0,0 @@ -var Constants = require('./constants'); -var LogWrapper = require('./log-wrapper'); -var OptionsHandler = require('./options-handler'); -var Promise = require('bluebird'); -var stringify = require('json-stringify-safe'); -var path = require('path'); -var fs = Promise.promisifyAll(require('fs')); - -// //////////////////////////////////////////////////////////////////////////// -// CONSTRUCTOR -// //////////////////////////////////////////////////////////////////////////// - -/** - * Created at [Generating a regular expression to match valid JavaScript identifiers](https://mathiasbynens.be/demo/javascript-identifier-regex). - * - * @type {RegExp} - * @private - */ -var identifierRegExpECMAScript6 = /^(?!(?:do|if|in|for|let|new|try|var|case|else|enum|eval|null|this|true|void|with|await|break|catch|class|const|false|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$)(?:[\$A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D])(?:[\$0-9A-Z_a-z\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF])*$/; - -// TODO turn into class - -/** - * Constructs the `Validator` with an (optional) logger. - * - * @param {(logger|cli|console)} [logger=console] - Logger instance. - * @returns {Writer} The instance. - * @constructor - * @class This class validates JS identifier. - *

- * **NOTE:** this class is intended for internal use only! - * @example - * var Validator = require('./validator.js'); - * var logger = ...; - * - * var validator = new Validator(logger); - */ -function Validator(logger) { - - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); - - /** - * The options handler. - * - * @type {OptionsHandler} - * @private - */ - this.optionsHandler = new OptionsHandler(logger); - - var self = this; - - /** - * This method checks if a given `identifier` is a valid ECMAScript 6 identifier. - * - * @param {string} identifier - The identifier to check. - * @returns {boolean} - `true` if valid, else `false`. - * @public - * @example - * var Validator = require('./validator.js'); - * var logger = ...; - * var validator = new Validator(logger); - * var identifier = 'foo'; - * - * logger.info('valid = ' + validator.validateIdentifier(identifier)); - */ - this.validateIdentifier = function (identifier) { - if (typeof identifier !== 'string') { - self.logInstance.debug('Invalid identifier, not a string, was type of: ' + (typeof identifier)); - return false; - } - return identifierRegExpECMAScript6.test(identifier); - }; -} - -Validator.prototype.constructor = Validator; -module.exports = Validator; diff --git a/lib/writer.js b/lib/writer.js deleted file mode 100644 index 8613edd..0000000 --- a/lib/writer.js +++ /dev/null @@ -1,509 +0,0 @@ -var Constants = require('./constants'); -var LogWrapper = require('./log-wrapper'); -var OptionsHandler = require('./options-handler'); -var Validator = require('./validator'); -var serializeJs = require('serialize-js'); -var jsYaml = require('js-yaml'); -var Promise = require('bluebird'); -var mkdirp = require('mkdirp-then'); -var jsonStringifySafe = require('json-stringify-safe'); -var path = require('path'); -var fs = Promise.promisifyAll(require('fs')); -var os = require('os'); -var isStream = require('is-stream'); - -// //////////////////////////////////////////////////////////////////////////// -// CONSTRUCTOR -// //////////////////////////////////////////////////////////////////////////// -// TODO turn into class - -/** - * Constructs the `Writer` with an (optional) logger. - * - * @param {(logger|cli|console)} [logger=console] - Logger instance. - * @returns {Writer} The instance. - * @constructor - * @class This class provides utility methods usable to write JS objects - * from memory to a JSON/JS/YAML destination - * (file, object or {@link stream.Readable}). - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * - * var writer = new Writer(logger); - */ -function Writer(logger) { - - /** - * The logger instance. - * - * @member {(logger|cli|console)} - * @private - */ - this.logInstance = new LogWrapper(logger); - - /** - * The options handler. - * - * @type {OptionsHandler} - * @private - */ - this.optionsHandler = new OptionsHandler(logger); - - /** - * The validator. - * - * @type {Validator} - */ - this.validator = new Validator(logger); - - var self = this; - - - /////////////////////////////////////////////////////////////////////////////// - // METHODS (PRIVATE) - /////////////////////////////////////////////////////////////////////////////// - - /** - * Creates a potential named `'module.exports[.exportsTo]'` string. - * - * @param {string} [exportsTo] - The export name. - * @returns {Promise} - Promise resolves with the exports string. - * @private - */ - function createExportsString(exportsTo) { - return new Promise(function (resolve, reject) { - var exports = 'module.exports'; - if (exportsTo && exportsTo !== '') { - if (!self.validator.validateIdentifier(exportsTo)) { - return reject(new Error('Found invalid identifier for exports: ' + exportsTo)); - } else { - exports = exports + '.' + exportsTo + ' = '; - } - } else { - exports = exports + ' = '; - } - resolve(exports); - }); - } - - /** - * Serialize a JS object to string. - * - * @param {object} object - The JS Object to serialize. - * @param {number} indent - The indention. - * @param {string} [exportsTo] - Name for export (*IMPORTANT:* must be a valid ES6 identifier). - * @returns {Promise} - Promise resolve with the serialized JS object. - * @private - * @todo [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> `'\'use strict\';' + os.EOL + os.EOL + ...`)? - */ - function serializeJsToString(object, indent, exportsTo) { - return createExportsString(exportsTo) - .then(function(exportsStr) { - return exportsStr + serializeJs.serialize(object, {indent: indent}) + ';' + os.EOL; - }); - } - - /** - * Serialize a JS object to JSON string. - * - * @param {object} object - Object to serialize. - * @param {number} indent - Indention. - * @returns {string} - The serialized JSON. - * @private - */ - function serializeJsToJsonString(object, indent) { - return jsonStringifySafe(object, null, indent) + os.EOL; - } - - /** - * Turns the destination file name into a name containing a consecutive - * number if it exists. It iterates over the files until it finds a file - * name which does not exist. - * - * @param {string} dest - The destination file. - * @returns {string} - A consecutive file name or the original one if - * `dest` file does not exist. - * @private - */ - function getConsecutiveDestName(dest) { - var tmpDest = dest; - var i = 1; - var destDirName = path.dirname(tmpDest); - var ext = path.extname(tmpDest); - var basename = path.basename(tmpDest, ext); - while (fs.existsSync(tmpDest)) { - tmpDest = path.join(destDirName, basename + '(' + i++ + ')' + ext); - } - return tmpDest; - } - - /** - * Writes a serialized object to file. - * - * @param {string} object - The object to write into file. - * @param {string} dest - The file destination path. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @param {function} resolve - The Promise `resolve` callback. - * @param {function} reject - The Promise `reject` callback. - * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. - * @see {@link Constants#YAML} - * @see {@link Constants#JSON} - * @see {@link Constants#JS} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @throws {Error} - If serialized JSON file could not be written due to any reason. - * @private - */ - function writeToFile(object, dest, target, resolve, reject, forceOverwrite) { - - /** - * Ensures that all dirs exists for `dest` and writes the file. - * - * @private - */ - function mkdirAndWrite() { - var destDir = path.dirname(dest); - self.logInstance.debug('Destination dir: ' + destDir); - mkdirp(destDir) - .then(function () { - self.logInstance.debug('Destination dir ' + destDir + ' successfully written'); - if (forceOverwrite === undefined || forceOverwrite === false) { - dest = getConsecutiveDestName(dest); - self.logInstance.info('Setting was: do not overwrite, using destination ' + dest + '.'); - } - return fs.writeFileAsync(dest, object, Constants.UTF8); - }) - .then(function () { - resolve('Writing \'' + target + '\' file \'' + dest + '\' successful.'); - }) - .catch(function (err) { - err.message = 'Could not write \'' + target + '\' file \'' + dest + '\', cause: ' + err.message; - reject(err); - }); - } - - return fs.statAsync(dest) - .then(function (stats) { - if (stats.isDirectory()) { - reject(new Error('Destination file is a directory, pls specify a valid file resource!')); - } else { - // file exists - mkdirAndWrite(); - } - }) - .catch(function (err) { - // ignore error (because file could possibly not exist at this point of time) - mkdirAndWrite(); - }); - } - - /** - * Writes a string serialized data object to a stream. - * - * @param {string} object - The data to write into stream. - * @param {string} dest - The stream destination. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @param {function} resolve - The Promise `resolve` callback. - * @param {function} reject - The Promise `reject` callback. - * @see {@link Constants#YAML} - * @see {@link Constants#JSON} - * @see {@link Constants#JS} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @throws {Error} - If serialized JS object could not be written due to any reason. - * @private - */ - function writeToStream(object, dest, target, resolve, reject) { - dest - .on('error', function (err) { - reject(err); - }) - .on('finish', function () { - resolve('Writing ' + target + ' to stream successful.'); - }); - - // write stringified data - dest.write(object); - dest.end(); - } - - /////////////////////////////////////////////////////////////////////////////// - // API METHODS (PUBLIC) - /////////////////////////////////////////////////////////////////////////////// - - /** - * Writes a JS object to a YAML destination. - * - * @param {object} object - The JS object to write into YAML destination. - * @param {Options} options - The write destination and indention. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @throws {Error} - If YAML destination could not be written due to any reason. - * @public - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * var writer = new Writer(logger); - * - * // ---- write obj to file - * - * var obj = {...}, - * var options = { - * dest: 'result.yml', - * indent: 2 - * } - * - * writer.writeYaml(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to Writable - * - * options = { - * dest: fs.createWriteStream('result.yml'), - * indent: 4 - * } - * - * writer.writeYaml(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - */ - this.writeYaml = function (object, options) { - return self.optionsHandler.ensureIndent(options) - .then(function (ensuredOptions) { - return new Promise(function (resolve, reject) { - // complain about missing destination - if (!ensuredOptions.dest) { - reject(new Error('Missing options.dest, pls specify existing destination resource!')); - } else { - var dest = ensuredOptions.dest; - var indent = ensuredOptions.indent; - - var yaml; - try { - yaml = jsYaml.safeDump(object, {indent: indent, noRefs: true}); - } catch (err) { - err.message = 'Could not write YAML file \'' + dest + '\', cause: ' + err.message; - return reject(err); - } - - if (typeof dest === 'string') { // file - writeToFile(yaml, dest, Constants.YAML, resolve, reject, ensuredOptions.force); - } else if (isStream.writable(dest)) { // stream - writeToStream(yaml, dest, Constants.YAML, resolve, reject); - } else { // object - ensuredOptions.dest = yaml; - resolve('Writing YAML to options.dest successful.'); - } - } - }); - }); - }; - - /** - * Writes a JS object to a JSON destination. - * - * @param {object} object - The JS object to write into JSON destination. - * @param {Options} options - The write destination and indention. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @public - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * var writer = new Writer(logger); - * - * // ---- write obj to file - * - * var obj = {...}; - * var options = { - * dest: 'result.json', - * indent: 2 - * } - * - * writer.writeJson(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to Writable - * - * options = { - * dest: fs.createWriteStream('result.json'), - * indent: 4 - * } - * - * writer.writeJson(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * // ---- write obj to object - * - * options = { - * dest: {}, - * indent: 4 - * } - * - * writer.writeJson(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - */ - this.writeJson = function (object, options) { - return self.optionsHandler.ensureIndent(options) - .then(function (ensuredOptions) { - return new Promise(function (resolve, reject) { - // complain about missing destination - if (!ensuredOptions.dest) { - reject(new Error('Missing options.dest, pls specify existing destination resource!')); - } else { - var dest = ensuredOptions.dest; - var indent = ensuredOptions.indent; - if (typeof dest === 'string') { // file - writeToFile(serializeJsToJsonString(object, indent), dest, Constants.JSON, resolve, reject, ensuredOptions.force); - } else if (isStream.writable(dest)) { // stream - writeToStream(serializeJsToJsonString(object, indent), dest, Constants.JSON, resolve, reject); - } else { // object - ensuredOptions.dest = serializeJsToJsonString(object, indent); - resolve('Writing JSON to options.dest successful.'); - } - } - }); - }); - }; - - /** - * Writes a JS object to a JS destination. The object is prefixed by `module.exports = `. - * - * @param {object} object - The JSON to write into JS destination. - * @param {Options} options - The write destination and indention. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @public - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * var writer = new Writer(logger); - * - * // ---- write obj to file - * - * var obj = {...}; - * var options = { - * dest: 'result.js', - * indent: 2 - * } - * - * writer.writeJs(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to Writable - * - * options = { - * dest: fs.createWriteStream('result.json'), - * indent: 4 - * } - * - * writer.writeJs(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to object - * - * options = { - * dest: {}, - * indent: 2 - * } - * - * writer.writeJs(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - */ - this.writeJs = function (object, options) { - return self.optionsHandler.ensureIndent(options) - .then(function (ensuredOptions) { - return new Promise(function (resolve, reject) { - // complain about missing destination - if (!ensuredOptions.dest) { - reject(new Error('Missing options.dest, pls specify existing destination resource!')); - } else { - var dest = ensuredOptions.dest; - var indent = ensuredOptions.indent; - if (typeof dest === 'string') { // file - serializeJsToString(object, indent, ensuredOptions.exports) - .then(function (data) { - return writeToFile(data, dest, Constants.JS, resolve, reject, ensuredOptions.force); - }) - .catch(function (err) { - reject(err); - }); - } else if (isStream.writable(dest)) { // stream - serializeJsToString(object, indent, ensuredOptions.exports) - .then(function (data) { - return writeToStream(data, dest, Constants.JS, resolve, reject); - }) - .catch(function (err) { - reject(err); - }); - } else { // object - var msg; - if (ensuredOptions.exports && ensuredOptions.exports !== '') { - if (!self.validator.validateIdentifier(ensuredOptions.exports)) { - reject(new Error('Found invalid identifier for exports: ' + ensuredOptions.exports)); - } else { - ensuredOptions.dest[ensuredOptions.exports] = object; - msg = 'Writing JS to options.dest.' + ensuredOptions.exports + ' successful.'; - } - } else { - ensuredOptions.dest = object; - msg = 'Writing JS to options.dest successful.'; - } - resolve(msg); - } - } - }); - }); - }; -} - -Writer.prototype.constructor = Writer; -module.exports = Writer; diff --git a/package.json b/package.json index 205f307..c543a11 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,8 @@ "build": "babel src -d lib", "docs": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", - "pretestBAK": "rm -Rf test/tmp", - "test": "jest --no-cache --coverage --runInBand --config=./.jestrc.js", + "pretest": "mkdir -pv test/tmp", + "test": "JYT_DEBUG=true JYT_ERROR=true jest --expand --no-cache --coverage --runInBand --config=./.jestrc.js", "eslint": "eslint ." }, "engines": { @@ -33,7 +33,9 @@ "dependencies": { "bluebird": "^3.4.6", "cli": "^1.0.1", + "cwd": "~0.10.0", "is-stream": "^1.1.0", + "joi": "~10.5.0", "js-yaml": "^3.6.1", "json-stringify-safe": "^5.0.1", "mkdirp-then": "^1.2.0", diff --git a/lib/constants.js b/src/constants.js similarity index 54% rename from lib/constants.js rename to src/constants.js index 0f3af46..8ceeed1 100644 --- a/lib/constants.js +++ b/src/constants.js @@ -1,28 +1,8 @@ -'use strict'; - -// //////////////////////////////////////////////////////////////////////////// -// CONSTRUCTOR -// //////////////////////////////////////////////////////////////////////////// - /** - * Constructs the constants. - * - * @returns {Constants} - The instance. - * @constructor - * @class Class which defines all constants usable in or with this module. + * @module jy-transform:constants + * @description Useful constants used for the module and its usage. + * @public */ -function Constants() { - return this; -} - -Constants.prototype = {}; -Constants.prototype.constructor = Constants; -var constants = new Constants(); -module.exports = constants; - -// //////////////////////////////////////////////////////////////////////////// -// CONSTANTS -// //////////////////////////////////////////////////////////////////////////// /** * The 'utf8' constant. @@ -31,7 +11,7 @@ module.exports = constants; * @constant * @public */ -Constants.prototype.UTF8 = 'utf8'; +export const UTF8 = 'utf8'; /** * The 'yaml' type constant. @@ -40,139 +20,139 @@ Constants.prototype.UTF8 = 'utf8'; * @constant * @public */ -Constants.prototype.YAML = 'yaml'; +export const TYPE_YAML = 'yaml'; /** * The 'json' type constant. - * * @type {string} * @constant * @public */ -Constants.prototype.JSON = 'json'; +export const TYPE_JSON = 'json'; /** * The 'js' type constant. - * * @type {string} * @constant * @public */ -Constants.prototype.JS = 'js'; +export const TYPE_JS = 'js'; /** * The type constants assembled in an array: `[ 'yaml', 'json', 'js' ]`. - * * @type {string[]} * @constant * @public */ -Constants.prototype.TYPES = [ constants.YAML, constants.JSON, constants.JS ]; +export const TYPES = [TYPE_YAML, TYPE_JSON, TYPE_JS]; /** - * The default file indention (4 SPACEs). + * A map for extensions to type. * + * @type {{yml: string, yaml: string, js: string, json: string}} + * @public + */ +export const TYPE_MAP = { + yml: TYPE_YAML, + yaml: TYPE_YAML, + js: TYPE_JS, + json: TYPE_JSON +}; + +/** + * The default file indention (4 SPACEs). * @type {number} * @constant * @public */ -Constants.prototype.DEFAULT_INDENT = 4; +export const DEFAULT_INDENT = 2; /** * The minimum file indention (0 SPACE). - * * @type {number} * @constant * @public */ -Constants.prototype.MIN_INDENT = 0; +export const MIN_INDENT = 0; /** * The maximum file indention (8 SPACEs). - * * @type {number} * @constant * @public */ -Constants.prototype.MAX_INDENT = 8; +export const MAX_INDENT = 8; /** * The default `origin` value: 'yaml'. - * * @type {string} * @constant * @public */ -Constants.prototype.DEFAULT_ORIGIN = constants.YAML; +export const DEFAULT_ORIGIN = TYPE_YAML; /** * The default `origin` value: 'js'. - * * @type {string} * @constant * @public */ -Constants.prototype.DEFAULT_TARGET = constants.JS; +export const DEFAULT_TARGET = TYPE_JS; /** * Whether to overwrite existing file or object on output. - * * @type {boolean} * @constant * @public */ -Constants.prototype.DEFAULT_FORCE_FILE_OVERWRITE = false; +export const DEFAULT_FORCE_FILE_OVERWRITE = false; /** * The `origin` description value. - * * @type {string} * @constant * @public */ -Constants.prototype.ORIGIN_DESCRIPTION = 'if not given, the type is tried to be inferred from the extension of source path, else it is \'' + constants.DEFAULT_ORIGIN + '\''; +export const ORIGIN_DESCRIPTION = 'if not given, the type is tried to be inferred from the extension of source path, ' + + 'else it is \'' + DEFAULT_ORIGIN + '\''; /** * The `target` description value. - * * @type {string} * @constant * @public */ -Constants.prototype.TARGET_DESCRIPTION = 'if not given, the type is tried to be inferred from the extension of destination path, else it is \'' + constants.DEFAULT_TARGET + '\''; +export const TARGET_DESCRIPTION = 'if not given, the type is tried to be inferred from the extension of destination' + + ' path, else it is \'' + DEFAULT_TARGET + '\''; /** * The `dest` description value. - * * @type {string} * @constant * @public */ -Constants.prototype.DEST_DESCRIPTION = 'storing relative to input file'; +export const DEST_DESCRIPTION = 'storing relative to input file'; /** * The `src` exports identifier value to read. - * * @type {string} * @public * @constant * @example * module.exports.foo = {...}; // here 'foo' is the identifier for an object to read from the source! */ -Constants.prototype.DEFAULT_JS_IMPORTS_IDENTIFIER = undefined; +export const DEFAULT_JS_IMPORTS_IDENTIFIER = undefined; /** * The `dest` exports identifier value to write. - * * @type {string} * @public * @constant */ -Constants.prototype.DEFAULT_JS_EXPORTS_IDENTIFIER = undefined; +export const DEFAULT_JS_EXPORTS_IDENTIFIER = undefined; /** * The default options. - * * @constant * @namespace * @property {string} origin=yaml - The default origin type. @@ -186,112 +166,102 @@ Constants.prototype.DEFAULT_JS_EXPORTS_IDENTIFIER = undefined; * @see {@link TARGET_DESCRIPTION} * @see {@link DEST_DESCRIPTION} */ -Constants.prototype.DEFAULT_OPTIONS = { - origin: constants.ORIGIN_DESCRIPTION, - target: constants.TARGET_DESCRIPTION, - dest: constants.DEST_DESCRIPTION, - indent: constants.DEFAULT_INDENT, - force: constants.DEFAULT_FORCE_FILE_OVERWRITE, - imports: constants.DEFAULT_JS_IMPORTS_IDENTIFIER, - exports: constants.DEFAULT_JS_EXPORTS_IDENTIFIER +export const DEFAULT_OPTIONS = { + origin: ORIGIN_DESCRIPTION, + target: TARGET_DESCRIPTION, + dest: DEST_DESCRIPTION, + indent: DEFAULT_INDENT, + force: DEFAULT_FORCE_FILE_OVERWRITE, + imports: DEFAULT_JS_IMPORTS_IDENTIFIER, + exports: DEFAULT_JS_EXPORTS_IDENTIFIER, }; /** * The transformation direction YAML ⇒ JS. - * * @type {string} * @constant * @public */ -Constants.prototype.YAML_TO_JS = 'yaml2js'; +export const YAML_TO_JS = 'yaml2js'; /** * The transformation direction YAML ⇒ JSON. - * * @type {string} * @constant * @public */ -Constants.prototype.YAML_TO_JSON = 'yaml2json'; +export const YAML_TO_JSON = 'yaml2json'; /** * The transformation direction JS ⇒ YAML. - * * @type {string} * @constant * @public */ -Constants.prototype.JS_TO_YAML = 'js2yaml'; +export const JS_TO_YAML = 'js2yaml'; /** * The transformation direction JSON ⇒ YAML. - * * @type {string} * @constant * @public */ -Constants.prototype.JSON_TO_YAML = 'json2yaml'; +export const JSON_TO_YAML = 'json2yaml'; /** * The transformation direction JSON ⇒ JS. - * * @type {string} * @constant * @public */ -Constants.prototype.JSON_TO_JS = 'json2js'; +export const JSON_TO_JS = 'json2js'; /** * The transformation direction JS ⇒ JSON. - * * @type {string} * @constant * @public */ -Constants.prototype.JS_TO_JSON = 'js2json'; +export const JS_TO_JSON = 'js2json'; /** * The transformation direction YAML ⇒ YAML. - * * @type {string} * @constant * @public */ -Constants.prototype.YAML_TO_YAML = 'yaml2yaml'; +export const YAML_TO_YAML = 'yaml2yaml'; /** * The transformation direction JSON ⇒ JSON. - * * @type {string} * @constant * @public */ -Constants.prototype.JSON_TO_JSON = 'json2json'; +export const JSON_TO_JSON = 'json2json'; /** * The transformation direction JS ⇒ JS. - * * @type {string} * @constant * @public */ -Constants.prototype.JS_TO_JS = 'js2js'; +export const JS_TO_JS = 'js2js'; /** * The transformation directions. - * * @type {string[]} * @constant * @public */ -Constants.prototype.TRANSFORMATIONS = [ - constants.YAML_TO_JS, - constants.YAML_TO_JSON, - constants.JS_TO_YAML, - constants.JSON_TO_YAML, - constants.JSON_TO_JS, - constants.JS_TO_JSON, - constants.YAML_TO_YAML, - constants.JSON_TO_JSON, - constants.JS_TO_JS +export const TRANSFORMATIONS = [ + YAML_TO_JS, + YAML_TO_JSON, + JS_TO_YAML, + JSON_TO_YAML, + JSON_TO_JS, + JS_TO_JSON, + YAML_TO_YAML, + JSON_TO_JSON, + JS_TO_JS ]; diff --git a/src/debug-log.js b/src/debug-log.js new file mode 100644 index 0000000..08a0366 --- /dev/null +++ b/src/debug-log.js @@ -0,0 +1,6 @@ +export const debug = typeof process.env.JYT_DEBUG !== 'undefined' ? + console.log.bind(null, '[DEBUG][jyt]:') : + (() => null); +export const error = typeof process.env.JYT_ERROR !== 'undefined' ? + console.error.bind(null, '[ERROR][jyt]:') : + (() => null); diff --git a/src/middleware.js b/src/middleware.js new file mode 100644 index 0000000..32a161c --- /dev/null +++ b/src/middleware.js @@ -0,0 +1,71 @@ +/** + * @module jy-transform:middlware + * @description The module exporting the {@link external:joi.Extension}s for option validations. + * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension Joi.extend(extension) method}. + * @public + */ + +/** + * Promise which reflects the identity of passed JSON: `f(object) → object`. + * + * @param {Object} object - The JS object which is resolved by Promise. + * @returns {Promise.} - A Promise resolving the passed JS `object`. + * @private + */ +function identity(object) { + return Promise.resolve(object); +} + +/** + * Middleware Promise which reflects the identity of passed JS: `f(object) → object`. + * + * @param {Object} object - The object which is returned in Promise. + * @returns {Promise.} A Promise resolving the passed JS object. + * @example + * import { identityMiddleware } from './lib/middleware'; + * transformer.transform(options, identityMiddleware) + * .then((object) => { + * // ... + * }): + * @protected + */ +export function identityMiddleware(object) { // TODO remove + return identity(object); +} + +/** + * Ensure that the given middleware Promise is a function if set. + * If not set a new JSON 'identity' Promise is returned which simply passes + * a JSON object. + * + * @param {Function} middleware - This middleware Promise can be used to intercept + * the JSON object for altering he passed JSON, the function signature is + * `function(object).` **NOTE:** the Promise has to return the processed JSON. + * @returns {Function} - The given middleware Promise or a new JSON 'identity' middleware Promise function. + * @throws {TypeError} - Will throw this error when the passed `middleware` is not type of `Function`. + * @example + * import { ensureMiddleware } from './lib/middleware'; + * const myMiddleware = async (object) => { + * //...do something with object + * return object; + * }; + * transformer.transform(options, ensureMiddleware(myMiddleware)) + * .then((transformedObject) => { + * //... + * }): + * @protected + */ +export function ensureMiddleware(middleware) { + if (middleware !== undefined && (typeof middleware !== 'function')) { + throw new TypeError('The provided middleware is not a Function type'); + } + if (!middleware) { + return identity; + } + return middleware; +} + +export default { + identityMiddleware, + ensureMiddleware, +}; diff --git a/src/reader.js b/src/reader.js new file mode 100644 index 0000000..8cf3972 --- /dev/null +++ b/src/reader.js @@ -0,0 +1,273 @@ +import jsYaml from 'js-yaml'; +import promisify from 'promisify-es6'; +import { Buffer } from 'buffer'; +import path from 'path'; +import fs from 'fs'; +import isStream from 'is-stream'; +import stringify from 'json-stringify-safe'; +import logger from 'cli'; +import { readerOptionsSchema } from './validation/options-schema'; +import Joi from './validation/joi-extensions'; +import { + UTF8, + TYPE_YAML, + TYPE_JS, + TYPE_JSON, +} from './constants'; + +const fsPromisified = promisify(fs); + +/** + * Creates a function to read from the passed source in to the given buffer array. + * + * @param {stream.Readable} readable - The source to read from. + * @param {Array} bufs - The temporary buffer array. + * @returns {Function} - The function which reads and buffers. + * @private + */ +function createReadableFunction(readable, bufs) { + return () => { + let chunk; + while (null !== (chunk = readable.read())) { + logger.debug('JSON chunk: ', chunk); + bufs.push(chunk); + } + }; +} + +/** + * Reads from a passed stream and resolves by callback. + * + * @param {Stream.Readable} readable - The source to read from. + * @param {Function} resolve - Callback for success case. + * @param {Function} reject - Callback for Error case. + * @param {string} origin - Origin type, must be 'yaml' or 'json'/'js'. + * @private + */ +function readFromStream(readable, resolve, reject, origin) { + const bufs = []; + readable + .on('readable', createReadableFunction(readable, bufs)) + .on('error', err => reject(err)) + .on('end', () => { + const buffer = Buffer.concat(bufs); + try { + logger.debug(origin + ' reading from Readable'); + if (origin === TYPE_JSON || origin === TYPE_JS) { + resolve(JSON.parse(buffer.toString(UTF8))); + } else { // HINT: commented (see below): if (origin === YAML) { + resolve(jsYaml.safeLoad(buffer.toString(UTF8))); + } + // HINT: for the sake of test coverage it's commented, since this is a private method + // we have control over options.origin inside this class! + // else { + // reject(new Error('Unsupported type: ' + origin + ' to read from Readable')); + // } + } catch (err) { // probably a SyntaxError for JSON or a YAMLException + logger.error('Unexpected error: ' + err.stack); + readable.emit('error', err); // send to .on('error',... + } + }); +} + +/** + * Reads the data from a given JS or JSON source. + * + * @param {Options} options - Contains the JS/JSON source reference to read from. + * @returns {Promise.} Contains the read JS object. + * @public + * @example + * var Reader = require('jy-transform').Reader; + * var logger = ...; + * var reader = new Reader(logger); + * + * // --- from file path + * + * var options = { + * src: 'foo.js' + * }; + * + * reader.readJs(options) + * .then(function (obj){ + * logger.info(JSON.stringify(obj)); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // --- from Readable + * + * options = { + * src: fs.createReadStream('foo.js') + * }; + * + * reader.readJs(options) + * .then(function (obj){ + * logger.info(JSON.stringify(obj)); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // --- from object + * + * options = { + * src: { + * foo: 'bar' + * } + * }; + * + * reader.readJs(options) + * .then(function (obj){ + * logger.info(JSON.stringify(obj)); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + */ +export async function readJs(options) { + logger.debug('OPTIONS BEFORE ASSERTING IN readJs:::' + JSON.stringify(options)); + const assertedOptions = await Joi.validate(options, readerOptionsSchema); + return new Promise((resolve, reject) => { + if (typeof assertedOptions.src === 'string') { + try { + const resolvedPath = path.resolve('', assertedOptions.src); + + if ((path.extname(assertedOptions.src) === '.js' || options.origin === TYPE_JS) && options.imports) { + // eslint-disable-next-line import/no-dynamic-require, global-require + const json = require(resolvedPath)[options.imports]; + logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(json, null, 4)); + if (!json) { + reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object' + + ' but could not find this object, pls ensure that file ' + assertedOptions.src + + ' contains it.')); + } else { + resolve(json); + } + } else { + // eslint-disable-next-line import/no-dynamic-require, global-require + resolve(require(resolvedPath)); + } + } catch (err) { // probably a SyntaxError + logger.error('Unexpected error: ' + err.stack); + reject(err); + } + } else if (isStream.readable(assertedOptions.src)) { + readFromStream(assertedOptions.src, resolve, reject, TYPE_JSON); + } else if (options.imports) { + const obj = assertedOptions.src[options.imports]; + logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(obj, null, 4)); + if (!obj) { + reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object ' + + 'but could not find this object, pls ensure that object source contains it.')); + } else { + resolve(obj); + } + } else { + resolve(assertedOptions.src); + } + }); +} + +/** + * Loads a single YAML source containing document and returns a JS object. + * *NOTE:* this function does not understand multi-document sources, it throws + * exception on those. + * + * @param {Options} options - Contains the YAML source reference to read from. + * @returns {Promise.} Contains the read JS object. + * @public + * @example + * var Reader = require('jy-transform').Reader; + * var logger = ...; + * var reader = new Reader(logger); + * + * // --- from file path + * + * options = { + * src: 'foo.yml' + * }; + * + * reader.readYaml(options) + * .then(function (obj){ + * logger.info(JSON.stringify(obj)); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // --- from Readable + * + * options = { + * src: fs.createReadStream('foo.yml') + * }; + * + * reader.readJs(options) + * .then(function (obj){ + * logger.info(JSON.stringify(obj)); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + */ +export async function readYaml(options) { + logger.debug('OPTIONS BEFORE ASSERTING IN readYaml::: ' + JSON.stringify(options)); + const assertedOptions = await Joi.validate(options, readerOptionsSchema); + const self = this; + return new Promise((resolve, reject) => { + if (typeof assertedOptions.src === 'string') { + // load source from YAML file + fsPromisified.readFile(assertedOptions.src, UTF8) + .then((yaml) => { + logger.debug('YAML loaded from file ' + assertedOptions.src); + try { + resolve(jsYaml.safeLoad(yaml)); + } catch (err) { // probably a YAMLException + logger.error('Unexpected error: ' + err.stack); + reject(err); + } + }) + .catch((err) => { + self.logger.error('Unexpected error: ' + err.stack); + reject(err); + }); + } else if (isStream.readable(assertedOptions.src)) { + readFromStream(assertedOptions.src, resolve, reject, TYPE_YAML); + } else { + resolve(assertedOptions.src); + } + }); +} + +// /** +// * Parses string as single YAML source containing multiple YAML document and turns a JS objects array. +// * +// * NOTE: This function does not understand multi-document sources, it throws exception on those. +// * +// * @param src {string} The YAML source to read. +// * @returns {Promise} Containing an array holding the multiple JSON objects. +// * @public +// */ +// Reader.prototype.readYamls = function (src) { +// // load source from YAML source +// return fsPromisified.readFile(src, 'utf8') +// .then(function(yaml) { +// logger.debug('YAML documents loaded from ' + src); // TOD: can this be shortened? -> return Promise.resolve(jsYaml.safeLoadAll(yaml)); +// return Promise.resolve().then(function () { +// var jsDocs = []; +// return jsYaml.safeLoadAll(yaml, function (doc) { // TOD this will not work in Promise environment!!! +// self.logger.trace(doc); +// jsDocs.push(doc); +// }); +// }); +// }); +// }; +// } + +export default { + readJs, + readYaml, +}; diff --git a/src/transformer.js b/src/transformer.js new file mode 100644 index 0000000..1f82aca --- /dev/null +++ b/src/transformer.js @@ -0,0 +1,389 @@ +import logger from 'cli'; +import { readJs, readYaml } from './reader'; +import { writeJs, writeJson, writeYaml } from './writer'; +//import { LogWrapper } from './log-wrapper'; +import { transformerOptionsSchema } from './validation/options-schema'; +import Joi from './validation/joi-extensions'; +import { ensureMiddleware } from './middleware'; +import { TRANSFORMATIONS } from './constants'; + +/** + * This method validates the transformation process described by the given + * options and provides the validate and enriched options and according name + * to resolve a proper function. + * + * @param {Options} options - The configuration for a transformation. + * @returns {Promise} - A Promise containing the passed `options` object and a 'transformation' string in an array. + * @example + * var OptionsHandler = require('./options-handler.js'); + * var logger = ...; + * var optionsHandler = new OptionsHandler(logger); + * + * optionsHandler.validateTransformation(options) + * .spread(function (validatedOptions, transformation) { + * ... + * )): + * @see {@link transformations} + * @public + */ +async function createAndValidateTransformation(options) { + const transformation = options.origin + '2' + options.target; + if (TRANSFORMATIONS.indexOf(transformation) < 0) { + throw new Error('Unsupported target type transformation \'' + options.origin + ' -> ' + + options.target + '\' configured in options.'); + } + return transformation; +} + +/** + * Internal delegate function to execute transformation logic (ITMO): + * - Input (read) + * - Transform + Middleware + * - Output (write) + * + * @param {Options} options - The configuration for a transformation. + * @param {Function} read - The reader function. + * @param {Function} [middleware] - The middleware to apply. + * @param {Function} write - The writer functions. + * @example + * var options = {...}; + * var middleware = function (obj) { + * ... + * }; + * itmo(options, readYaml, middleware, writeJson); + * @private + */ +function itmo(options, read, middleware, write) { + return read(options) + .then(ensureMiddleware(middleware)) + .then(json => write(json, options)); +} + +// //////////////////////////////////////////////////////////////////////////// +// TRANSFORMATION METHODS (DELEGATES) +// //////////////////////////////////////////////////////////////////////////// + +/** + * Convert YAML to JS. + * + * @param {Options} options - The configuration for a transformation. + * @param {Function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is `function(object)`. + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} Will throw plain error when writing to JS destination failed due to any reason. + * @example + * import { transformer } from 'jy-transform'; + * const options = {...}; + * var middleware = function (obj) { + * ... + * }; + * yamlToJs(options, middleware); + * @see itmo + * @private + */ +function yamlToJs(options, middleware) { + return itmo(options, readYaml, middleware, writeJs); +} + +/** + * Convert YAML to JSON. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { + * ... + * }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.yamlToJson(options, middleware); + * @see itmo + * @private + */ +function yamlToJson(options, middleware) { + return itmo(options, readYaml, middleware, writeJson); +} + +/** + * Convert JS to YAML. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { + * ... + * }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.jsToYaml(options, middleware); + * @see itmo + * @private + */ +function jsToYaml(options, middleware) { + return itmo(options, readJs, middleware, writeYaml); +} + +/** + * Convert JSON to JS. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { + * ... + * }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.jsonToJs(options, middleware); + * @see itmo + * @private + */ +function jsonToJs(options, middleware) { + return itmo(options, readJs, middleware, writeJs); +} + +/** + * Convert JS to JSON. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { + * ... + * }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.jsToJson(options, middleware); + * @see itmo + * @private + */ +function jsToJson(options, middleware) { + return itmo(options, readJs, middleware, writeJson); +} + +/** + * Convert YAML to YAML. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { + * ... + * }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.yamlToYaml(options, middleware); + * @see itmo + * @private + */ +function yamlToYaml(options, middleware) { + return itmo(options, readYaml, middleware, writeYaml); +} + +/** + * Convert JSON to JSON. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} - Will throw this error when the passed `middleware` + * is not type of `Function`. + * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. + * @example + * var logger = ...; + * var options = {...}; + * var middleware = function (obj) { + * ... + * }; + * var Transformer = require('jy-transform'); + * var transformer = new Transformer(logger); + * transformer.jsonToJson(options, middleware); + * @see itmo + * @private + */ +function jsonToJson(options, middleware) { + return itmo(options, readJs, middleware, writeJson); +} + +/** + * Convert JS to JS. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JS object for manipulation. The function signature is: + * ``` + * function(object) + * ``` + * **NOTE:** the Promise has to return the processed JS object! + * @returns {Promise.} Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. + * @throws {Error} Will throw plain error when writing to JS destination failed due to any reason. + * @example + * var options = {...}; + * var middleware = async (json) { + * ... + * }; + * jsToJs(options, middleware); + * @see itmo + * @private + */ +function jsToJs(options, middleware) { + return itmo(options, readJs, middleware, writeJs); +} + +/** + * A transformation name to internal function mapping. + * + * @namespace + * @property {Function} yaml2js - The transformation function for YAML ⇒ JS. + * @property {Function} yaml2json - The transformation function for YAML ⇒ JSON. + * @property {Function} yaml2yaml - The transformation function for YAML ⇒ YAML. + * @property {Function} json2yaml - The transformation function for JSON ⇒ YAML. + * @property {Function} json2js - The transformation function for JSON ⇒ JS. + * @property {Function} json2json - The transformation function for JSON ⇒ JSON. + * @property {Function} js2yaml - The transformation function for JS ⇒ YAML. + * @property {Function} js2json - The transformation function for JS ⇒ JSON. + * @property {Function} js2js - The transformation function for JS ⇒ JS. + * @private + */ +const transformations = { + yaml2js: yamlToJs, + yaml2json: yamlToJson, + yaml2yaml: yamlToYaml, + json2yaml: jsToYaml, + json2js: jsonToJs, + json2json: jsonToJson, + js2yaml: jsToYaml, + js2json: jsToJson, + js2js: jsToJs +}; + +// /** +// * Constructs the `Transformer` with options and an (optional) logger. +// * +// * @param {(logger|cli|console)} [logger=console] - Logger instance. +// * @returns {Transformer} - The instance. +// * @constructor +// * @class This class provides all methods usable to handle YAML, JSON and JS and +// * their transformations. +// * @example +// * var logger = ...; +// * var Transformer = require('jy-transform'); +// * var transformer = new Transformer(logger); +// */ +// export class Transformer { + + // const logger = new LogWrapper(logger); +// const writer = new Writer(logger); +// const reader = new Reader(logger); + +// //////////////////////////////////////////////////////////////////////////// +// PUBLIC API +// //////////////////////////////////////////////////////////////////////////// + +/** + * The entry method for all transformation accepting a configuration object and + * an (optional) middleware function. + * + * @param {Options} options - The configuration for a transformation. + * @param {function} [middleware] - This middleware Promise can be used to intercept + * the JSON object for altering the passed JSON, the function signature is: + * ``` + * function(json) + * ``` + *

+ * **NOTE:** the Promise has to return the processed JSON! + * @returns {Promise.} Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. + * @throws {Error} Will throw plain error when writing to file failed due to any reason. + * @public + * @example + * import { transform } from 'jy-transform'; + * const options = {...}; + * const middleware = async (json) { + * json.myproperty = 'new value'; + * return json; + * }; + * + * transform(options, middleware) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + */ +export async function transform(options, middleware) { + logger.debug('transform'); + const ensuredOptions = await Joi.validate(options, transformerOptionsSchema); + const transformation = await createAndValidateTransformation(ensuredOptions); + logger.debug('Calling transformation: ' + transformation); + return await transformations[transformation](ensuredOptions, middleware); +} + +export default { + transform, +}; diff --git a/src/type-definitions.js b/src/type-definitions.js new file mode 100644 index 0000000..e2debc3 --- /dev/null +++ b/src/type-definitions.js @@ -0,0 +1,62 @@ +/** + * @module type-definitions + * @description The type definitions for this module. + */ + +// ///////////////////////////////////////////////////////////////////////////// +// EXTERNAL DEFINITIONS +// ///////////////////////////////////////////////////////////////////////////// + +/** + * Hapi.js Joi. + * @external joi + * @see {@link https://github.com/hapijs/joi Hapi Joi} + */ + +/** + * Joi validation error. + * @typedef ValidationError + * @memberof external:joi + * @see {@link hhttps://github.com/hapijs/joi/blob/v10.2.0/API.md#errors Joi errors} + */ + +/** + * The validation schema. Can be a {@link external:joi} type object or a plain object + * where every key is assigned a {@link external:joi} type object. + * @typedef Schema + * @memberof external:joi + * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#joi Joi API} + */ + +/** + * Hapi.js Joi schema extension. + * @typedef Extension + * @memberof external:joi + * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension Hapi Joi Extension} + */ + +/** + * Joi `validate` method. + * @callback validate + * @memberof external:joi + * @see {@link https://github.com/hapijs/joi/blob/master/API.md#validatevalue-schema-options-callback Joi.validate} + */ + +// ///////////////////////////////////////////////////////////////////////////// +// INTERNAL DEFINITIONS +// ///////////////////////////////////////////////////////////////////////////// + +/** + * The configuration properties provided to the framework functions. + * @typedef {object} Options + * @property {string} [origin=yaml] - The origin type. + * @property {string} [target=js] - The target type. + * @property {(string|Stream.Readable|object)} src - The source (if `string` type is treated as a file path). + * @property {(string|Stream.Writable|object)} [dest] - The destination (if `string` type is treated as a file path). + * @property {number} [indent=2] - The indention in files. + * @property {string} [imports=undefined] - The exports name for reading from JS source files + * or objects only. + * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. + * @property {string} [force=false] - Force overwriting of existing output files on write phase. + * @public + */ diff --git a/src/validation/joi-extensions-file-helper.js b/src/validation/joi-extensions-file-helper.js new file mode 100644 index 0000000..2632ecf --- /dev/null +++ b/src/validation/joi-extensions-file-helper.js @@ -0,0 +1,28 @@ +import fs from 'fs'; +import path from 'path'; +import { debug, error } from '../debug-log'; + +/** + * @module validation:joi-extensions-file-helper + * @description An (extended) Joi validation schema helper functions for the module options on FS validation. + * @protected + */ + +/** + * Checks if given `pathStr` is an existing file after resolving `pathStr` relative to CWD. + * + * @param {string} pathStr - The string to check for being a file. + * @returns {boolean} Value `true` if it is a file and exists, else `false`. + * @protected + */ +export function isExistingFile(pathStr) { + debug('>>>>>>>>>>>>>DEBUG ===================================') + error('>>>>>>>>>>>>>ERROR ===================================' + new Error('JYT Error')) + const filePath = path.resolve(pathStr); + try { + const stats = fs.statSync(filePath); + return stats.isFile(); + } catch (err) { + return false; + } +} diff --git a/src/validation/joi-extensions-identifier-helper.js b/src/validation/joi-extensions-identifier-helper.js new file mode 100644 index 0000000..71686bc --- /dev/null +++ b/src/validation/joi-extensions-identifier-helper.js @@ -0,0 +1,32 @@ +/** + * @module validation:joi-extensions-identifier-helper + * @description An (extended) Joi validation schema helper function for the module options to validate ES6 identifiers. + * @protected + */ + +/** + * Created at [Generating a regular expression to match valid JavaScript identifiers] + * (https://mathiasbynens.be/demo/javascript-identifier-regex). + * @type {RegExp} + * @private + */ +// eslint-disable-next-line max-len, no-useless-escape +const identifierRegExpECMAScript6 = /^(?!(?:do|if|in|for|let|new|try|var|case|else|enum|eval|null|this|true|void|with|await|break|catch|class|const|false|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$)(?:[\$A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D])(?:[\$0-9A-Z_a-z\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF])*$/; + +/** + * This method checks if a given `identifier` is a valid ECMAScript 6 identifier. + * + * @param {string} identifier - The identifier to check. + * @returns {boolean} A `true` if valid, else `false`. + * @protected + * @example + * import { isValidEs6Identifier } from './validation/joi-extensions-identifier-helper.js'; + * const identifier = 'foo'; + * console.log('valid = ' + isValidEs6Identifier(identifier)); + */ +export function isValidEs6Identifier(identifier) { + if (typeof identifier !== 'string') { + return false; + } + return identifierRegExpECMAScript6.test(identifier); +} diff --git a/src/validation/joi-extensions.js b/src/validation/joi-extensions.js new file mode 100644 index 0000000..f31300c --- /dev/null +++ b/src/validation/joi-extensions.js @@ -0,0 +1,54 @@ +import JoiBase from 'joi'; +import promisify from 'promisify-es6'; +import { isExistingFile } from './joi-extensions-file-helper'; +import { isValidEs6Identifier } from './joi-extensions-identifier-helper'; + +/** + * @module validation:joi-extension + * @description The module exporting the {@link external:joi.Extension}s for option validations. + * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension Joi.extend(extension) method}. + * @public + */ + +/** + * The common {@link external:joi.Schema} validation extensions: + * - `existingFile` - needs to be an absolute or relative path to an existing file. + * - `validEs6Identifier` - needs to be a valid ECMAScript 6 identifier. + * @type {external:joi.Extension} + * @private + */ +export const EXTENSIONS = { + base: JoiBase.string(), + name: 'string', + language: { + existingFile: 'needs to be an absolute or relative path to an existing file [given "value": {{v}}, {{err}}]', + validEs6Identifier: 'needs to be a valid ECMAScript 6 identifier [given "value": {{v}}, {{err}}]' + }, + rules: [ + { + name: 'existingFile', + validate(params, value, state, options) { + if (isExistingFile(value)) { + return value; // Everything is OK + } + // Generate an error, state and options need to be passed + return this.createError('string.existingFile', { v: value }, state, options); + } + }, + { + name: 'validEs6Identifier', + validate(params, value, state, options) { + if (isValidEs6Identifier(value)) { + return value; // Everything is OK + } + // Generate an error, state and options need to be passed + return this.createError('string.validEs6Identifier', { v: value }, state, options); + } + } + ] +}; + +const JoiExtended = JoiBase.extend(EXTENSIONS); +JoiExtended.validate = promisify(JoiExtended.validate); + +export default JoiExtended; diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-helper.js new file mode 100644 index 0000000..994d939 --- /dev/null +++ b/src/validation/options-schema-helper.js @@ -0,0 +1,108 @@ +import path from 'path'; +import logger from 'cli'; +import isStream from 'is-stream'; +import { + TYPE_MAP, + DEFAULT_ORIGIN, + DEFAULT_TARGET, +} from '../constants'; + +/** + * @module validation:options-schema-helper + * @description The module options schema used in {@link module:options-validator}. + * @type {Object} + * @protected + * @see {@link module:validation:options-validator} + */ + +/** + * Infer from path extension to a type using default value as fallback. + * + * @param {string} pathStr - The file path with or without extension. + * @param {boolean} origin - If the type is origin (true) or target (false). + * @param {string} defaultValue - The default value to use if type cannot be inferred from path. + * @returns {string} A type value. + * @private + */ +const getTypeFromFilePath = (pathStr, origin, defaultValue) => { + let type; + try { + let ext = path.extname(pathStr); + if (ext.charAt(0) === '.') { + ext = ext.substr(1); + } + type = TYPE_MAP[ext]; + if (!type) { + type = defaultValue; + } + } catch (err) { + logger.error(err.stack); + type = defaultValue; + } + return type; +}; + +/** + * + * @param context + * @returns {string} + * @protected + */ +export const inferOriginDefaultFromStreamReadableFilePath = (context) => { + let destType; + if (isStream.readable(context.dest)) { + destType = getTypeFromFilePath(context.src.path, true, DEFAULT_ORIGIN); + } else { + destType = DEFAULT_TARGET; + } + return destType; +}; + +/** + * + * @param context + * @returns {string} + * @protected + */ +export const inferOriginDefaultFromFilePath = (context) => { + return getTypeFromFilePath(context.src, true, DEFAULT_ORIGIN); +}; + +/** + * + * @param context + * @returns {string} + * @protected + */ +export const inferTargetDefaultFromStreamWritableFilePath = (context) => { + let destType; + if (isStream.writable(context.dest)) { + destType = getTypeFromFilePath(context.dest.path, false, DEFAULT_TARGET); + } else { + destType = DEFAULT_TARGET; + } + return destType; +}; + +/** + * + * @param context + * @returns {string} + * @protected + */ +export const inferTargetDefaultFromFilePath = (context) => { + let destType; + if (typeof context.dest === 'string') { + destType = getTypeFromFilePath(context.dest, false, DEFAULT_TARGET); + } else { + destType = DEFAULT_TARGET; + } + return destType; +}; + +export default { + inferOriginDefaultFromStreamReadableFilePath, + inferTargetDefaultFromStreamWritableFilePath, + inferOriginDefaultFromFilePath, + inferTargetDefaultFromFilePath, +}; diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js new file mode 100644 index 0000000..f2b1b96 --- /dev/null +++ b/src/validation/options-schema.js @@ -0,0 +1,145 @@ +import { Stream } from 'stream'; +import Joi from './joi-extensions'; +import { + inferOriginDefaultFromStreamReadableFilePath, + inferTargetDefaultFromStreamWritableFilePath, + inferOriginDefaultFromFilePath, + inferTargetDefaultFromFilePath, +} from './options-schema-helper'; +import { + TYPE_YAML, + TYPE_JS, + TYPE_JSON, + DEFAULT_FORCE_FILE_OVERWRITE, + DEFAULT_INDENT, + MIN_INDENT, + MAX_INDENT, +} from '../constants'; + +/** + * @module validation:options-schema + * @description The module options schema used in {@link module:options-validator}. + * @type {Object} + * @protected + * @see {@link module:options-validator} + */ + +// ///////////////////////////////////////////////////////////////////////////// +// SCHEMAS +// ///////////////////////////////////////////////////////////////////////////// + +/** + * The prepared {@link external:joi.JoiSchema} for validating the {@link Reader} options. + * @type {JoiSchema} + * @constant + * @private + */ +export const readerOptionsSchema = Joi.object().keys({ + src: Joi + .alternatives().try( + Joi.string() + .min(1) + .existingFile(), + Joi.object().type(Stream.Readable), + Joi.object().type(Object), + ) + .required() + .description('The input source (if string type it is treated as a file path).'), + origin: Joi + .when('src', { + is: Joi.object().type(Stream.Readable), + then: Joi + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(inferOriginDefaultFromStreamReadableFilePath, + 'tried origin default inferred from src type if not set (Stream.Readable)'), + otherwise: Joi + .when('src', { + is: Joi.string(), + then: Joi + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(inferOriginDefaultFromFilePath, 'tried origin default inferred from src type if not set (String)'), + otherwise: Joi // else could only be JS Object + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(TYPE_JS) + }), + }) + .description('The origin type of input.'), + imports: Joi + .string() + .validEs6Identifier() + .description('The name of property to import while reading a JS input source.'), +}).unknown() + .required(); + +/** + * The prepared {@link external:joi.JoiSchema} for validating the {@link Writer} options. + * @type {JoiSchema} + * @constant + * @private + */ +export const writerOptionsSchema = Joi.object().keys({ + dest: Joi + .alternatives().try( + Joi.string() // TODO must be existing file (relative or not? -> check) + .min(1), + Joi.object().type(Stream.Writable), + Joi.object().type(Object), + ) + .required() + .description('The output destination (if string type it is treated as a file path).'), + target: Joi + .when('dest', { + is: Joi.object().type(Stream.Writable), + then: Joi + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(inferTargetDefaultFromStreamWritableFilePath, + 'tried target default inferred from dest type if not set (Stream.Writable)'), + otherwise: Joi + .when('dest', { + is: Joi.string(), + then: Joi + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(inferTargetDefaultFromFilePath, 'tried target default inferred from dest type if not set (String)'), + otherwise: Joi // check + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(TYPE_JS), + }), + }) + .description('The target type of output.'), + exports: Joi + .string() + .validEs6Identifier() + .description('The name of property to export while writing a JS object to a JS output destination.'), + indent: Joi + .number() + .integer() + .min(MIN_INDENT) + .max(MAX_INDENT) + .default(DEFAULT_INDENT) + .description('The indention for pretty-print.'), + force: Joi + .boolean() + .default(DEFAULT_FORCE_FILE_OVERWRITE) + .description('Force overwriting of existing output files on write phase.'), +}).unknown() + .required(); + +/** + * The prepared {@link external:joi.JoiSchema} for validating the {@link Transformer} options. + * @type {JoiSchema} + * @constant + * @private + */ +export const transformerOptionsSchema = readerOptionsSchema.concat(writerOptionsSchema).required(); + +export default { + readerOptionsSchema, + writerOptionsSchema, + transformerOptionsSchema, +}; diff --git a/src/writer.js b/src/writer.js new file mode 100644 index 0000000..baaa017 --- /dev/null +++ b/src/writer.js @@ -0,0 +1,441 @@ +import serializeJs from 'serialize-js'; +import jsYaml from 'js-yaml'; +import promisify from 'promisify-es6'; +import mkdirp from 'mkdirp-then'; +import jsonStringifySafe from 'json-stringify-safe'; +import path from 'path'; +import fs from 'fs'; +import os from 'os'; +import isStream from 'is-stream'; +import logger from 'cli'; +import { transformerOptionsSchema } from './validation/options-schema'; +import Joi from './validation/joi-extensions'; +import { + UTF8, + TYPE_YAML, + TYPE_JS, + TYPE_JSON, +} from './constants'; + +const fsPromisified = promisify(fs); + +// //////////////////////////////////////////////////////////////////////////// +// METHODS (PRIVATE) +// //////////////////////////////////////////////////////////////////////////// + +/** + * Creates a potential named `'module.exports[.exportsTo]'` string. + * + * @param {string} [exportsTo] - The export name. + * @returns {Promise.} Resolves with the exports string. + * @private + */ +function createExportsString(exportsTo) { + return new Promise((resolve) => { + let exports = 'module.exports'; + if (exportsTo) { + exports += '.' + exportsTo + ' = '; + } else { + exports += ' = '; + } + resolve(exports); + }); +} + +/** + * Serialize a JS object to string. + * + * @param {Object} object - The JS Object to serialize. + * @param {number} indent - The indention. + * @param {string} [exportsTo] - Name for export (*IMPORTANT:* must be a valid ES6 identifier). + * @returns {Promise} - Promise resolve with the serialized JS object. + * @private + * @todo [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> `'\'use strict\';' + os.EOL + os.EOL + ...`)? + */ +function serializeJsToString(object, indent, exportsTo) { + return createExportsString(exportsTo) + .then(exportsStr => exportsStr + serializeJs.serialize(object, { indent }) + ';' + os.EOL); +} + +/** + * Serialize a JS object to JSON string. + * + * @param {Object} object - Object to serialize. + * @param {number} indent - Indention. + * @returns {string} The serialized JSON. + * @private + */ +function serializeJsToJsonString(object, indent) { + return jsonStringifySafe(object, null, indent) + os.EOL; +} + +/** + * Turns the destination file name into a name containing a consecutive + * number if it exists. It iterates over the files until it finds a file + * name which does not exist. + * + * @param {string} dest - The destination file. + * @returns {string} - A consecutive file name or the original one if + * `dest` file does not exist. + * @private + */ +function getConsecutiveDestName(dest) { + let tmpDest = dest; + let i = 0; + const destDirName = path.dirname(tmpDest); + const ext = path.extname(tmpDest); + const basename = path.basename(tmpDest, ext); + while (fs.existsSync(tmpDest)) { + tmpDest = path.join(destDirName, basename + '(' + (i += 1) + ')' + ext); + } + return tmpDest; +} + +/** + * Writes a serialized object to file. + * + * @param {string} object - The object to write into file. + * @param {string} dest - The file destination path. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @param {Function} resolve - The Promise `resolve` callback. + * @param {Function} reject - The Promise `reject` callback. + * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. + * @see {@link Constants#TYPE_YAML} + * @see {@link Constants#TYPE_JSON} + * @see {@link Constants#TYPE_JS} + * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). + * @throws {Error} If serialized JSON file could not be written due to any reason. + * @private + */ +function writeToFile(object, dest, target, resolve, reject, forceOverwrite) { + /** + * Ensures that all dirs exists for `dest` and writes the file. + * + * @private + */ + function mkdirAndWrite() { + const destDir = path.dirname(dest); + logger.debug('Destination dir: ' + destDir); + mkdirp(destDir) + .then(() => { + logger.debug('Destination dir ' + destDir + ' successfully written'); + if (forceOverwrite === undefined || forceOverwrite === false) { // TODO shorten + dest = getConsecutiveDestName(dest); + logger.debug('Setting was: do not overwrite, using destination ' + dest + '.'); + } + return fsPromisified.writeFile(dest, object, UTF8); + }) + .then(() => resolve('Writing \'' + target + '\' file \'' + dest + '\' successful.')) + .catch((err) => { + err.message = 'Could not write \'' + target + '\' file \'' + dest + '\', cause: ' + err.message; + reject(err); + }); + } + + return fsPromisified.stat(dest) + .then((stats) => { + if (stats.isDirectory()) { // TODO remove when checked by Schema? Hmm, it could not exist at this point of time...! + reject(new Error('Destination file is a directory, pls specify a valid file resource!')); + } else { + // file exists + mkdirAndWrite(); + } + }) + .catch((err) => { + // ignore error (because file could possibly not exist at this point of time) + mkdirAndWrite(); + }); +} + +/** + * Writes a string serialized data object to a stream. + * + * @param {string} object - The data to write into stream. + * @param {string} dest - The stream destination. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @param {Function} resolve - The Promise `resolve` callback. + * @param {Function} reject - The Promise `reject` callback. + * @see {@link Constants#TYPE_YAML} + * @see {@link Constants#TYPE_JSON} + * @see {@link Constants#TYPE_JS} + * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). + * @throws {Error} If serialized JS object could not be written due to any reason. + * @private + */ +function writeToStream(object, dest, target, resolve, reject) { + dest + .on('error', err => reject(err)) + .on('finish', () => resolve('Writing ' + target + ' to stream successful.')); + + // write stringified data + dest.write(object); + dest.end(); +} + +/** + * @class This class provides utility methods usable to write JS objects + * from memory to a JSON/JS/YAML destination + * (file, object or {@link stream.Readable}). + * @example + * const { Writer } from 'jy-transform'); + * const logger = ...; + * const writer = new Writer(logger); + */ +// export class Writer { +// /** +// * Constructs the `Writer` with an (optional) logger. +// * +// * @param {(logger|cli|console)} [logger=console] - Logger instance. +// * @public +// */ +// constructor(logger) { +// this.logger = new LogWrapper(logger); +// } + +/** + * Writes a JS object to a YAML destination. + * + * @param {Object} object - The JS object to write into YAML destination. + * @param {Options} options - The write destination and indention. + * @see {@link Constants#MIN_INDENT} + * @see {@link Constants#DEFAULT_INDENT} + * @see {@link Constants#MAX_INDENT} + * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). + * @throws {Error} If YAML destination could not be written due to any reason. + * @public + * @example + * var Writer = require('jy-transform').Writer; + * var logger = ...; + * var writer = new Writer(logger); + * + * // ---- write obj to file + * + * var obj = {...}, + * var options = { + * dest: 'result.yml', + * indent: 2 + * } + * + * writer.writeYaml(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // ---- write obj to Writable + * + * options = { + * dest: fs.createWriteStream('result.yml'), + * indent: 4 + * } + * + * writer.writeYaml(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + */ +export async function writeYaml(object, options) { + const assertedOptions = await Joi.validate(options, transformerOptionsSchema); + return new Promise((resolve, reject) => { + let yaml; + try { + yaml = jsYaml.safeDump(object, { indent: assertedOptions.indent, noRefs: true }); + } catch (err) { + err.message = 'Could not write YAML file \'' + assertedOptions.dest + '\', cause: ' + err.message; + reject(err); + return; + } + + if (typeof assertedOptions.dest === 'string') { // file + writeToFile(yaml, assertedOptions.dest, TYPE_YAML, resolve, reject, assertedOptions.force); + } else if (isStream.writable(assertedOptions.dest)) { // stream + writeToStream(yaml, assertedOptions.dest, TYPE_YAML, resolve, reject); + } else { // object + options.dest = yaml; + resolve('Writing YAML to options.dest successful.'); + } + }); +} + +/** + * Writes a JS object to a JSON destination. + * + * @param {Object} object - The JS object to write into JSON destination. + * @param {Options} options - The write destination and indention. + * @see {@link Constants#MIN_INDENT} + * @see {@link Constants#DEFAULT_INDENT} + * @see {@link Constants#MAX_INDENT} + * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). + * @public + * @example + * var Writer = require('jy-transform').Writer; + * var logger = ...; + * var writer = new Writer(logger); + * + * // ---- write obj to file + * + * var obj = {...}; + * var options = { + * dest: 'result.json', + * indent: 2 + * } + * + * writer.writeJson(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // ---- write obj to Writable + * + * options = { + * dest: fs.createWriteStream('result.json'), + * indent: 4 + * } + * + * writer.writeJson(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * // ---- write obj to object + * + * options = { + * dest: {}, + * indent: 4 + * } + * + * writer.writeJson(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + */ +export async function writeJson(object, options) { + const assertedOptions = await Joi.validate(options, transformerOptionsSchema); + return new Promise((resolve, reject) => { + if (typeof assertedOptions.dest === 'string') { // file + writeToFile(serializeJsToJsonString(object, assertedOptions.indent), assertedOptions.dest, TYPE_JSON, + resolve, reject, assertedOptions.force); + } else if (isStream.writable(assertedOptions.dest)) { // stream + writeToStream(serializeJsToJsonString(object, assertedOptions.indent), assertedOptions.dest, + TYPE_JSON, resolve, reject); + } else { // object + options.dest = serializeJsToJsonString(object, assertedOptions.indent); + resolve('Writing JSON to options.dest successful.'); + } + }); +} + +/** + * Writes a JS object to a JS destination. The object is prefixed by `module.exports[.${options.exports}] = `. + * + * @param {Object} object - The JSON to write into JS destination. + * @param {Options} options - The write destination and indention. + * @see {@link Constants#MIN_INDENT} + * @see {@link Constants#DEFAULT_INDENT} + * @see {@link Constants#MAX_INDENT} + * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). + * @public + * @example + * var Writer = require('jy-transform').Writer; + * var logger = ...; + * var writer = new Writer(logger); + * + * // ---- write obj to file + * + * var obj = {...}; + * var options = { + * dest: 'result.js', + * indent: 2 + * } + * + * writer.writeJs(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // ---- write obj to Writable + * + * options = { + * dest: fs.createWriteStream('result.json'), + * indent: 4 + * } + * + * writer.writeJs(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + * + * + * // ---- write obj to object + * + * options = { + * dest: {}, + * indent: 2 + * } + * + * writer.writeJs(obj, options) + * .then(function (msg){ + * logger.info(msg); + * }) + * .catch(function (err) { + * logger.error(err.stack); + * }); + */ +export async function writeJs(object, options) { + const assertedOptions = await Joi.validate(options, transformerOptionsSchema); + return new Promise((resolve, reject) => { + if (typeof assertedOptions.dest === 'string') { // file + serializeJsToString(object, assertedOptions.indent, assertedOptions.exports) + .then((data) => { + writeToFile(data, assertedOptions.dest, TYPE_JS, resolve, reject, assertedOptions.force); + }) + .catch(err => reject(err)); + } else if (isStream.writable(assertedOptions.dest)) { // stream + serializeJsToString(object, assertedOptions.indent, assertedOptions.exports) + .then((data) => { + writeToStream(data, assertedOptions.dest, TYPE_JS, resolve, reject); + }) + .catch(err => reject(err)); + } else { // object + let msg; + if (assertedOptions.exports) { + options.dest[assertedOptions.exports] = object; + msg = 'Writing JS to options.dest.' + assertedOptions.exports + ' successful.'; + } else { + options.dest = object; + msg = 'Writing JS to options.dest successful.'; + } + resolve(msg); + } + }); +} +// } + +export default { + writeJs, + writeJson, + writeYaml, +}; diff --git a/test-data-by-js-to-file.json b/test-data-by-js-to-file.json new file mode 100644 index 0000000..d0ae716 --- /dev/null +++ b/test-data-by-js-to-file.json @@ -0,0 +1,3 @@ +{ + "test": "test" +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..0d7c584 --- /dev/null +++ b/test.js @@ -0,0 +1,36 @@ +const path = require('path'); + + +// import { Writer } from './src/writer'; +// import { Reader } from './src/reader'; +// +// (async () => { +// try { +// const writer = new Writer(); +// const file = './test-data-by-js-to-file.json'; +// console.log('test'); +// const msg = await writer.writeJson({ test: 'test' }, { dest: file }); +// console.log(msg); +// } catch (err) { +// console.error(err); +// } +// })(); +// +// (async () => { +// try { +// const reader = new Reader(); +// console.log('test2'); +// const msg = await reader.readJs({ +// origin: 'json', +// src: './test/data/test-data-corrupted.json' +// }); +// console.log(msg); +// } catch (err) { +// console.error(JSON.stringify(err.message, null, 4)); +// } +// })(); + +console.log(path.resolve('test/data/test-data.js')); +console.log(path.resolve('/test/data/test-data.js')); +console.log(path.resolve('./test/data/test-data.js')); +console.log(path.resolve('/Users/jens.krefeldt/Development/finanzcheck/jy-transform/test/data/test-data.js')); diff --git a/test/data/test-data b/test/data/test-data new file mode 100644 index 0000000..d3cac0e --- /dev/null +++ b/test/data/test-data @@ -0,0 +1,3 @@ +module.exports = { + myproperty: "old value" +}; diff --git a/test/data/test-data-json b/test/data/test-data-json new file mode 100644 index 0000000..46b2aae --- /dev/null +++ b/test/data/test-data-json @@ -0,0 +1,3 @@ +{ + "myproperty": "old value" +} diff --git a/test/data/test-data-yaml b/test/data/test-data-yaml new file mode 100644 index 0000000..f2bc229 --- /dev/null +++ b/test/data/test-data-yaml @@ -0,0 +1 @@ +myproperty: old value diff --git a/test/unit/test-index.js b/test/unit/test-index.js index 28853a7..48ab2df 100644 --- a/test/unit/test-index.js +++ b/test/unit/test-index.js @@ -1,11 +1,8 @@ import index from '../../index'; -import Transformer from '../../lib/transformer'; -import Reader from '../../lib/reader'; -import Writer from '../../lib/writer'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; /** - * @module test-unit:index + * @module jy-transform:test-unit:index * @description This unit test module tests the correct exporting from _./index.js_. */ @@ -13,47 +10,66 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - index - ', () => { describe('Exports Check Unit Tests', () => { describe('Exports', () => it('should be an existing Object', () => { + expect.assertions(3); expect(index).toBeDefined(); expect(index).toBeInstanceOf(Object); - expect(Object.keys(index)).toHaveLength(5); + expect(Object.keys(index)).toHaveLength(4); }) ); describe('Exported Transformer', () => - it('should be an existing Transformer class', () => { + it('should be an existing Transformer object', () => { + expect.assertions(5); expect(index.Transformer).toBeDefined(); - expect(index.Transformer).toBeInstanceOf(Function); - expect(new index.Transformer()).toBeInstanceOf(Transformer); + expect(index.Transformer).toBeInstanceOf(Object); + expect(Object.keys(index.Transformer)).toHaveLength(1); + const { transform } = index.Transformer; + expect(transform).toBeDefined(); + expect(transform).toBeInstanceOf(Function); }) ); describe('Exported Reader', () => - it('should be an existing Reader class', () => { + it('should be an existing Reader object', () => { + expect.assertions(7); expect(index.Reader).toBeDefined(); - expect(index.Reader).toBeInstanceOf(Function); - expect(new index.Reader()).toBeInstanceOf(Reader); + expect(index.Reader).toBeInstanceOf(Object); + expect(Object.keys(index.Reader)).toHaveLength(2); + const { readJs, readYaml } = index.Reader; + expect(readJs).toBeDefined(); + expect(readJs).toBeInstanceOf(Function); + expect(readYaml).toBeDefined(); + expect(readYaml).toBeInstanceOf(Function); }) ); describe('Exported Writer', () => - it('should be an existing Writer class', () => { + it('should be an existing Writer object', () => { + expect.assertions(9); expect(index.Writer).toBeDefined(); - expect(index.Writer).toBeInstanceOf(Function); - expect(new index.Writer()).toBeInstanceOf(Writer); + expect(index.Writer).toBeInstanceOf(Object); + expect(Object.keys(index.Writer)).toHaveLength(3); + const { writeJs, writeJson, writeYaml } = index.Writer; + expect(writeJs).toBeDefined(); + expect(writeJs).toBeInstanceOf(Function); + expect(writeJson).toBeDefined(); + expect(writeJson).toBeInstanceOf(Function); + expect(writeYaml).toBeDefined(); + expect(writeYaml).toBeInstanceOf(Function); }) ); describe('Exported Constants', () => it('should be an existing Constants instance', () => { + expect.assertions(4); expect(index.Constants).toBeDefined(); - expect(index.Constants).toBeInstanceOf(Object); - }) - ); + expect(typeof index.Constants === 'object').toBeTruthy(); + const { DEFAULT_FORCE_FILE_OVERWRITE } = index.Constants; + expect(DEFAULT_FORCE_FILE_OVERWRITE).toBeDefined(); + expect(DEFAULT_FORCE_FILE_OVERWRITE).toBeFalsy(); + // TODO + // {DEFAULT_FORCE_FILE_OVERWRITE: false, DEFAULT_INDENT: 2, DEFAULT_JS_EXPORTS_IDENTIFIER: undefined, DEFAULT_JS_IMPORTS_IDENTIFIER: undefined, DEFAULT_OPTIONS: {dest: storing relative to input file, exports: undefined, force: false, imports: undefined, indent: 2, origin: if not given, the type is tried to be inferred from the extension of source path, else it is 'yaml', target: if not given, the type is tried to be inferred from the extension of destination path, else it is 'js'}, DEFAULT_ORIGIN: yaml, DEFAULT_TARGET: js, DEST_DESCRIPTION: storing relative to input file, JS: js, JSON: json, JSON_TO_JS: json2js, JSON_TO_JSON: json2json, JSON_TO_YAML: json2yaml, JS_TO_JS: js2js, JS_TO_JSON: js2json, JS_TO_YAML: js2yaml, MAX_INDENT: 8, MIN_INDENT: 0, ORIGIN_DESCRIPTION: if not given, the type is tried to be inferred from the extension of source path, else it is 'yaml', TARGET_DESCRIPTION: if not given, the type is tried to be inferred from the extension of destination path, else it is 'js', TRANSFORMATIONS: [yaml2js, yaml2json, js2yaml, json2yaml, json2js, js2json, yaml2yaml, json2json, js2js], TYPES: [yaml, json, js], UTF8: utf8, YAML: yaml, YAML_TO_JS: yaml2js, YAML_TO_JSON: yaml2json, YAML_TO_YAML: yaml2yaml} - describe('Exported Middleware', () => - it('should be an existing Middleware instance', () => { - expect(index.Middleware).toBeDefined(); - expect(index.Middleware).toBeInstanceOf(Object); }) ); }); diff --git a/test/unit/test-log-wrapper.js b/test/unit/test-log-wrapper.js deleted file mode 100644 index 38ba97c..0000000 --- a/test/unit/test-log-wrapper.js +++ /dev/null @@ -1,236 +0,0 @@ -import LogWrapper from '../../lib/log-wrapper'; -import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; - -/** - * @classdesc This unit test suite checks the validity and correctness of {@link LogWrapper} class. - */ - -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - log-wrapper - ', () => { - /** - * `INFO` message buffer. - * @private - */ - let infoMsg; - - /** - * `DEBUG` message buffer. - * @private - */ - let debugMsg; - - /** - * `TRACE` message buffer. - * @private - */ - let traceMsg; - - /** - * `ERROR` message buffer. - * @private - */ - let errorMsg; - - /** - * The log wrapper testee. - * @private - */ - let logWrapper; - - /** - * `INFO` message constant. - * @constant - * @private - */ - const INFO = 'INFO'; - - /** - * `DEBUG` message constant. - * @constant - * @private - */ - const DEBUG = 'DEBUG'; - - /** - * `TRACE` message constant. - * @constant - * @private - */ - const TRACE = 'TRACE'; - - /** - * `ERROR` message constant. - * @constant - * @private - */ - const ERROR = 'ERROR'; - - /** - * A verbose result buffer. - * @type {Array} - * @private - */ - const verboseResultArray = []; - - /** - * A mock logger writing to message buffers. - * - * @type {{info: ((p1:*)=>*), debug: ((p1:*)=>*), trace: ((p1:*)=>*), error: ((p1:*)=>*)}} - * @private - */ - const mockLogger = { - info: msg => (infoMsg = msg), - debug: msg => (debugMsg = msg), - trace: msg => (traceMsg = msg), - error: msg => (errorMsg = msg) - }; - - /** - * A mock logger without `debug` function writing to message buffers. - * - * @type {{info: ((p1:*)=>*), trace: ((p1:*)=>*), error: ((p1:*)=>*)}} - * @private - */ - const mockLoggerWithoutDebugFunction = { - info: msg => (infoMsg = msg), - trace: msg => (traceMsg = msg), - error: msg => (errorMsg = msg), - }; - - /** - * A mock logger without `trace` function writing to message buffers. - * - * @type {{info: ((p1:*)=>*), debug: ((p1:*)=>*), error: ((p1:*)=>*)}} - * @private - */ - const mockLoggerWithoutTraceFunction = { - info: msg => (infoMsg = msg), - debug: msg => (debugMsg = msg), - error: msg => (errorMsg = msg), - }; - - /** - * A mock logger function writing to verbose result array. - * @type {{info: ((p1?:*)=>Number)}} - * @private - */ - const mockLoggerWithVerboseFunction = { - info: msg => verboseResultArray.push(msg) - }; - - describe('Testing LogWrapper with mockLogger', () => { - /** - * Resets the log wrapper with mock logger and all message buffers with `undefined`. - */ - beforeEach(() => { - infoMsg = undefined; - debugMsg = undefined; - traceMsg = undefined; - errorMsg = undefined; - logWrapper = new LogWrapper(mockLogger); - }); - - let expected = INFO; - it('should log with ' + expected, () => { - logWrapper.info(expected); - expect(infoMsg).toBe(expected); - }); - - expected = DEBUG; - it('should log with ' + expected, () => { - logWrapper.debug(expected); - expect(debugMsg).toBe(expected); - }); - - expected = TRACE; - it('should log with ' + expected, () => { - logWrapper.trace(expected); - expect(traceMsg).toBe(expected); - }); - - expected = ERROR; - it('should log with ' + expected, () => { - logWrapper.error(expected); - expect(errorMsg).toBe(expected); - }); - - const verboseExpected = { - origin: 'origin', - target: 'target', - src: 'src', - dest: 'dest', - indent: 'indent' - }; - - it('should log options', async () => { - logWrapper = new LogWrapper(mockLoggerWithVerboseFunction); - const options = await logWrapper.verboseOptions(verboseExpected); - expect(options).toBe(verboseExpected); - expect(verboseResultArray.indexOf('origin: ' + verboseExpected.origin)).toBeGreaterThan(-1); - expect(verboseResultArray.indexOf('target: ' + verboseExpected.target)).toBeGreaterThan(-1); - expect(verboseResultArray.indexOf('src: ' + verboseExpected.src)).toBeGreaterThan(-1); - expect(verboseResultArray.indexOf('dest: ' + verboseExpected.dest)).toBeGreaterThan(-1); - expect(verboseResultArray.indexOf('indent: ' + verboseExpected.indent)).toBeGreaterThan(-1); - }); - }); - - describe('Testing LogWrapper with mockLoggerWithoutDebugFunction', () => { - /** - * Resets the mock logger message targets. - */ - beforeEach(() => { - infoMsg = undefined; - debugMsg = undefined; - traceMsg = undefined; - errorMsg = undefined; - logWrapper = new LogWrapper(mockLoggerWithoutDebugFunction); - }); - - let expected = INFO; - it('should log with ' + expected, () => { - logWrapper.info(expected); - expect(infoMsg).toBe(expected); - }); - - expected = DEBUG; - it('should log with ' + expected, () => { - logWrapper.debug(expected); - expect(infoMsg).toBe(expected); - }); - - expected = ERROR; - it('should log with ' + expected, () => { - logWrapper.error(expected); - expect(errorMsg).toBe(expected); - }); - }); - - describe('Testing LogWrapper with mockLoggerWithoutTraceFunction', () => { - /** - * Resets the mock logger message targets. - */ - beforeEach(() => { - infoMsg = undefined; - debugMsg = undefined; - errorMsg = undefined; - logWrapper = new LogWrapper(mockLoggerWithoutTraceFunction); - }); - - let expected = INFO; - it('should log with ' + expected, () => { - logWrapper.info(expected); - expect(infoMsg).toBe(expected); - }); - - expected = TRACE; - it('should log with ' + expected, () => { - logWrapper.trace(expected); - expect(debugMsg).toBe(expected); - }); - - expected = ERROR; - it('should log with ' + expected, () => { - logWrapper.error(expected); - expect(errorMsg).toBe(expected); - }); - }); -}); diff --git a/test/unit/test-middleware.js b/test/unit/test-middleware.js index 78faf68..ff32ce7 100644 --- a/test/unit/test-middleware.js +++ b/test/unit/test-middleware.js @@ -1,16 +1,15 @@ import objectPath from 'object-path'; import { logger } from '../logger'; -import { Transformer, Middleware } from '../../index'; +import { transform } from '../../src/transformer'; +import { ensureMiddleware, identityMiddleware } from '../../src/middleware'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; /** - * @classdesc This unit test suite checks the validity and correctness of {@link Middleware} class. + * @module jy-transform:unit-test:test-middleware + * @description This unit test suite checks the validity and correctness of {@link middleware} module. */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - middleware - ', () => { - const identityMiddleware = Middleware.identityMiddleware; - let transformer; - /** * Middleware function for altering JSON. * @@ -57,13 +56,6 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - middleware - ', () => { return func(json).then(jsonResult => expect(jsonResult).toBe(json)); }; - /** - * Init the test logger. - */ - beforeAll(() => { - transformer = new Transformer(logger); - }); - describe('Testing Transformer middleware', () => { it('should alter json', () => { expect.assertions(4); @@ -71,7 +63,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - middleware - ', () => { src: {}, dest: {} }; - return transformer.transform(options, middleware) + return transform(options, middleware) .then((msg) => { logger.info(msg); logger.info('options.dest: ' + JSON.stringify(options.dest, null, 4)); @@ -97,26 +89,26 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - middleware - ', () => { describe('Testing middleware.ensureMiddleware()', () => { it('should provide passed function', () => { expect.assertions(2); - const func = Middleware.ensureMiddleware(identityMiddleware); + const func = ensureMiddleware(identityMiddleware); expect(func).toBeInstanceOf(Function); expect(func).toBe(identityMiddleware); }); it('should throw TypeError if middleware passed is not a function type', () => { expect.assertions(1); - expect(() => Middleware.ensureMiddleware('not a function')).toThrow(TypeError); + expect(() => ensureMiddleware('not a function')).toThrow(TypeError); }); it('should provide identity Promise if middleware passed is null', async () => { expect.assertions(2); - const func = Middleware.ensureMiddleware(); + const func = ensureMiddleware(); expect(func).toBeInstanceOf(Function); await assertIdentityPromise(func); }); it('should provide identity Promise if middleware passed is undefined', async () => { expect.assertions(2); - const func = Middleware.ensureMiddleware(undefined); + const func = ensureMiddleware(undefined); expect(func).toBeInstanceOf(Function); await assertIdentityPromise(func); }); diff --git a/test/unit/test-options-handler.js b/test/unit/test-options-handler.js deleted file mode 100644 index 689642e..0000000 --- a/test/unit/test-options-handler.js +++ /dev/null @@ -1,338 +0,0 @@ -import fs from 'fs'; -import fsExtra from 'fs-extra'; -import path from 'path'; -import stream from 'stream'; -import OptionsHandler from '../../lib/options-handler'; -import { logger } from '../logger'; -import Constants from '../../lib/constants'; -import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; - -/** - * @classdesc This unit test suite checks the validity and correctness of {@link OptionsHandler} class. - */ - -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-handler - ', () => { - /** - * Temporary base dir for writer test output. - * @type {string} - * @constant - * @private - */ - const OPTIONSHANDLER_TEST_BASE_DIR = './test/tmp/options-handler'; - - /** - * The testee. - * @type {OptionsHandler} - * @private - */ - let optionsHandler; - - /** - * Init the test logger and Writer. - */ - beforeAll(() => { - fsExtra.ensureDirSync(OPTIONSHANDLER_TEST_BASE_DIR); - fsExtra.emptyDirSync(OPTIONSHANDLER_TEST_BASE_DIR); - optionsHandler = new OptionsHandler(logger); - }); - - /** - * Assert an `Error` for a given options function. - * - * @param {Object} options - The options which potentially produce the error. - * @param {Function} optionsFunc - The function to call for assertion. - * @param {Error} [errorType=Error] - The error type to expect. - * @private - */ - function expectOptionsError(options, optionsFunc, errorType = Error) { - expect.assertions(1); - return expect(optionsFunc(options)).rejects.toBeInstanceOf(errorType); - } - - describe('Testing OptionsHandler.validateTransformation(...)', () => { - it('should reject when options is missing', async () => - await expectOptionsError(null, optionsHandler.validateTransformation) - ); - - it('should reject when options.origin is missing', async () => - await expectOptionsError({ target: Constants.YAML }, optionsHandler.validateTransformation) - ); - - it('should reject when options.target is missing', async () => - await expectOptionsError(null, optionsHandler.validateTransformation) - ); - - it('should resolve transformation correctly from valid origin and target', async () => { - const options = { - origin: Constants.YAML, - target: Constants.JS, - }; - const results = await optionsHandler.validateTransformation(options); - expect(results).toHaveLength(2); - expect(results[0]).toBe(options); - expect(results[1]).toBe(Constants.YAML + '2' + Constants.JS); - }); - - it('should reject with Error due to invalid target', async () => { - const invalidOptions = { - origin: Constants.YAML, - target: 'INVALID_TARGET', - }; - await expectOptionsError(invalidOptions, optionsHandler.validateTransformation); - }); - }); - - describe('Testing OptionsHandler.completeOptions(...)', () => { - it('should reject when options is missing', async () => - await expectOptionsError(null, optionsHandler.completeOptions) - ); - - it('should resolve options.src/origin and options.dest/target with default values (' + Constants.DEFAULT_ORIGIN - + '/' + Constants.DEFAULT_TARGET + ')', async () => { - const PATH_WITH_INVALID_EXT = 'PATH_WITH_INVALID.EXT'; - const options = { - src: PATH_WITH_INVALID_EXT, - dest: PATH_WITH_INVALID_EXT, - }; - const resultOptions = await optionsHandler.completeOptions(options); - expect(resultOptions.origin).toBe(Constants.DEFAULT_ORIGIN); - expect(resultOptions.target).toBe(Constants.DEFAULT_TARGET); - expect(resultOptions.dest).toBe(PATH_WITH_INVALID_EXT); - expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); - expect(resultOptions.force).toBe(Constants.DEFAULT_FORCE_FILE_OVERWRITE); - }); - - it('should resolve options.force should result to ' + !Constants.DEFAULT_FORCE_FILE_OVERWRITE, async () => { - const PATH_WITH_INVALID_EXT = 'PATH_WITH_INVALID.EXT'; - const force = !Constants.DEFAULT_FORCE_FILE_OVERWRITE; - const options = { - src: PATH_WITH_INVALID_EXT, - dest: PATH_WITH_INVALID_EXT, - force, - }; - const resultOptions = await optionsHandler.completeOptions(options); - expect(resultOptions.origin).toBe(Constants.DEFAULT_ORIGIN); - expect(resultOptions.target).toBe(Constants.DEFAULT_TARGET); - expect(resultOptions.dest).toBe(PATH_WITH_INVALID_EXT); - expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); - expect(resultOptions.force).toBe(force); - }); - }); - - describe('Testing OptionsHandler.ensureIndent(...)', () => { - it('should reject when options is missing', async () => - await expectOptionsError(null, optionsHandler.ensureIndent) - ); - - it('should set default indent if indent is missing', async () => { - const resultOptions = await optionsHandler.ensureIndent({}); - expect(resultOptions.indent).toBeDefined(); - expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); - }); - - it('should set default indent if indent < minimum indent', async () => { - const options = { - indent: (Constants.MIN_INDENT - 1), - target: Constants.YAML, - }; - const resultOptions = await optionsHandler.ensureIndent(options); - expect(resultOptions.target).toBe(Constants.YAML); - expect(resultOptions.indent).toBeDefined(); - expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); - }); - - it('should set default indent if indent < JS/JSON minimum indent', async () => { - const resultOptions = await optionsHandler.ensureIndent({ indent: Constants.MIN_INDENT - 1 }); - expect(resultOptions.indent).toBeDefined(); - expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); - }); - - it('should set default indent if indent > than maximum indent', async () => { - const resultOptions = await optionsHandler.ensureIndent({ indent: Constants.MAX_INDENT + 1 }); - expect(resultOptions.indent).toBeDefined(); - expect(resultOptions.indent).toBe(Constants.DEFAULT_INDENT); - }); - }); - - describe('Testing OptionsHandler.assertOrigin(...)', () => { - it('should reject when options is missing', async () => - await expectOptionsError(null, optionsHandler.assertOrigin) - ); - - it('should resolve options.origin for valid type YAML', async () => { - const options = { origin: Constants.YAML }; - const resultOptions = await optionsHandler.assertOrigin(options); - expect(resultOptions.origin).toBeDefined(); - expect(resultOptions.origin).toBe(Constants.YAML, 'result origin should have type ' + Constants.YAML); - }); - - it('should resolve options.origin for valid type JS', async () => { - const resultOptions = await optionsHandler.assertOrigin({ origin: Constants.JS }); - expect(resultOptions.origin).toBeDefined(); - expect(resultOptions.origin).toBe(Constants.JS); - }); - - it('should resolve options.origin for valid type JSON', async () => { - const resultOptions = await optionsHandler.assertOrigin({ origin: Constants.JSON }); - expect(resultOptions.origin).toBeDefined(); - expect(resultOptions.origin).toBe(Constants.JSON); - }); - - it('should reject when options.origin is invalid type', async () => - await expectOptionsError({ origin: 'INVALID_TYPE' }, optionsHandler.assertOrigin) - ); - }); - - describe('Testing OptionsHandler.assertTarget(...)', () => { - it('should reject when options is missing', async () => - await expectOptionsError(null, optionsHandler.assertTarget) - ); - - it('should resolve options.target for valid type YAML', async () => { - const resultOptions = await optionsHandler.assertTarget({ target: Constants.YAML }); - expect(resultOptions.target).toBeDefined(); - expect(resultOptions.target).toBe(Constants.YAML); - }); - - it('should resolve options.target for valid type JS', async () => { - const resultOptions = await optionsHandler.assertTarget({ target: Constants.JS }); - expect(resultOptions.target).toBeDefined(); - expect(resultOptions.target).toBe(Constants.JS); - }); - - it('should resolve options.target for valid type JSON', async () => { - const resultOptions = await optionsHandler.assertTarget({ target: Constants.JSON }); - expect(resultOptions.target).toBeDefined(); - expect(resultOptions.target).toBe(Constants.JSON); - }); - - it('should reject when options.target is invalid type', async () => - await expectOptionsError({ origin: 'INVALID_TYPE' }, optionsHandler.assertTarget) - ); - }); - - describe('Testing OptionsHandler.ensureDest(...)', () => { - it('should reject when options is missing', async () => - await expectOptionsError(null, optionsHandler.ensureDest) - ); - - it('should reject when options.src is stream but options.target is missing', async () => - await expectOptionsError({ dest: new stream.Writable() }, optionsHandler.ensureDest) - ); - - it('should resolve options.dest with value \'' + Constants.DEFAULT_OPTIONS.dest + '\' to relative file path to ' - + Constants.YAML + ' file', async () => { - const fileBaseName = 'test'; - const options = { - src: fileBaseName + '.' + Constants.JS, - dest: Constants.DEFAULT_OPTIONS.dest, - target: Constants.YAML - }; - const resultOptions = await optionsHandler.ensureDest(options); - expect(resultOptions.dest).toBeDefined(); - expect(resultOptions.dest).toBe(fileBaseName + '.' + Constants.YAML); - }); - - it('should resolve options.dest with value \'' + Constants.DEFAULT_OPTIONS.dest + '\' to relative file path to ' - + Constants.JS + ' file', async () => { - const fileBaseName = 'test'; - const options = { - src: fileBaseName + '.' + Constants.YAML, - dest: Constants.DEFAULT_OPTIONS.dest, - target: Constants.JS - }; - const resultOptions = await optionsHandler.ensureDest(options); - expect(resultOptions.dest).toBeDefined(); - expect(resultOptions.dest).toBe(fileBaseName + '.' + Constants.JS); - }); - - it('should resolve options.dest with value \'' + Constants.DEFAULT_OPTIONS.dest + '\' to relative file path to ' - + Constants.JSON + ' file', async () => { - const fileBaseName = 'test'; - const options = { - src: fileBaseName + '.' + Constants.YAML, - dest: Constants.DEFAULT_OPTIONS.dest, - target: Constants.JSON - }; - const resultOptions = await optionsHandler.ensureDest(options); - expect(resultOptions.dest).toBeDefined(); - expect(resultOptions.dest).toBe(fileBaseName + '.' + Constants.JSON); - }); - - it('should reject options.dest when invalid target type is provided', async () => { - const options = { - src: 'test.' + Constants.YAML, - dest: Constants.DEFAULT_OPTIONS.dest, - target: 'INVALID_TARGET' - }; - await expectOptionsError(options, optionsHandler.ensureDest); - }); - - it('should reject when Writable is given but not target', async () => - await expectOptionsError({ dest: fs.createWriteStream(OPTIONSHANDLER_TEST_BASE_DIR + '/myOutput.txt') }, - optionsHandler.ensureDest) - ); - - it('should resolve original options.dest', async () => { - const destObj = {}; - const resultOptions = await optionsHandler.ensureDest({ dest: destObj }); - expect(resultOptions.dest).toBeDefined(); - expect(resultOptions.dest).toBe(destObj); - }); - - it('should resolve with undefined for options.dest', async () => { - const resultOptions = await optionsHandler.ensureDest({}); - expect(resultOptions.dest).toBeUndefined(); - }); - }); - - describe('Testing OptionsHandler.ensureSrc(...)', () => { - it('should reject when options is missing', async () => - await expectOptionsError(null, optionsHandler.ensureSrc, TypeError) // TODO check if this really TypeError!?! - ); - - it('should reject when options.src is not given', async () => - await expectOptionsError({}, optionsHandler.ensureSrc) - ); - - it('should resolve original options.src', async () => { - const existingFile = path.resolve('./test/data/test-file.yaml'); - const resultOptions = await optionsHandler.ensureSrc({ src: existingFile }); - expect(resultOptions.src).toBeDefined(); - expect(resultOptions.src).toBe(existingFile); - }); - - it('should reject when options.src has value of not existing file', async () => - // HINT: this gives no Error type but plain object: - // {"errno":-2,"code":"ENOENT","syscall":"stat","path":"NON_EXISTING_FILE"} - await expectOptionsError({ src: 'NON_EXISTING_FILE' }, optionsHandler.ensureSrc) - ); - - it('should reject when options.src is a directory', async () => - await expectOptionsError({ src: './test/data' }, optionsHandler.ensureSrc) - ); - - it('should reject when Readable is given but not origin', async () => - await expectOptionsError({ src: fs.createReadStream('./test/data/readable-test-dummy.txt') }, - optionsHandler.ensureSrc) - ); - - it('should resolve original options.src Readable', async () => { - const readable = fs.createReadStream('./test/data/readable-test-dummy.txt'); - const options = { - src: readable, - origin: Constants.JSON - }; - const resultOptions = await optionsHandler.ensureSrc(options); - expect(resultOptions.src).toBeDefined(); - expect(resultOptions.src).toBe(readable); - }); - - it('should resolve original options.src object', async () => { - const srcObj = {}; - const resultOptions = await optionsHandler.ensureSrc({ src: srcObj }); - expect(resultOptions.src).toBeDefined(); - expect(resultOptions.src).toBe(srcObj); - }); - }); -}); diff --git a/test/unit/test-reader.js b/test/unit/test-reader.js index d517e2a..61c580f 100644 --- a/test/unit/test-reader.js +++ b/test/unit/test-reader.js @@ -1,30 +1,44 @@ import YAMLException from 'js-yaml/lib/js-yaml/exception'; import fs from 'fs'; -import { Reader, Constants } from '../../index'; +import { readJs, readYaml } from '../../src/reader'; import { logger } from '../logger'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; +import { + TYPE_JS, + TYPE_YAML, + TYPE_JSON, +} from '../../src/constants'; /** - * @classdesc This unit test suite checks the validity and correctness of {@link Reader} class. + * @module jy-transform:unit-test:test-reader + * @description This unit test suite checks the validity and correctness of {@link Reader} class. */ + describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { /** - * The testee. - * @type {Reader} + * Assert a `Error` properties for a given reader function. + * + * @param {Object} options - The options which potentially produce the error. + * @param {Function} readerFunc - The function to call for assertion. + * @param {Object} [match={name:'Error'}] - The propertie(s) error should contain. + * @private */ - let reader; + const expectReaderError = (options, readerFunc, match = { name: 'Error' }) => { + expect.assertions(1); + return expect(readerFunc(options)).rejects.toMatchObject(match); + }; /** * Assert an `Error` for a given reader function. * - * @param {Object} options - The options which potentially produce the error. - * @param {Function} readerFunc - The function to call for assertion. - * @param {Error} [expectedErrorType=Error] - The error type to expect. + * @param {Object} options - The options which potentially produce the error. + * @param {Function} readerFunc - The function to call for assertion. + * @param {Error} [match=Error'] - The error type to match. * @private */ - const expectReaderError = (options, readerFunc, expectedErrorType = Error) => { + const expectReaderErrorByType = (options, readerFunc, match = Error) => { expect.assertions(1); - return expect(readerFunc(options)).rejects.toBeInstanceOf(expectedErrorType); + return expect(readerFunc(options)).rejects.toBeInstanceOf(match); }; /** @@ -43,27 +57,22 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { expect(json[key]).toBe(expectedValue); }; - beforeAll(() => { - reader = new Reader(logger); - }); - describe('Testing Reader.readJs(...)', () => { const exports = 'fooBar'; const exportsNotExists = 'notFooBar'; const invalidIdentifier = '#3/-'; - it('should read JS from file', async () => - await expectReaderSuccess({ src: './test/data/test-data.js' }, reader.readJs, 'myproperty', 'old value') - ); - - it('should read JS from file with options.imports == \'\'', async () => { + it('should reject on reading JS with options.imports == \'\'', () => { const options = { src: './test/data/test-data.js', imports: '', }; - await expectReaderSuccess(options, reader.readJs, 'myproperty', 'old value'); + return expectReaderError(options, readJs, { name: 'ValidationError', isJoi: true }); }); + it('should read JS from file', async () => + await expectReaderSuccess({ src: './test/data/test-data.js' }, readJs, 'myproperty', 'old value') + ); it('should read JS from file with options.imports == \'' + exports + '\'', async () => { expect.assertions(5); @@ -71,7 +80,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { src: './test/data/test-imports.js', imports: exports, }; - const json = await reader.readJs(options); + const json = await readJs(options); expect(json).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(json, exports)).toBeFalsy(); expect(Object.prototype.hasOwnProperty.call(json, 'bar')).toBeFalsy(); @@ -85,9 +94,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { const options = { src: './test/data/test-imports.txt', imports: exports, - origin: Constants.JS, + origin: TYPE_JS, }; - const json = await reader.readJs(options); + const json = await readJs(options); expect(json).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(json, exports)).toBeFalsy(); expect(Object.prototype.hasOwnProperty.call(json, 'bar')).toBeFalsy(); @@ -101,7 +110,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { src: './test/data/test-imports.js', imports: invalidIdentifier, }; - return expectReaderError(options, reader.readJs); + return expectReaderError(options, readJs, { name: 'ValidationError', isJoi: true }); }); it('should reject read JS from file with Error on non-existent identifier for options.imports: ' @@ -110,33 +119,23 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { src: './test/data/test-imports.js', imports: exportsNotExists, }; - return expectReaderError(options, reader.readJs); + return expectReaderErrorByType(options, readJs, Error); }); it('should read JSON from file', async () => - await expectReaderSuccess({ src: './test/data/test-data.json' }, reader.readJs, 'myproperty', 'old value') + await expectReaderSuccess({ src: './test/data/test-data.json' }, readJs, 'myproperty', 'old value') ); it('should read JS from object', async () => { - const options = { - src: { - test: 'value', - }, - }; - await expectReaderSuccess(options, reader.readJs, 'test', 'value'); - }); - - it('should read JS from object with options.imports == \'\'', async () => { const options = { src: { foo: 'bar', }, - imports: '', }; - await expectReaderSuccess(options, reader.readJs, 'foo', 'bar'); + await expectReaderSuccess(options, readJs, 'foo', 'bar'); }); - it('should read JS from object with options.imports == \'' + exports + '\'', async () => { + it('should read JS from Object with options.imports == \'' + exports + '\'', async () => { expect.assertions(6); const options = { src: { @@ -147,7 +146,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { }, imports: exports, }; - const json = await reader.readJs(options); + const json = await readJs(options); expect(json).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(json, exports)).toBeFalsy(); expect(Object.prototype.hasOwnProperty.call(json, 'bar')).toBeTruthy(); @@ -156,7 +155,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { expect(json.foo).toBe('bar'); }); - it('should reject read JS from object with Error on invalid identifier for options.imports: ' + it('should reject read JS from Object with Error on invalid identifier for options.imports: ' + invalidIdentifier, () => { const options = { src: { @@ -167,10 +166,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { }, imports: invalidIdentifier, }; - return expectReaderError(options, reader.readJs); + return expectReaderError(options, readJs, { name: 'ValidationError', isJoi: true }); }); - it('should reject read JS from file with Error on non-existent identifier for options.imports: ' + it('should reject read (deep) JS from file with Error on non-existent identifier for options.imports: ' + exportsNotExists, () => { const options = { src: { @@ -181,71 +180,88 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { }, imports: exportsNotExists, }; - return expectReaderError(options, reader.readJs); + return expectReaderErrorByType(options, readJs, Error); }); it('should read JSON from stream', async () => - await expectReaderSuccess({ src: fs.createReadStream('./test/data/test-data.json') }, - reader.readJs, 'myproperty', 'old value') + await expectReaderSuccess({ + origin: TYPE_JSON, + src: fs.createReadStream('./test/data/test-data.json') + }, readJs, 'myproperty', 'old value') ); it('should read corrupted JSON from file path and fail by SyntaxError', () => { - return expectReaderError({ src: './test/data/test-data-corrupted.json' }, reader.readJs, SyntaxError); + return expectReaderErrorByType({ + origin: TYPE_JSON, + src: './test/data/test-data-corrupted.json' + }, readJs, SyntaxError); }); it('should read invalid JSON from file path and fail by SyntaxError', () => { - const options = { src: './test/data/test-data-wrong-syntax.json' }; - return expectReaderError(options, reader.readJs, SyntaxError); + return expectReaderErrorByType({ + origin: TYPE_JSON, + src: './test/data/test-data-wrong-syntax.json' + }, readJs, SyntaxError); }); it('should read corrupted JSON from stream and fail by SyntaxError', () => { - const options = { src: fs.createReadStream('./test/data/test-data-corrupted.json') }; - return expectReaderError(options, reader.readJs, SyntaxError); + const options = { + origin: TYPE_JSON, + src: fs.createReadStream('./test/data/test-data-corrupted.json'), + }; + return expectReaderErrorByType(options, readJs, SyntaxError); }); it('should read invalid JSON from stream and fail by SyntaxError', () => { - return expectReaderError({ src: fs.createReadStream('./test/data/test-data-wrong-syntax.json') }, - reader.readJs, SyntaxError); + return expectReaderErrorByType({ + origin: TYPE_JSON, + src: fs.createReadStream('./test/data/test-data-wrong-syntax.json') + }, readJs, SyntaxError); }); it('should fail JS(ON) read by missing options', () => { - return expectReaderError(null, reader.readJs); + return expectReaderError(null, readJs, { name: 'ValidationError', isJoi: true }); }); it('should fail JS(ON) read by missing options.src', () => { - return expectReaderError({}, reader.readJs); + return expectReaderError({}, readJs, { name: 'ValidationError', isJoi: true }); }); }); describe('Testing Reader.readYaml(...)', () => { it('should read YAML from file', async () => - await expectReaderSuccess({ src: './test/data/test-data.yaml' }, reader.readYaml, 'myproperty', 'old value') + await expectReaderSuccess({ src: './test/data/test-data.yaml' }, readYaml, 'myproperty', 'old value') ); it('should read JS from object', async () => - await expectReaderSuccess({ src: { test: 'value' } }, reader.readYaml, 'test', 'value') + await expectReaderSuccess({ src: { test: 'value' } }, readYaml, 'test', 'value') ); it('should read YAML from stream', async () => - await expectReaderSuccess({ src: fs.createReadStream('./test/data/test-data.yaml') }, - reader.readYaml, 'myproperty', 'old value') + await expectReaderSuccess({ + origin: TYPE_YAML, + src: fs.createReadStream('./test/data/test-data.yaml'), + }, readYaml, 'myproperty', 'old value') ); it('should read invalid YAML from file path and fail by YAMLException', () => { - return expectReaderError({ src: './test/data/test-data-wrong-syntax.yaml' }, reader.readYaml, YAMLException); + return expectReaderErrorByType({ src: './test/data/test-data-wrong-syntax.yaml' }, readYaml, + YAMLException); }); it('should read invalid YAML from stream and fail by YAMLException', () => { - return expectReaderError({ src: fs.createReadStream('./test/data/test-data-wrong-syntax.yaml') }, - reader.readYaml); + return expectReaderErrorByType({ + origin: TYPE_YAML, + src: fs.createReadStream('./test/data/test-data-wrong-syntax.yaml'), + }, readYaml, YAMLException); }); it('should fail YAML read by missing input options', () => { - return expectReaderError(null, reader.readYaml); + return expectReaderError(null, readYaml, { name: 'ValidationError', isJoi: true }); }); it('should fail YAML read by missing options.src', () => { - return expectReaderError({}, reader.readYaml); + return expectReaderError({}, readYaml, { name: 'ValidationError', isJoi: true }); }); }); }); diff --git a/test/unit/test-transformer.js b/test/unit/test-transformer.js index 60ccbcf..70d7ba3 100644 --- a/test/unit/test-transformer.js +++ b/test/unit/test-transformer.js @@ -4,13 +4,20 @@ import fsExtra from 'fs-extra'; import fs from 'fs'; import path from 'path'; import { logger } from '../logger'; -import { Transformer, Constants } from '../../index'; +import { transform } from '../../src/transformer'; +import { + UTF8, + TYPE_YAML, + TYPE_JS, + TYPE_JSON, +} from '../../src/constants'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; const fsPromised = promisify(fs); /** - * @classdesc This unit test suite checks the correct transformation behaviour of {@link Transformer} class. + * @module jy-transform:unit-test:test-transformer + * @description This unit test suite checks the correct transformation behaviour of {@link Transformer} class. */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { @@ -26,16 +33,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { */ const TRANSFORMER_TEST_BASE_DIR = './test/tmp/transformer'; - /** - * The testee. - * @type {Transformer} - */ - let transformer; - beforeAll(() => { fsExtra.ensureDirSync(TRANSFORMER_TEST_BASE_DIR); fsExtra.emptyDirSync(TRANSFORMER_TEST_BASE_DIR); - transformer = new Transformer(logger); }); /** @@ -68,7 +68,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { * @param {Function} middleware - The transformation middleware. */ function assertTransformSuccess(options, middleware) { - return transformer.transform(options, middleware) + return transform(options, middleware) .then((msg) => { logger.info(msg); const stats = fsExtra.statSync(options.dest); @@ -87,12 +87,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { * @param {Function} done - Test finished callback. */ function assertYamlTransformSuccess(options, middleware, done) { - return transformer.transform(options, middleware) + return transform(options, middleware) .then((msg) => { logger.info(msg); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); - return fsExtra.readFileAsync(options.dest, Constants.UTF8) + return fsPromised.readFile(options.dest, UTF8) .then((yaml) => { try { const json = jsYaml.safeLoad(yaml); @@ -115,10 +115,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { it('should store ' + DEST + ' file relative to ' + TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml', async () => { expect.assertions(2); - const msg = await transformer.transform({ src: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml') }, - middlewareFunc); + const msg = await transform({ + src: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml'), + dest: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.js'), + }, middlewareFunc); logger.info(msg); - const stats = fsExtra.statSync(DEST); + const stats = fs.statSync(DEST); expect(stats.isFile()).toBeTruthy(); // eslint-disable-next-line import/no-unresolved, global-require, import/no-dynamic-require const json = require('../tmp/transformer/test-data.js'); @@ -194,13 +196,13 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { dest: path.resolve(DEST), }; - transformer.transform(options, middlewareFunc) + transform(options, middlewareFunc) .then((msg) => { logger.info(msg); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); - fsPromised.readFile(options.dest, Constants.UTF8) + fsPromised.readFile(options.dest, UTF8) .then((yaml) => { logger.debug('YAML loaded from file ' + options.dest); try { @@ -231,13 +233,13 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { dest: path.resolve(DEST), }; - transformer.transform(options, middlewareFunc) + transform(options, middlewareFunc) .then((msg) => { logger.info(msg); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); - fsPromised.readFile(options.dest, Constants.UTF8) + fsPromised.readFile(options.dest, UTF8) .then((yaml) => { logger.debug('YAML loaded from file ' + options.dest); try { diff --git a/test/unit/test-validator.js b/test/unit/test-validator.js deleted file mode 100644 index 2879303..0000000 --- a/test/unit/test-validator.js +++ /dev/null @@ -1,34 +0,0 @@ -import Validator from '../../lib/validator'; -import { logger } from '../logger'; -import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; - -/** - * @classdesc This unit test suite checks validity and correctness. - */ - -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - validator - ', () => { - /** - * The testee. - * @type {Validator} - */ - let validator; - - beforeAll(() => { - validator = new Validator(logger); - }); - - const nonStringIdentifier = {}; - it('should validate non-string identifier \'' + JSON.toString(nonStringIdentifier) + '\' to false', () => - expect(validator.validateIdentifier(nonStringIdentifier)).toBeFalsy() - ); - - const invalidIdentifier = '#3/-'; - it('should validate invalid identifier \'' + invalidIdentifier + '\' to false', () => - expect(validator.validateIdentifier(invalidIdentifier)).toBeFalsy() - ); - - const validIdentifier = 'bar'; - it('should validate \'' + validIdentifier + '\' identifier to true', () => - expect(validator.validateIdentifier(validIdentifier)).toBeTruthy() - ); -}); diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js index 286991f..f97789a 100644 --- a/test/unit/test-writer.js +++ b/test/unit/test-writer.js @@ -4,18 +4,25 @@ import YAMLException from 'js-yaml/lib/js-yaml/exception'; import fs from 'fs'; import os from 'os'; import stream from 'stream'; -import { Writer } from '../../index'; +import { Writer } from '../../src/writer'; import { logger } from '../logger'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; +import { + TYPE_YAML, + TYPE_JS, + TYPE_JSON, +} from '../../src/constants'; const fsPromised = promisify(fs); /** - * @classdesc This unit test suite checks the validity and correctness of {@link Writer} class. + * @module jy-transform:unit-test:test-writer + * @description This unit test suite checks the validity and correctness of {@link Writer} class. */ + describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { /** - * Samlpe JSON content used in tests. + * Sample JSON content used in tests. * * @type {{test: string}} * @constant @@ -76,7 +83,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { // check for existing source file let statErr; try { - await fsPromised.stat(dest); // TODO could we check this in Async mode? + await fsPromised.stat(dest); statErr = new Error('Error expected when checking file = ' + dest); } catch (err) { logger.info('Error is EXPECTED: ' + err.stack); @@ -91,18 +98,32 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { /** * Assert an `Error` for a given writer function. * - * @param {Object} json - The json to write. - * @param {Object} options - The options which potentially produce the error. - * @param {Function} writerFunc - The function to call for assertion. - * @param {Error} [expectedErrorType=Error] - The error type to expect. + * @param {Object} json - The json to write. + * @param {Object} options - The options which potentially produce the error. + * @param {Function} writerFunc - The function to call for assertion. + * @param {Object} [match={name:'Error'}] - The propertie(s) error should contain. * @private */ - const expectWriterError = (json, options, writerFunc, expectedErrorType = Error) => { + const expectWriterError = (json, options, writerFunc, match = { name: 'Error' }) => { expect.assertions(1); - return expect(writerFunc(json, options)).rejects.toBeInstanceOf(expectedErrorType); + return expect(writerFunc(json, options)).rejects.toMatchObject(match); }; - describe('Testing Writer.writeJs(...)', () => { + /** + * Assert an `Error` for a given writer function. + * + * @param {Object} json - The json to write. + * @param {Object} options - The options which potentially produce the error. + * @param {Function} writerFunc - The function to call for assertion. + * @param {Error} [match=Error'] - The error type to match. + * @private + */ + const expectWriterErrorByType = (json, options, writerFunc, match = Error) => { + expect.assertions(1); + return expect(writerFunc(json, options)).rejects.toBeInstanceOf(match); + }; + + describe('testing Writer.writeJs(...)', () => { it('should write JS to file', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.js'; @@ -123,6 +144,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { expect.assertions(4); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-exports-identifier.js'; const options = { + target: TYPE_JS, dest: fs.createWriteStream(file), exports: 'test', }; @@ -140,7 +162,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js', exports: '#3/-', }; - return expectWriterError(JSON_CONTENT, options, writer.writeJs); + return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'#3/-\')', () => { @@ -149,7 +171,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: '#3/-' }; - return expectWriterError(JSON_CONTENT, options, writer.writeJs); + return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'if\')', () => { @@ -158,18 +180,21 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: 'if' }; - return expectWriterError(JSON_CONTENT, options, writer.writeJs); + return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by provoked error', () => { const errorThrowingStream = new stream.Writable(); - // eslint-disable-next-line no-underscore-dangle - errorThrowingStream._write = (chunk, encoding, done) => { + // eslint-disable-next-line no-underscore-dangle, func-names + errorThrowingStream._write = function (chunk, encoding, done) { logger.info('stream emitting Error now'); this.emit('error', new Error('Dummy Error')); done(); }; - return expectWriterError(JSON_CONTENT, { dest: errorThrowingStream }, writer.writeJs); + return expectWriterErrorByType(JSON_CONTENT, { + target: TYPE_JS, + dest: errorThrowingStream + }, writer.writeJs, Error); }); it('should write JS to JS object', async () => { @@ -182,17 +207,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { expect(options.dest.test).toBe('value'); }); - it('should write JS to JS object with options.exports == \'\'', async () => { - expect.assertions(4); + it('should reject to write JS to JS object with options.exports == \'\'', async () => { const options = { dest: {}, exports: '' }; - const msg = await writer.writeJs(JSON_CONTENT, options); - expect(msg).toBeDefined(); - expect(options.dest).toBeDefined(); - expect(Object.prototype.hasOwnProperty.call(options.dest, 'test')).toBeTruthy(); - expect(options.dest.test).toBe('value'); + return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); }); const exports = 'foo'; @@ -216,25 +236,24 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: {}, exports: invalidIdentifier }; - return expectWriterError(JSON_CONTENT, options, writer.writeJs); + return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS with Error on missing destination', () => { - return expectWriterError(JSON_CONTENT, {}, writer.writeJs); + return expectWriterError(JSON_CONTENT, {}, writer.writeJs, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS to file by invalid file path', (done) => { const options = { - dest: WRITER_TEST_BASE_DIR + '/<>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + + dest: WRITER_TEST_BASE_DIR + + '/<>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/test-data-by-js-to-file.js' }; - writer.writeJs(JSON_CONTENT, options) .then((msg) => { - expect(msg).toBeDefined(); - done(new Error('Error expected')); + done(new Error('Error expected, but got success message: ' + msg)); }) .catch((err) => { logger.info('EXPECTED ERROR: ' + (err.stack ? err.stack : err)); @@ -246,7 +265,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { // "syscall": "mkdir", // "path": "..." // } - expect(typeof err === 'object').toBeTruthy(); // TODO instanceOf ? + expect(err).not.toBeInstanceOf(Error); + expect(typeof err === 'object').toBeTruthy(); expect(err.code).toBe('ENAMETOOLONG'); done(); }); @@ -268,7 +288,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JSON to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-json-stream.json'; - const msg = await writer.writeJson(JSON_CONTENT, { dest: fs.createWriteStream(file) }); + const msg = await writer.writeJson(JSON_CONTENT, { + target: TYPE_JSON, + dest: fs.createWriteStream(file), + }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -285,7 +308,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }); it('should reject with Error on missing destination', () => { - return expectWriterError(JSON_CONTENT, {}, writer.writeJson); + return expectWriterError(JSON_CONTENT, {}, writer.writeJson, { name: 'ValidationError', isJoi: true }); }); }); @@ -301,14 +324,20 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write YAML to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.yaml'; - const msg = await writer.writeYaml(JSON_CONTENT, { dest: fs.createWriteStream(file) }); + const msg = await writer.writeYaml(JSON_CONTENT, { + target: TYPE_YAML, + dest: fs.createWriteStream(file), + }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); it('should write stringified YAML to JS object', async () => { expect.assertions(3); - const options = { dest: {} }; + const options = { + target: TYPE_YAML, + dest: {} + }; const msg = await writer.writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); @@ -320,29 +349,28 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { const invalidYamlJson = () => {}; return expectWriterError(invalidYamlJson, { dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file-invalid.yaml' }, - writer.writeYaml, YAMLException); + writer.writeYaml, { name: 'YAMLException' }); }); it('should reject with Error on missing destination', () => { - return expectWriterError(JSON_CONTENT, { }, writer.writeYaml); + return expectWriterError(JSON_CONTENT, { }, writer.writeYaml, { name: 'ValidationError', isJoi: true }); }); }); describe('Testing force overwrite file', () => { it('should reject when options.dest is a directory', () => { - return expectWriterError(JSON_CONTENT, { dest: './test/data' }, writer.writeYaml); + return expectWriterErrorByType(JSON_CONTENT, { dest: './test/data' }, writer.writeYaml, Error); }); it('should write YAML to stream, overwrite on 2nd write, ' + 'don\'t overwrite on 3rd write and overwrite on 4th write', async () => { - // expect.assertions(11); + expect.assertions(13); const dest = WRITER_TEST_BASE_DIR + '/test-data-file-overwriting.yaml'; let options = { indent: 4, dest, }; - let index = 1; const asyncFunctions = [ async () => { const msg = await writer.writeYaml(JSON_CONTENT, options); @@ -363,6 +391,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { return 'overwrite test #2 should overwrite existing YAML file \'' + dest + '\''; }, async () => { + options = { + indent: 4, + dest, + force: false, + }; const msg = await writer.writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); @@ -373,7 +406,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { options = { indent: 4, dest, - force: false + force: true }; const msg = await writer.writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); @@ -388,13 +421,14 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }; const msg = await writer.writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); - await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); + await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); return 'overwrite test #5 shouldn\'t overwrite existing YAML file \'' + dest + '\' and \'' + WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml\', but write ' + 'new file \'' + WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml\''; } ]; + await asyncFunctions.reduce((p, fn, idx) => { return p.then((msg) => { if (msg) { diff --git a/test/unit/validation/test-joi-extensions-file-helper.js b/test/unit/validation/test-joi-extensions-file-helper.js new file mode 100644 index 0000000..310e9f9 --- /dev/null +++ b/test/unit/validation/test-joi-extensions-file-helper.js @@ -0,0 +1,27 @@ +import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; +import { isExistingFile } from '../../../src/validation/joi-extensions-file-helper'; + +/** + * @module jy-transform:test-unit:test-joi-extension-file-helper + * @description This unit test module tests validation FS helper method. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-file-helper - ', () => { + describe('Method isExistingFile(pathStr) ', () => { + it('should return true on relative path string with existing file', () => + expect(isExistingFile('test/unit/validation/test-joi-extensions-file-helper.js')).toBeTruthy() + ); + + it('should return true on relative path string with existing file starting with \'./\'', () => + expect(isExistingFile('./test/unit/validation/test-joi-extensions-file-helper.js')).toBeTruthy() + ); + + it('should return false on incorrect path string with non-existing file', () => + expect(isExistingFile('/foo/bar/non-exist.html')).toBeFalsy() + ); + + it('should return false on existing directory path string', () => + expect(isExistingFile('./test')).toBeFalsy() + ); + }); +}); diff --git a/test/unit/validation/test-joi-extensions-identifier-helper.js b/test/unit/validation/test-joi-extensions-identifier-helper.js new file mode 100644 index 0000000..4cf068d --- /dev/null +++ b/test/unit/validation/test-joi-extensions-identifier-helper.js @@ -0,0 +1,24 @@ +import { isValidEs6Identifier } from '../../../src/validation/joi-extensions-identifier-helper'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; + +/** + * @module jy-transform:unit-test:test-joi-extensions-identifier-helper + * @description This unit test suite checks validity and correctness of ES6 identifiers. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-identifier-helper - ', () => { + const nonStringIdentifier = {}; + it('should validate non-string identifier \'' + JSON.toString(nonStringIdentifier) + '\' to false', () => + expect(isValidEs6Identifier(nonStringIdentifier)).toBeFalsy() + ); + + const invalidIdentifier = '#3/-'; + it('should validate invalid identifier \'' + invalidIdentifier + '\' to false', () => + expect(isValidEs6Identifier(invalidIdentifier)).toBeFalsy() + ); + + const validIdentifier = 'bar'; + it('should validate \'' + validIdentifier + '\' identifier to true', () => + expect(isValidEs6Identifier(validIdentifier)).toBeTruthy() + ); +}); diff --git a/test/unit/validation/test-options-schema-helper.js b/test/unit/validation/test-options-schema-helper.js new file mode 100644 index 0000000..d5f1813 --- /dev/null +++ b/test/unit/validation/test-options-schema-helper.js @@ -0,0 +1,35 @@ +import stringify from 'json-stringify-safe'; +import stream from 'stream'; +import { + inferOriginDefaultFromFilePath, + inferTargetDefaultFromFilePath, +} from '../../../src/validation/options-schema-helper'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; +import { + TYPE_YAML, + TYPE_JS, + TYPE_JSON, + DEFAULT_FORCE_FILE_OVERWRITE, + DEFAULT_JS_IMPORTS_IDENTIFIER, + DEFAULT_JS_EXPORTS_IDENTIFIER, + DEFAULT_ORIGIN, + DEFAULT_TARGET, + DEFAULT_INDENT, + MIN_INDENT, + MAX_INDENT, +} from '../../../src/constants'; +import { transformerOptionsSchema } from '../../../src/validation/options-schema'; +import Joi from '../../../src/validation/joi-extensions'; + +/** + * @module jy-transform:unit-test:test-options-schema-helper + * @description This unit test suite checks the validity and correctness of options schema helper methods. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { + describe('Method isExistingFile(pathStr) ', () => { + it('should return true on relative path string with existing file', () => + expect(inferOriginDefaultFromFilePath('test/unit/validation/test-joi-extensions-file-helper.js')).toBeTruthy() + ); + }); +}); diff --git a/test/unit/validation/test-options-schema.js b/test/unit/validation/test-options-schema.js new file mode 100644 index 0000000..1b67de1 --- /dev/null +++ b/test/unit/validation/test-options-schema.js @@ -0,0 +1,423 @@ +import stringify from 'json-stringify-safe'; +import Stream from 'stream'; +import fs from 'fs'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; +import { + TYPE_YAML, + TYPE_JS, + TYPE_JSON, + DEFAULT_FORCE_FILE_OVERWRITE, + DEFAULT_JS_IMPORTS_IDENTIFIER, + DEFAULT_JS_EXPORTS_IDENTIFIER, + DEFAULT_ORIGIN, + DEFAULT_TARGET, + DEFAULT_INDENT, + MIN_INDENT, + MAX_INDENT, +} from '../../../src/constants'; +import { transformerOptionsSchema } from '../../../src/validation/options-schema'; +import Joi from '../../../src/validation/joi-extensions'; + +/** + * @module jy-transform:unit-test:test-options-schema + * @description This unit test suite checks the validity and correctness of options schema. + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { + /** + * Expect a `ValidationError` for a given options function. + * + * @param {Options} options - The options which potentially produce the error. + * @private + */ + function expectOptionsValidationError(options) { + expect.assertions(1); + return expect(Joi.validate(options, transformerOptionsSchema)).rejects.toMatchObject({ + name: 'ValidationError', + isJoi: true, + }); + } + + /** + * Expect a validation success for a given options. + * + * @param {Options} options - The options which should be correct. + * @private + */ + function expectOptionsValidationSuccess(options) { + expect.assertions(1); + return expect(Joi.validate(options, transformerOptionsSchema)).resolves.toMatchObject(options); + } + + describe('Testing options schema validation', () => { + it('should reject when options is missing (null)', async () => + await expectOptionsValidationError(null) + ); + + it('should reject when options is missing (undefined)', async () => + await expectOptionsValidationError(undefined) + ); + + it('should set all defaults', async () => { + expect.assertions(6); + const options = { + src: './test/data/test-data.yaml', + dest: './test/tmp/test-data.js', + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); + expect(validatedOptions.target).toBe(DEFAULT_TARGET); + expect(validatedOptions.indent).toBe(DEFAULT_INDENT); + expect(validatedOptions.imports).toBe(DEFAULT_JS_IMPORTS_IDENTIFIER); + expect(validatedOptions.exports).toBe(DEFAULT_JS_EXPORTS_IDENTIFIER); + expect(validatedOptions.force).toBe(DEFAULT_FORCE_FILE_OVERWRITE); + }); + + it('should infer options.origin and options.target from file type', async () => { + expect.assertions(2); + const options = { + src: './test/data/test-data.js', // non default type + dest: 'some-file.yml', // non default type + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.origin).toBe(TYPE_JS); + expect(validatedOptions.target).toBe(TYPE_YAML); + }); + + it('should infer options.origin and options.target from set origin and target', async () => { + expect.assertions(2); + const options = { + origin: TYPE_JS, + target: TYPE_YAML, + src: new Stream.Readable(), // no inference possible + dest: new Stream.Writable(), // no inference possible + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.origin).toBe(TYPE_JS); + expect(validatedOptions.target).toBe(TYPE_YAML); + }); + + it('should infer options.origin to JS from object type', async () => { + expect.assertions(1); + const options = { + src: {}, + dest: 'some-file', + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.origin).toBe(TYPE_JS); + }); + }); + + describe('Testing options.src schema validation', () => { + it('should reject when options.src is an existing directory path string', async () => + await expectOptionsValidationError({ + src: './test', + dest: 'some-file', + }) + ); + + it('should reject when options.src is undefined', async () => + await expectOptionsValidationError({ dest: 'some-file' }) + ); + + it('should reject when options.src is null', async () => + await expectOptionsValidationError({ + src: null, + dest: 'some-file', + }) + ); + + it('should resolve to default origin ' + DEFAULT_ORIGIN + ' when options.src is Stream.Readable and ' + + 'options.origin is not set', async () => { + expect.assertions(2); + const options = { + src: new Stream.Readable(), + dest: new Stream.Writable(), + target: TYPE_YAML, + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); + expect(validatedOptions.target).toBe(TYPE_YAML); + }); + }); + + describe('Testing options.dest schema validation', () => { + it('should reject when options.dest is undefined', async () => + await expectOptionsValidationError({ src: './test/data/test-data.js' }) + ); + + it('should reject when options.dest is null', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: null, + }) + ); + + it('should resolve output when options.dest is Stream.Writable and options.target is set', async () => { + expect.assertions(2); + const options = { + src: './test/data/test-data.js', + dest: new Stream.Writable(), + target: TYPE_YAML, + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.origin).toBe(TYPE_JS); + expect(validatedOptions.target).toBe(TYPE_YAML); + }); + + it('should resolve output when options.dest is Stream.Writable (to YAML file) and options.target is not set', + async () => { + expect.assertions(2); + const options = { + src: './test/data/test-data.js', + dest: fs.createWriteStream('./test/tmp/test-data.yaml'), + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.origin).toBe(TYPE_JS); + expect(validatedOptions.target).toBe(TYPE_YAML); + }); + + it('should resolve default ' + DEFAULT_TARGET + ' output when options.dest is Stream.Writable (without path) ' + + 'and options.target is not set)', async () => { + expect.assertions(2); + const options = { + src: './test/data/test-data.js', + dest: new Stream.Writable(), + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.origin).toBe(TYPE_JS); + expect(validatedOptions.target).toBe(DEFAULT_TARGET); + }); + }); + + describe('Testing options.origin schema validation', () => { + it('should resolve with default ' + DEFAULT_ORIGIN + ' when options.origin is undefined and options.src does ' + + 'not allow to infer the type', async () => { + const options = { + src: './test/data/test-data', + dest: 'some-file' + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); + }); + + it('should resolve when options.origin has valid target ' + TYPE_JS, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data', + dest: 'some-file', + origin: TYPE_JS, + }); + }); + + it('should resolve when options.origin has valid target ' + TYPE_JSON, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data-json', + dest: 'some-file', + origin: TYPE_JSON, + }); + }); + + it('should resolve when options.origin has valid target ' + TYPE_YAML, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data-yaml', + dest: 'some-file', + origin: TYPE_YAML, + }); + }); + + it('should reject when options.origin is null', async () => + await expectOptionsValidationError({ + src: './test/data/test-data-yaml', + dest: 'some-file', + origin: null, + }) + ); + + it('should reject when options.origin is not allowed value', async () => + await expectOptionsValidationError({ + src: './test/data/test-data-yaml', + dest: 'some-file', + origin: 'not-allowed', + }) + ); + }); + + describe('Testing options.target schema validation', () => { + it('should resolve with default ' + DEFAULT_TARGET + ' when options.target is undefined', async () => { + const options = { + src: './test/data/test-data.js', + dest: 'some-file' + }; + const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + expect(validatedOptions.target).toBe(DEFAULT_TARGET); + }); + + it('should resolve when options.target has valid target ' + TYPE_JS, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + target: TYPE_JS, + }); + }); + + it('should resolve when options.target has valid target ' + TYPE_JSON, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data.json', + dest: 'some-file', + target: TYPE_JS, + }); + }); + + it('should resolve when options.target has valid target ' + TYPE_YAML, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data.yaml', + dest: 'some-file', + target: TYPE_YAML, + }); + }); + + it('should reject when options.target is null', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + target: null, + }) + ); + + it('should reject when options.target is not allowed value', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + target: 'not-allowed', + }) + ); + }); + + describe('Testing options.imports schema validation', () => { + const nonStringIdentifier = {}; + it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + imports: nonStringIdentifier, + }) + ); + + const invalidIdentifier = '#3/-'; + it('should reject invalid identifier \'' + invalidIdentifier + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + imports: invalidIdentifier, + }) + ); + + const validIdentifier = 'bar'; + it('should accept valid \'' + validIdentifier + '\' identifier', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + imports: validIdentifier, + }) + ); + }); + + describe('Testing options.exports schema validation', () => { + const nonStringIdentifier = {}; + it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + exports: nonStringIdentifier, + }) + ); + + const invalidIdentifier = '#3/-'; + it('should reject invalid identifier \'' + invalidIdentifier + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + exports: invalidIdentifier, + }) + ); + + const validIdentifier = 'bar'; + it('should accept valid \'' + validIdentifier + '\' identifier', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + exports: validIdentifier, + }) + ); + }); + + describe('Testing options.force schema validation', () => { + const notBoolean = {}; + it('should reject non-boolean value \'' + stringify(notBoolean) + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + force: notBoolean, + }) + ); + + it('should accept valid value \'false\'', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + force: false, + }) + ); + + it('should accept valid value \'true\'', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + force: true, + }) + ); + }); + + describe('Testing options.indent schema validation', () => { + const notInteger = 0.5; + it('should reject non-integer value \'' + stringify(notInteger) + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: notInteger, + }) + ); + + it('should accept valid \'' + MIN_INDENT + '\'', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: MIN_INDENT, + }) + ); + + it('should accept valid \'' + MAX_INDENT + '\'', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: MAX_INDENT, + }) + ); + + it('should reject < \'' + MIN_INDENT + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: MIN_INDENT - 1, + }) + ); + + it('should reject > \'' + MAX_INDENT + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: MAX_INDENT + 1, + }) + ); + }); +}); From 91f1d4e78b521a036e7ffaf030f0f0c82368dc46 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 2 Jun 2017 09:32:35 +0200 Subject: [PATCH 03/58] remove crap --- test-data-by-js-to-file.json | 3 --- test.js | 36 ------------------------------------ 2 files changed, 39 deletions(-) delete mode 100644 test-data-by-js-to-file.json delete mode 100644 test.js diff --git a/test-data-by-js-to-file.json b/test-data-by-js-to-file.json deleted file mode 100644 index d0ae716..0000000 --- a/test-data-by-js-to-file.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "test": "test" -} diff --git a/test.js b/test.js deleted file mode 100644 index 0d7c584..0000000 --- a/test.js +++ /dev/null @@ -1,36 +0,0 @@ -const path = require('path'); - - -// import { Writer } from './src/writer'; -// import { Reader } from './src/reader'; -// -// (async () => { -// try { -// const writer = new Writer(); -// const file = './test-data-by-js-to-file.json'; -// console.log('test'); -// const msg = await writer.writeJson({ test: 'test' }, { dest: file }); -// console.log(msg); -// } catch (err) { -// console.error(err); -// } -// })(); -// -// (async () => { -// try { -// const reader = new Reader(); -// console.log('test2'); -// const msg = await reader.readJs({ -// origin: 'json', -// src: './test/data/test-data-corrupted.json' -// }); -// console.log(msg); -// } catch (err) { -// console.error(JSON.stringify(err.message, null, 4)); -// } -// })(); - -console.log(path.resolve('test/data/test-data.js')); -console.log(path.resolve('/test/data/test-data.js')); -console.log(path.resolve('./test/data/test-data.js')); -console.log(path.resolve('/Users/jens.krefeldt/Development/finanzcheck/jy-transform/test/data/test-data.js')); From 0820d98ddb857b20010dd2345374ae1c77f119d6 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 20 Jun 2017 23:39:31 +0200 Subject: [PATCH 04/58] Tests green, but coverage not 100% --- .jestrc.js | 16 +-- Makefile | 2 +- README.md | 26 ++-- docs/CHANGELOG.md | 2 +- docs/USAGE.md | 24 ++-- inch.json | 20 +-- jyt | 116 +--------------- src/debug-log.js | 29 +++- src/jyt.js | 112 ++++++++++++++++ src/middleware.js | 2 +- src/transformer.js | 29 ++-- src/validation/joi-extensions-file-helper.js | 4 +- src/validation/joi-extensions.js | 4 +- src/validation/options-schema-helper.js | 16 +-- src/validation/options-schema.js | 8 +- src/writer.js | 38 +++--- test/unit/test-middleware.js | 4 +- test/unit/test-reader.js | 2 +- test/unit/test-writer.js | 133 +++++++++++-------- 19 files changed, 313 insertions(+), 274 deletions(-) create mode 100755 src/jyt.js diff --git a/.jestrc.js b/.jestrc.js index 511463b..c899b8c 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -7,7 +7,7 @@ module.exports = { 'index.js' ], coverageDirectory: './coverage/', - coverageReporters: ['html', 'lcov', 'lcovonly', 'text'], + coverageReporters: ['html', 'lcov', 'lcovonly', 'text'], coverageThreshold: { global: { branches: 100, @@ -18,17 +18,17 @@ module.exports = { }, mapCoverage: true, testMatch: [ - '**/test/unit/validation/test-joi-extensions-file-helper.js', - '**/test/unit/validation/test-joi-extensions-identifier-helper.js', + // '**/test/unit/validation/test-joi-extensions-file-helper.js', + // '**/test/unit/validation/test-joi-extensions-identifier-helper.js', //'**/test/unit/test-transformer.js', // '**/test/unit/test-index.js', - '**/test/unit/test-reader.js', - // '**/test/unit/test-writer.js', - '**/test/unit/validation/test-options-schema.js', - //'**/test/unit/**/*.js', + //'**/test/unit/test-reader.js', + //'**/test/unit/test-writer.js', + // '**/test/unit/validation/test-options-schema.js', + '**/test/unit/**/*.js', // '**/test/test-log-wrapper.js', - // '**/test/test-middleware.js', + // '**/test/unit/test-middleware.js', // '**/test/test-index.js', //'/*.js!**/test/functional/util/**', // '!**/test/*.js', diff --git a/Makefile b/Makefile index 790d10a..9ce77a7 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ publish: test readme ## Publishes module to NPM registry. npm publish eslint: ## Runs ESLint. - @echo "Running ESLint...\n" + @printf "Running ESLint...\n" npm run eslint help: ## Prints the help about targets. diff --git a/README.md b/README.md index 77c8e4e..6e6cb45 100644 --- a/README.md +++ b/README.md @@ -288,15 +288,15 @@ Additionally, on API level to a: ## CLI Usage -The CLI provides the `jyt` command (actually, this might require the use of options). +The CLI provides the `jyt.js` command (actually, this might require the use of options). After the global installation you can access the `Transformer`'s command options with the usual `--help` option which prints an overview about all available CLI properties: ``` -$ jyt --help +$ jyt.js --help Usage: - jyt INPUT-FILE [OUTPUT-FILE] [OPTIONS] + jyt.js INPUT-FILE [OUTPUT-FILE] [OPTIONS] Options: -o, --origin [STRING] The origin type of INPUT-FILE: [ js | json | yaml ]. (Default is if not given, the type is tried to be inferred from the extension of source path, else it is 'yaml') @@ -367,7 +367,7 @@ Then we can transform it to a JSON content as _foo.json_ file: simply by using this command: ``` -$ jyt foo.yaml -t json -i 2 +$ jyt.js foo.yaml -t json -i 2 ``` In this example we have overwritten the standard target type (which is `js`) @@ -380,7 +380,7 @@ default `js` would have been applied! If the source would have been a `js` type like ``` -$ jyt foo.js -t json -i 2 +$ jyt.js foo.js -t json -i 2 ``` then the `js` value for `origin` is automatically inferred from file extension. @@ -389,7 +389,7 @@ Accordingly, this is also true for the `target` option. #### Example: JSON ⇒ JS The command ``` -$ jyt foo.json -i 2 +$ jyt.js foo.json -i 2 ``` results in _foo.js_: ```javascript @@ -401,7 +401,7 @@ module.exports = { #### Example: JS ⇒ YAML The command ``` -$ jyt foo.js -t yaml +$ jyt.js foo.js -t yaml ``` results in _foo.yaml_: ```yaml @@ -412,7 +412,7 @@ foo: bar Simply specify the _output_ file with a different file name: ``` -$ jyt foo.json results/foobar.yaml +$ jyt.js foo.json results/foobar.yaml ``` #### Example: Transformation with Unsupported Source File Extension @@ -422,7 +422,7 @@ file has a file name which does not imply the type (here a JSON type in a TEXT file), then you can simply provide the `-o` option with the correct `origin` type (of course, the `-t` option works analogous): ``` -$ jyt foo.txt foobar.yaml -o json +$ jyt.js foo.txt foobar.yaml -o json ``` #### Example: Read from File with Exports Identifier @@ -443,7 +443,7 @@ module.exports.bar = { ``` but you want to convert only `bar` object, then call: ``` -$ jyt foo.js bar.yaml -m bar +$ jyt.js foo.js bar.yaml -m bar ``` to get the YAML result: ```yaml @@ -486,7 +486,7 @@ foo: bar ``` using this command: ``` -$ jyt foo.yaml foobar.js -x foobar +$ jyt.js foo.yaml foobar.js -x foobar ``` This generates the following output in JS file using `foobar` as identifier: ```javascript @@ -511,7 +511,7 @@ overwriting your input source or already generated targets. But let's say we want to overwrite the original source now because you want to change the indention from 2 to 4 SPACEs, then we can do this as follows: ``` -$ jyt foo.js -f +$ jyt.js foo.js -f ``` Of course, leaving out the `-f` switch creates a new file relatively to the `origin`, named as _foo(1).js_ (note the consecutive number). Naturally, @@ -782,7 +782,7 @@ section for more details about conventions. - [[#33](https://github.com/deadratfink/jy-transform/issues/33)] Enhance `LogWrapper` with `TRACE` level (API) - [[#32](https://github.com/deadratfink/jy-transform/issues/32)] Introduce input and output on CLI as ARGS instead of OPTIONS (non-backwards compatible change for CLI usage, _no_ impact on API level!) - - e.g. on CLI type in `$ jyt foo.js bar.yaml` instead of `$ jyt -s foo.js -d bar.yaml` + - e.g. on CLI type in `$ jyt.js foo.js bar.yaml` instead of `$ jyt.js -s foo.js -d bar.yaml` - [[#31](https://github.com/deadratfink/jy-transform/issues/31)] Bugfix: given `Object` source results in 'yaml' for origin (API) - [Cleanup] Update dependencies diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bd9a7c1..cd55faf 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -41,7 +41,7 @@ - [[#33](https://github.com/deadratfink/jy-transform/issues/33)] Enhance `LogWrapper` with `TRACE` level (API) - [[#32](https://github.com/deadratfink/jy-transform/issues/32)] Introduce input and output on CLI as ARGS instead of OPTIONS (non-backwards compatible change for CLI usage, _no_ impact on API level!) - - e.g. on CLI type in `$ jyt foo.js bar.yaml` instead of `$ jyt -s foo.js -d bar.yaml` + - e.g. on CLI type in `$ jyt.js foo.js bar.yaml` instead of `$ jyt.js -s foo.js -d bar.yaml` - [[#31](https://github.com/deadratfink/jy-transform/issues/31)] Bugfix: given `Object` source results in 'yaml' for origin (API) - [Cleanup] Update dependencies diff --git a/docs/USAGE.md b/docs/USAGE.md index e97a99b..5701a47 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -126,15 +126,15 @@ Additionally, on API level to a: ## CLI Usage -The CLI provides the `jyt` command (actually, this might require the use of options). +The CLI provides the `jyt.js` command (actually, this might require the use of options). After the global installation you can access the `Transformer`'s command options with the usual `--help` option which prints an overview about all available CLI properties: ``` -$ jyt --help +$ jyt.js --help Usage: - jyt INPUT-FILE [OUTPUT-FILE] [OPTIONS] + jyt.js INPUT-FILE [OUTPUT-FILE] [OPTIONS] Options: -o, --origin [STRING] The origin type of INPUT-FILE: [ js | json | yaml ]. (Default is if not given, the type is tried to be inferred from the extension of source path, else it is 'yaml') @@ -205,7 +205,7 @@ Then we can transform it to a JSON content as _foo.json_ file: simply by using this command: ``` -$ jyt foo.yaml -t json -i 4 +$ jyt.js foo.yaml -t json -i 4 ``` In this example we have overwritten the standard target type (which is `js`) @@ -218,7 +218,7 @@ default `js` would have been applied! If the source would have been a `js` type like ``` -$ jyt foo.js -t json -i 4 +$ jyt.js foo.js -t json -i 4 ``` then the `js` value for `origin` is automatically inferred from file extension. @@ -227,7 +227,7 @@ Accordingly, this is also true for the `target` option. #### Example: JSON ⇒ JS The command ``` -$ jyt foo.json -i 4 +$ jyt.js foo.json -i 4 ``` results in _foo.js_: ```javascript @@ -239,7 +239,7 @@ module.exports = { #### Example: JS ⇒ YAML The command ``` -$ jyt foo.js -t yaml +$ jyt.js foo.js -t yaml ``` results in _foo.yaml_: ```yaml @@ -250,7 +250,7 @@ foo: bar Simply specify the _output_ file with a different file name: ``` -$ jyt foo.json results/foobar.yaml +$ jyt.js foo.json results/foobar.yaml ``` #### Example: Transformation with Unsupported Source File Extension @@ -260,7 +260,7 @@ file has a file name which does not imply the type (here a JSON type in a TEXT file), then you can simply provide the `-o` option with the correct `origin` type (of course, the `-t` option works analogous): ``` -$ jyt foo.txt foobar.yaml -o json +$ jyt.js foo.txt foobar.yaml -o json ``` #### Example: Read from File with Exports Identifier @@ -281,7 +281,7 @@ module.exports.bar = { ``` but you want to convert only `bar` object, then call: ``` -$ jyt foo.js bar.yaml -m bar +$ jyt.js foo.js bar.yaml -m bar ``` to get the YAML result: ```yaml @@ -324,7 +324,7 @@ foo: bar ``` using this command: ``` -$ jyt foo.yaml foobar.js -x foobar +$ jyt.js foo.yaml foobar.js -x foobar ``` This generates the following output in JS file using `foobar` as identifier: ```javascript @@ -349,7 +349,7 @@ overwriting your input source or already generated targets. But let's say we want to overwrite the original source now because you want to change the indention from 2 to 4 SPACEs, then we can do this as follows: ``` -$ jyt foo.js -f +$ jyt.js foo.js -f ``` Of course, leaving out the `-f` switch creates a new file relatively to the `origin`, named as _foo(1).js_ (note the consecutive number). Naturally, diff --git a/inch.json b/inch.json index 27b91b7..4268668 100644 --- a/inch.json +++ b/inch.json @@ -1,12 +1,12 @@ { - "files": { - "included": [ - "lib/**/*.js", - "test/test*.js", - "index.js", - "jyt" - ], - "excluded": [ - ] - } + "files": { + "included": [ + "src/**/*.js", + "test/test*.js", + "index.js", + "jyt" + ], + "excluded": [ + ] + } } diff --git a/jyt b/jyt index 266f1b8..be0f0db 100755 --- a/jyt +++ b/jyt @@ -1,114 +1,4 @@ -#!/usr/bin/env node +#!./node_modules/.bin/babel-node -var path = require('path'); -var constants = require('./lib/constants.js'); -var cli = require('cli'); -var Transformer = require('./lib/transformer.js'); -var transformer = new Transformer(cli); - -// //////////////////////////////////////////////////////////////////////////// -// CLI INIT -// //////////////////////////////////////////////////////////////////////////// - -/** - * How to use the CLI. - * - * @type {string} - * @private - */ -var usage = path.basename(__filename) + ' INPUT-FILE [OUTPUT-FILE] [OPTIONS]'; - -/** - * The path to package.json. - * - * @type {string} - * @private - */ -var packagePath = path.join(__dirname, 'package.json'); - -/** - * The options description for parsing the command line input, must be an object with opts defined like: - * ``` - * long_tag: [short_tag, description, value_type, default_value]; - * ``` - * @type {{origin: *[], target: *[], src: string[], dest: *[], indent: *[], force: string[], imports: string, - * exports: string}} - * @private - */ -var options = { - origin: ['o', 'The origin type of INPUT-FILE: [ ' + constants.JS + ' | ' + constants.JSON + ' | ' + constants.YAML - + ' ]', 'string', constants.DEFAULT_OPTIONS.origin], - target: ['t', 'The target type of OUTPUT-FILE: [ ' + constants.JS + ' | ' + constants.JSON + ' | ' + constants.YAML - + ' ]', 'string', constants.DEFAULT_OPTIONS.target], - indent: ['i', 'The indention for pretty-print: 1 - 8', 'int', constants.DEFAULT_INDENT], - force: ['f', 'Force overwriting of existing output files on write phase: when files are not overwritten (which' + - ' is default), then the next transformation with same output file name gets a consecutive number on the base' + - ' file name, e.g. in case of foo.yaml it would be foo(1).yaml'], - imports: ['m', 'Define a \'module.exports[.identifier] = \' identifier (to read from JS _source_ file only, must' + - ' be a valid JS identifier!)', 'string', constants.DEFAULT_OPTIONS.imports], - exports: ['x', 'Define a \'module.exports[.identifier] = \' identifier (for usage in JS destination file only,' + - ' must be a valid JS identifier!)', 'string', constants.DEFAULT_OPTIONS.exports] -}; - -/** - * Prints the error to console and exit with 1. - * - * @param {string|Error} err - The error to print. - * @private - */ -function error(err) { - cli.error('////////////////////////////////////////////////////////////////////////////////'); - cli.error(err); - if (err.stack) { - cli.debug(err.stack); - } - cli.error('////////////////////////////////////////////////////////////////////////////////'); - cli.getUsage(1); -} - -/** - * The main entry callback. When calling `cli.main()` this receives the `options` - * given on CLI, then does the transformation with these options and finally, it - * prints the result to the CLI. - * - * @param {Array} args - The first mandatory argument is the input file (`args[0]`), the second (optional) - * argument is the output file (`args[1]`). - * @param {module:type-definitions~Options} cliOptions - The options provided via CLI. - * @private - */ -function main(args, cliOptions) { - // read file args and set to options - - if (args.length > 0) { - cli.debug('input file: ' + args[0]); - cliOptions.src = args[0]; - } else { - error('please specify an input file as first argument!'); - } - if (args.length > 1) { - cli.debug('output file: ' + args[1]); - cliOptions.dest = args[1]; - } else { - cli.debug('output file not specified, using default'); - } - - // transform with options - - return transformer.transform(cliOptions) - .then(function (msg) { - cli.info(msg); - }) - .catch(function (err) { - error(err); - }); -} - -/* - * Init the CLI instance. - */ -cli.width = 120; -cli.setUsage(usage); -cli.setApp(packagePath); -cli.enable('version', 'status', 'timeout'); -cli.parse(options); -cli.main(main); +// eslint-disable-next-line no-unused-vars +import jyt from './src/jyt'; diff --git a/src/debug-log.js b/src/debug-log.js index 08a0366..59f8e9d 100644 --- a/src/debug-log.js +++ b/src/debug-log.js @@ -1,6 +1,23 @@ -export const debug = typeof process.env.JYT_DEBUG !== 'undefined' ? - console.log.bind(null, '[DEBUG][jyt]:') : - (() => null); -export const error = typeof process.env.JYT_ERROR !== 'undefined' ? - console.error.bind(null, '[ERROR][jyt]:') : - (() => null); +/* eslint-disable no-console */ + +/** + * @module jy-transform:debug-log + * @description The debug logger. Can be enabled via environment variables (set to `true`): + * - `JYT_DEBUG`: (only DEBUG logging via `console.log`) + * - `JYT_DEBUG`: (only ERROR logging via `console.error`) + * @public + */ + +/** + * DEBUG function. + * @protected + */ +export const debug = process.env.JYT_DEBUG === 'true' ? + console.log.bind(null, '[DEBUG][jyt.js]:') : + (() => null); + +export const error = process.env.JYT_ERROR === 'true' ? + console.error.bind(null, '[ERROR][jyt.js]:') : + (() => null); + +/* eslint-enable no-console */ diff --git a/src/jyt.js b/src/jyt.js new file mode 100755 index 0000000..94ab602 --- /dev/null +++ b/src/jyt.js @@ -0,0 +1,112 @@ +import path from 'path'; +import cli from 'cli'; +import { DEFAULT_INDENT, DEFAULT_OPTIONS, TYPE_JS, TYPE_JSON, TYPE_YAML } from './constants'; +import { transform } from './transformer'; + +// //////////////////////////////////////////////////////////////////////////// +// CLI INIT +// //////////////////////////////////////////////////////////////////////////// + +/** + * How to use the CLI. + * + * @type {string} + * @private + */ +const usage = path.basename(__filename) + ' INPUT-FILE [OUTPUT-FILE] [OPTIONS]'; + +/** + * The path to package.json. + * + * @type {string} + * @private + */ +const packagePath = path.join(__dirname, '../package.json'); + +/** + * The options description for parsing the command line input, must be an object with opts defined like: + * ``` + * long_tag: [short_tag, description, value_type, default_value]; + * ``` + * @type {{origin: *[], target: *[], src: string[], dest: *[], indent: *[], force: string[], imports: string, + * exports: string}} + * @private + */ +const options = { + origin: [ + 'o', 'The origin type of INPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + + ' ]', 'string', DEFAULT_OPTIONS.origin], + target: [ + 't', 'The target type of OUTPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + + ' ]', 'string', DEFAULT_OPTIONS.target], + indent: ['i', 'The indention for pretty-print: 1 - 8', 'int', DEFAULT_INDENT], + force: [ + 'f', 'Force overwriting of existing output files on write phase: when files are not overwritten (which' + + ' is default), then the next transformation with same output file name gets a consecutive number on the base' + + ' file name, e.g. in case of foo.yaml it would be foo(1).yaml'], + imports: [ + 'm', 'Define a \'module.exports[.identifier] = \' identifier (to read from JS _source_ file only, must' + + ' be a valid JS identifier!)', 'string', DEFAULT_OPTIONS.imports], + exports: [ + 'x', 'Define a \'module.exports[.identifier] = \' identifier (for usage in JS destination file only,' + + ' must be a valid JS identifier!)', 'string', DEFAULT_OPTIONS.exports] +}; + +/** + * Prints the error to console and exit with 1. + * + * @param {string|Error} err - The error to print. + * @private + */ +function error(err) { + cli.error('////////////////////////////////////////////////////////////////////////////////'); + cli.error(err); + if (err.stack) { + cli.debug(err.stack); + } + cli.error('////////////////////////////////////////////////////////////////////////////////\n'); + cli.getUsage(1); +} + +/** + * The main entry callback. When calling `cli.main()` this receives the `options` + * given on CLI, then does the transformation with these options and finally, it + * prints the result to the CLI. + * + * @param {Array} args - The first mandatory argument is the input file (`args[0]`), the second (optional) + * argument is the output file (`args[1]`). + * @param {module:type-definitions~Options} cliOptions - The options provided via CLI. + * @private + */ +function main(args, cliOptions) { + // read file args and set to options + + if (args.length > 0) { + cli.debug('input file: ' + args[0]); + cliOptions.src = args[0]; + } else { + error('please specify an input file as first argument!'); + } + if (args.length > 1) { + cli.debug('output file: ' + args[1]); + cliOptions.dest = args[1]; + } else { + cli.debug('output file not specified, using default'); + } + + // transform with options + + return transform(cliOptions) + .then(msg => cli.info(msg)) + .catch(err => error(err)); +} + +/* + * Init the CLI instance. + */ +cli.width = 120; +cli.setUsage(usage); +cli.setApp(packagePath); +cli.enable('version', 'status', 'timeout'); +cli.parse(options); +cli.main(main); diff --git a/src/middleware.js b/src/middleware.js index 32a161c..f71d737 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -1,5 +1,5 @@ /** - * @module jy-transform:middlware + * @module jy-transform:middleware * @description The module exporting the {@link external:joi.Extension}s for option validations. * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension Joi.extend(extension) method}. * @public diff --git a/src/transformer.js b/src/transformer.js index 1f82aca..261918a 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -1,11 +1,11 @@ import logger from 'cli'; +import { TRANSFORMATIONS } from './constants'; +import { ensureMiddleware } from './middleware'; import { readJs, readYaml } from './reader'; -import { writeJs, writeJson, writeYaml } from './writer'; +import Joi from './validation/joi-extensions'; //import { LogWrapper } from './log-wrapper'; import { transformerOptionsSchema } from './validation/options-schema'; -import Joi from './validation/joi-extensions'; -import { ensureMiddleware } from './middleware'; -import { TRANSFORMATIONS } from './constants'; +import { writeJs, writeJson, writeYaml } from './writer'; /** * This method validates the transformation process described by the given @@ -32,6 +32,7 @@ async function createAndValidateTransformation(options) { throw new Error('Unsupported target type transformation \'' + options.origin + ' -> ' + options.target + '\' configured in options.'); } + console.log('Transformation: ' + transformation) return transformation; } @@ -47,16 +48,17 @@ async function createAndValidateTransformation(options) { * @param {Function} write - The writer functions. * @example * var options = {...}; - * var middleware = function (obj) { + * var middleware = async (obj) => { * ... * }; - * itmo(options, readYaml, middleware, writeJson); + * const result = await itmo(options, readYaml, middleware, writeJson); * @private */ -function itmo(options, read, middleware, write) { - return read(options) - .then(ensureMiddleware(middleware)) - .then(json => write(json, options)); +async function itmo(options, read, middleware, write) { + const ensuredMiddleware = ensureMiddleware(middleware); + let json = await read(options); + json = await ensuredMiddleware(json); + return await write(json, options); } // //////////////////////////////////////////////////////////////////////////// @@ -70,9 +72,8 @@ function itmo(options, read, middleware, write) { * @param {Function} [middleware] - This middleware Promise can be used to intercept * the JS object for manipulation. The function signature is `function(object)`. * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. + * @returns {Promise.} Containing the transformation result as message (e.g. to be logged by caller). + * @throws {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. * @throws {Error} Will throw plain error when writing to JS destination failed due to any reason. * @example * import { transformer } from 'jy-transform'; @@ -336,7 +337,7 @@ const transformations = { // */ // export class Transformer { - // const logger = new LogWrapper(logger); +// const logger = new LogWrapper(logger); // const writer = new Writer(logger); // const reader = new Reader(logger); diff --git a/src/validation/joi-extensions-file-helper.js b/src/validation/joi-extensions-file-helper.js index 2632ecf..fa8f17c 100644 --- a/src/validation/joi-extensions-file-helper.js +++ b/src/validation/joi-extensions-file-helper.js @@ -1,6 +1,6 @@ import fs from 'fs'; import path from 'path'; -import { debug, error } from '../debug-log'; +import { debug } from '../debug-log'; /** * @module validation:joi-extensions-file-helper @@ -17,7 +17,7 @@ import { debug, error } from '../debug-log'; */ export function isExistingFile(pathStr) { debug('>>>>>>>>>>>>>DEBUG ===================================') - error('>>>>>>>>>>>>>ERROR ===================================' + new Error('JYT Error')) + //error('>>>>>>>>>>>>>ERROR ===================================' + new Error('JYT Error')) const filePath = path.resolve(pathStr); try { const stats = fs.statSync(filePath); diff --git a/src/validation/joi-extensions.js b/src/validation/joi-extensions.js index f31300c..a35e68a 100644 --- a/src/validation/joi-extensions.js +++ b/src/validation/joi-extensions.js @@ -21,8 +21,8 @@ export const EXTENSIONS = { base: JoiBase.string(), name: 'string', language: { - existingFile: 'needs to be an absolute or relative path to an existing file [given "value": {{v}}, {{err}}]', - validEs6Identifier: 'needs to be a valid ECMAScript 6 identifier [given "value": {{v}}, {{err}}]' + existingFile: 'needs to be an absolute or relative path to an existing file (given file path: {{v}})', + validEs6Identifier: 'needs to be a valid ECMAScript 6 identifier (given identifier: {{v}})' }, rules: [ { diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-helper.js index 994d939..e43e2c8 100644 --- a/src/validation/options-schema-helper.js +++ b/src/validation/options-schema-helper.js @@ -49,13 +49,13 @@ const getTypeFromFilePath = (pathStr, origin, defaultValue) => { * @protected */ export const inferOriginDefaultFromStreamReadableFilePath = (context) => { - let destType; + let type; if (isStream.readable(context.dest)) { - destType = getTypeFromFilePath(context.src.path, true, DEFAULT_ORIGIN); + type = getTypeFromFilePath(context.src.path, true, DEFAULT_ORIGIN); } else { - destType = DEFAULT_TARGET; + type = DEFAULT_ORIGIN; } - return destType; + return type; }; /** @@ -75,13 +75,13 @@ export const inferOriginDefaultFromFilePath = (context) => { * @protected */ export const inferTargetDefaultFromStreamWritableFilePath = (context) => { - let destType; + let type; if (isStream.writable(context.dest)) { - destType = getTypeFromFilePath(context.dest.path, false, DEFAULT_TARGET); + type = getTypeFromFilePath(context.dest.path, false, DEFAULT_TARGET); } else { - destType = DEFAULT_TARGET; + type = DEFAULT_TARGET; } - return destType; + return type; }; /** diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js index f2b1b96..d66403e 100644 --- a/src/validation/options-schema.js +++ b/src/validation/options-schema.js @@ -39,6 +39,7 @@ export const readerOptionsSchema = Joi.object().keys({ .alternatives().try( Joi.string() .min(1) + .label('src - INPUT-FILE') .existingFile(), Joi.object().type(Stream.Readable), Joi.object().type(Object), @@ -59,7 +60,7 @@ export const readerOptionsSchema = Joi.object().keys({ then: Joi .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferOriginDefaultFromFilePath, 'tried origin default inferred from src type if not set (String)'), + .default(inferOriginDefaultFromFilePath, 'origin resolving from src type if latter not set (String)'), otherwise: Joi // else could only be JS Object .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) @@ -84,7 +85,8 @@ export const writerOptionsSchema = Joi.object().keys({ dest: Joi .alternatives().try( Joi.string() // TODO must be existing file (relative or not? -> check) - .min(1), + .min(1) + .label('dest - OUTPUT-FILE'), Joi.object().type(Stream.Writable), Joi.object().type(Object), ) @@ -104,7 +106,7 @@ export const writerOptionsSchema = Joi.object().keys({ then: Joi .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferTargetDefaultFromFilePath, 'tried target default inferred from dest type if not set (String)'), + .default(inferTargetDefaultFromFilePath, 'try target resolving from dest type if latter not set (String)'), otherwise: Joi // check .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) diff --git a/src/writer.js b/src/writer.js index baaa017..d893705 100644 --- a/src/writer.js +++ b/src/writer.js @@ -1,21 +1,16 @@ -import serializeJs from 'serialize-js'; +import logger from 'cli'; +import fs from 'fs'; +import isStream from 'is-stream'; import jsYaml from 'js-yaml'; -import promisify from 'promisify-es6'; -import mkdirp from 'mkdirp-then'; import jsonStringifySafe from 'json-stringify-safe'; -import path from 'path'; -import fs from 'fs'; +import mkdirp from 'mkdirp-then'; import os from 'os'; -import isStream from 'is-stream'; -import logger from 'cli'; -import { transformerOptionsSchema } from './validation/options-schema'; +import path from 'path'; +import promisify from 'promisify-es6'; +import serializeJs from 'serialize-js'; +import { TYPE_JS, TYPE_JSON, TYPE_YAML, UTF8 } from './constants'; import Joi from './validation/joi-extensions'; -import { - UTF8, - TYPE_YAML, - TYPE_JS, - TYPE_JSON, -} from './constants'; +import { transformerOptionsSchema } from './validation/options-schema'; const fsPromisified = promisify(fs); @@ -50,7 +45,8 @@ function createExportsString(exportsTo) { * @param {string} [exportsTo] - Name for export (*IMPORTANT:* must be a valid ES6 identifier). * @returns {Promise} - Promise resolve with the serialized JS object. * @private - * @todo [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> `'\'use strict\';' + os.EOL + os.EOL + ...`)? + * @todo [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> + * `'\'use strict\';' + os.EOL + os.EOL + ...`)? */ function serializeJsToString(object, indent, exportsTo) { return createExportsString(exportsTo) @@ -164,7 +160,7 @@ function writeToFile(object, dest, target, resolve, reject, forceOverwrite) { */ function writeToStream(object, dest, target, resolve, reject) { dest - .on('error', err => reject(err)) + .on('error', reject) .on('finish', () => resolve('Writing ' + target + ' to stream successful.')); // write stringified data @@ -405,6 +401,7 @@ export async function writeJson(object, options) { * }); */ export async function writeJs(object, options) { + //logger.debug('OPTIONS BEFORE ASSERTING IN writeJs:::' + JSON.stringify(options)); const assertedOptions = await Joi.validate(options, transformerOptionsSchema); return new Promise((resolve, reject) => { if (typeof assertedOptions.dest === 'string') { // file @@ -412,27 +409,26 @@ export async function writeJs(object, options) { .then((data) => { writeToFile(data, assertedOptions.dest, TYPE_JS, resolve, reject, assertedOptions.force); }) - .catch(err => reject(err)); + .catch(reject); } else if (isStream.writable(assertedOptions.dest)) { // stream serializeJsToString(object, assertedOptions.indent, assertedOptions.exports) .then((data) => { writeToStream(data, assertedOptions.dest, TYPE_JS, resolve, reject); }) - .catch(err => reject(err)); + .catch(reject); } else { // object let msg; if (assertedOptions.exports) { - options.dest[assertedOptions.exports] = object; + assertedOptions.dest[assertedOptions.exports] = object; msg = 'Writing JS to options.dest.' + assertedOptions.exports + ' successful.'; } else { - options.dest = object; + Object.assign(assertedOptions.dest, object); msg = 'Writing JS to options.dest successful.'; } resolve(msg); } }); } -// } export default { writeJs, diff --git a/test/unit/test-middleware.js b/test/unit/test-middleware.js index ff32ce7..e230e71 100644 --- a/test/unit/test-middleware.js +++ b/test/unit/test-middleware.js @@ -1,8 +1,8 @@ import objectPath from 'object-path'; -import { logger } from '../logger'; -import { transform } from '../../src/transformer'; import { ensureMiddleware, identityMiddleware } from '../../src/middleware'; +import { transform } from '../../src/transformer'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; +import { logger } from '../logger'; /** * @module jy-transform:unit-test:test-middleware diff --git a/test/unit/test-reader.js b/test/unit/test-reader.js index 61c580f..55f2a35 100644 --- a/test/unit/test-reader.js +++ b/test/unit/test-reader.js @@ -11,7 +11,7 @@ import { /** * @module jy-transform:unit-test:test-reader - * @description This unit test suite checks the validity and correctness of {@link Reader} class. + * @description This unit test suite checks the validity and correctness of _./src/reader.js_ module. */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js index f97789a..dee83a6 100644 --- a/test/unit/test-writer.js +++ b/test/unit/test-writer.js @@ -4,7 +4,7 @@ import YAMLException from 'js-yaml/lib/js-yaml/exception'; import fs from 'fs'; import os from 'os'; import stream from 'stream'; -import { Writer } from '../../src/writer'; +import { writeJs, writeJson, writeYaml } from '../../src/writer'; import { logger } from '../logger'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; import { @@ -17,7 +17,7 @@ const fsPromised = promisify(fs); /** * @module jy-transform:unit-test:test-writer - * @description This unit test suite checks the validity and correctness of {@link Writer} class. + * @description This unit test suite checks the validity and correctness of _./src/js_ module. */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { @@ -38,16 +38,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { */ const WRITER_TEST_BASE_DIR = './test/tmp/writer'; - /** - * The testee. - * @type {Writer} - */ - let writer; - beforeAll(() => { fsExtra.ensureDirSync(WRITER_TEST_BASE_DIR); fsExtra.emptyDirSync(WRITER_TEST_BASE_DIR); - writer = new Writer(logger); }); /** @@ -104,7 +97,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { * @param {Object} [match={name:'Error'}] - The propertie(s) error should contain. * @private */ - const expectWriterError = (json, options, writerFunc, match = { name: 'Error' }) => { + const expectWriteError = (json, options, writerFunc, match = { name: 'Error' }) => { expect.assertions(1); return expect(writerFunc(json, options)).rejects.toMatchObject(match); }; @@ -118,16 +111,16 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { * @param {Error} [match=Error'] - The error type to match. * @private */ - const expectWriterErrorByType = (json, options, writerFunc, match = Error) => { + const expectWriteErrorByType = (json, options, writerFunc, match = Error) => { expect.assertions(1); return expect(writerFunc(json, options)).rejects.toBeInstanceOf(match); }; - describe('testing Writer.writeJs(...)', () => { + describe('Testing writeJs(...)', () => { it('should write JS to file', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.js'; - const msg = await writer.writeJs(JSON_CONTENT, { dest: file }); + const msg = await writeJs(JSON_CONTENT, { src: JSON_CONTENT, dest: file }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -135,7 +128,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JS to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.js'; - const msg = writer.writeJs(JSON_CONTENT, { dest: fs.createWriteStream(file) }); + const msg = writeJs(JSON_CONTENT, { src: JSON_CONTENT, dest: fs.createWriteStream(file) }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -144,11 +137,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { expect.assertions(4); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-exports-identifier.js'; const options = { + src: JSON_CONTENT, target: TYPE_JS, dest: fs.createWriteStream(file), exports: 'test', }; - const msg = writer.writeJs(JSON_CONTENT, options); + const msg = writeJs(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(file); // eslint-disable-next-line import/no-unresolved, global-require @@ -159,28 +153,31 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JS to file and fail by invalid exports identifier (\'#3/-\')', () => { const options = { + src: JSON_CONTENT, dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js', exports: '#3/-', }; - return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'#3/-\')', () => { const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js'; const options = { + src: JSON_CONTENT, dest: fs.createWriteStream(file), - exports: '#3/-' + exports: '#3/-', }; - return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'if\')', () => { const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js'; const options = { + src: JSON_CONTENT, dest: fs.createWriteStream(file), - exports: 'if' + exports: 'if', }; - return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by provoked error', () => { @@ -191,16 +188,17 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { this.emit('error', new Error('Dummy Error')); done(); }; - return expectWriterErrorByType(JSON_CONTENT, { + return expectWriteErrorByType(JSON_CONTENT, { + src: JSON_CONTENT, target: TYPE_JS, - dest: errorThrowingStream - }, writer.writeJs, Error); + dest: errorThrowingStream, + }, writeJs, Error); }); it('should write JS to JS object', async () => { expect.assertions(4); - const options = { dest: {} }; - const msg = await writer.writeJs(JSON_CONTENT, options); + const options = { src: JSON_CONTENT, dest: {} }; + const msg = await writeJs(JSON_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(options.dest, 'test')).toBeTruthy(); @@ -209,20 +207,22 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should reject to write JS to JS object with options.exports == \'\'', async () => { const options = { + src: JSON_CONTENT, dest: {}, - exports: '' + exports: '', }; - return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); }); const exports = 'foo'; it('should write JS to JS object with options.exports == \'' + exports + '\'', async () => { expect.assertions(5); const options = { + src: JSON_CONTENT, dest: {}, - exports + exports, }; - const msg = await writer.writeJs(JSON_CONTENT, options); + const msg = await writeJs(JSON_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(options.dest, exports)).toBeTruthy(); @@ -233,25 +233,27 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { const invalidIdentifier = '#3/-'; it('should reject write JS with Error on invalid identifier for options.exports: ' + invalidIdentifier, () => { const options = { + src: JSON_CONTENT, dest: {}, - exports: invalidIdentifier + exports: invalidIdentifier, }; - return expectWriterError(JSON_CONTENT, options, writer.writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS with Error on missing destination', () => { - return expectWriterError(JSON_CONTENT, {}, writer.writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, { src: JSON_CONTENT }, writeJs, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS to file by invalid file path', (done) => { const options = { + src: JSON_CONTENT, dest: WRITER_TEST_BASE_DIR + '/<>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/test-data-by-js-to-file.js' }; - writer.writeJs(JSON_CONTENT, options) + writeJs(JSON_CONTENT, options) .then((msg) => { done(new Error('Error expected, but got success message: ' + msg)); }) @@ -273,14 +275,14 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }, 10000); // TODO timeout needed? }); - describe('Testing Writer.writeJson(...)', () => { + describe('Testing writeJson(...)', () => { it('should write JSON to file', async () => { expect.assertions(2); const options = { src: JSON_CONTENT, dest: WRITER_TEST_BASE_DIR + '/test-data-by-json-to-file.json' }; - const msg = await writer.writeJson(JSON_CONTENT, options); + const msg = await writeJson(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(options.dest); }); @@ -288,7 +290,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JSON to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-json-stream.json'; - const msg = await writer.writeJson(JSON_CONTENT, { + const msg = await writeJson(JSON_CONTENT, { + src: JSON_CONTENT, target: TYPE_JSON, dest: fs.createWriteStream(file), }); @@ -298,8 +301,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JS to JS object', async () => { expect.assertions(4); - const options = { dest: {} }; - const msg = await writer.writeJson(JSON_CONTENT, options); + const options = { + src: JSON_CONTENT, + dest: {}, + }; + const msg = await writeJson(JSON_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); const result = JSON.parse(options.dest); @@ -308,15 +314,18 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }); it('should reject with Error on missing destination', () => { - return expectWriterError(JSON_CONTENT, {}, writer.writeJson, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, { src: JSON_CONTENT }, writeJson, { name: 'ValidationError', isJoi: true }); }); }); - describe('Testing Writer.writeYaml(...)', () => { + describe('Testing writeYaml(...)', () => { it('should write YAML to file', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.yaml'; - const msg = await writer.writeYaml(JSON_CONTENT, { dest: file }); + const msg = await writeYaml(JSON_CONTENT, { + src: JSON_CONTENT, + dest: file + }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -324,7 +333,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write YAML to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.yaml'; - const msg = await writer.writeYaml(JSON_CONTENT, { + const msg = await writeYaml(JSON_CONTENT, { + src: JSON_CONTENT, target: TYPE_YAML, dest: fs.createWriteStream(file), }); @@ -335,10 +345,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write stringified YAML to JS object', async () => { expect.assertions(3); const options = { + src: JSON_CONTENT, target: TYPE_YAML, dest: {} }; - const msg = await writer.writeYaml(JSON_CONTENT, options); + const msg = await writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); const key = Object.keys(JSON_CONTENT)[0]; @@ -346,20 +357,25 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }); it('should reject with Error by invalid src object', () => { - const invalidYamlJson = () => {}; - return expectWriterError(invalidYamlJson, - { dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file-invalid.yaml' }, - writer.writeYaml, { name: 'YAMLException' }); + const invalidYamlJson = () => { + }; + return expectWriteError(invalidYamlJson, { + src: JSON_CONTENT, + dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file-invalid.yaml' + }, writeYaml, { name: 'YAMLException' }); }); it('should reject with Error on missing destination', () => { - return expectWriterError(JSON_CONTENT, { }, writer.writeYaml, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, { src: JSON_CONTENT }, writeYaml, { name: 'ValidationError', isJoi: true }); }); }); describe('Testing force overwrite file', () => { it('should reject when options.dest is a directory', () => { - return expectWriterErrorByType(JSON_CONTENT, { dest: './test/data' }, writer.writeYaml, Error); + return expectWriteErrorByType(JSON_CONTENT, { + src: JSON_CONTENT, + dest: './test/data' + }, writeYaml, Error); }); it('should write YAML to stream, overwrite on 2nd write, ' + @@ -367,24 +383,26 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { expect.assertions(13); const dest = WRITER_TEST_BASE_DIR + '/test-data-file-overwriting.yaml'; let options = { + src: JSON_CONTENT, indent: 4, dest, }; const asyncFunctions = [ async () => { - const msg = await writer.writeYaml(JSON_CONTENT, options); + const msg = await writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(dest); return 'overwrite test #1 should initially write YAML to file \'' + dest + '\''; }, async () => { options = { + src: JSON_CONTENT, indent: 4, dest, force: true }; - const msg = await writer.writeYaml(JSON_CONTENT, options); + const msg = await writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(dest); await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); @@ -392,11 +410,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }, async () => { options = { + src: JSON_CONTENT, indent: 4, dest, force: false, }; - const msg = await writer.writeYaml(JSON_CONTENT, options); + const msg = await writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); return 'overwrite test #3 shouldn\'t overwrite existing YAML file \'' + dest + @@ -404,11 +423,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }, async () => { options = { + src: JSON_CONTENT, indent: 4, dest, force: true }; - const msg = await writer.writeYaml(JSON_CONTENT, options); + const msg = await writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); @@ -416,10 +436,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }, async () => { options = { + src: JSON_CONTENT, indent: 4, dest, }; - const msg = await writer.writeYaml(JSON_CONTENT, options); + const msg = await writeYaml(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); @@ -450,8 +471,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { // }, Promise.resolve()); // await Promise.all(, (value, index, length) => { - // return value().then(msg => logger.info('testing overwrite #' + (index + 1) + '/' + asyncFunctions.length + ': ' + msg)); - // }).then(); + // return value().then(msg => logger.info('testing overwrite #' + (index + 1) + '/' + asyncFunctions.length + + // ': ' + msg)); }).then(); }, 5000); // we have to set higher timeout here because some travis jobs failed due to 2 sec timeout! }); }); From 47ed125790196aa25c57b8af05701a43d924762b Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Wed, 21 Jun 2017 00:21:08 +0200 Subject: [PATCH 05/58] More tests --- .jestrc.js | 2 ++ src/reader.js | 19 ++++--------------- src/transformer.js | 15 ++++----------- test/unit/test-reader.js | 11 +++++++++++ 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/.jestrc.js b/.jestrc.js index c899b8c..4f3cdbb 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -4,6 +4,8 @@ module.exports = { collectCoverageFrom: [ //'lib/**/*.js', 'src/**/*.js', + '!src/jyt.js', // TODO: maybe later! + '!src/debug-log.js', // TODO: maybe later! 'index.js' ], coverageDirectory: './coverage/', diff --git a/src/reader.js b/src/reader.js index 8cf3972..ebad6ee 100644 --- a/src/reader.js +++ b/src/reader.js @@ -229,10 +229,6 @@ export async function readYaml(options) { logger.error('Unexpected error: ' + err.stack); reject(err); } - }) - .catch((err) => { - self.logger.error('Unexpected error: ' + err.stack); - reject(err); }); } else if (isStream.readable(assertedOptions.src)) { readFromStream(assertedOptions.src, resolve, reject, TYPE_YAML); @@ -255,17 +251,10 @@ export async function readYaml(options) { // // load source from YAML source // return fsPromisified.readFile(src, 'utf8') // .then(function(yaml) { -// logger.debug('YAML documents loaded from ' + src); // TOD: can this be shortened? -> return Promise.resolve(jsYaml.safeLoadAll(yaml)); -// return Promise.resolve().then(function () { -// var jsDocs = []; -// return jsYaml.safeLoadAll(yaml, function (doc) { // TOD this will not work in Promise environment!!! -// self.logger.trace(doc); -// jsDocs.push(doc); -// }); -// }); -// }); -// }; -// } +// logger.debug('YAML documents loaded from ' + src); // TOD: can this be shortened? -> return +// Promise.resolve(jsYaml.safeLoadAll(yaml)); return Promise.resolve().then(function () { var jsDocs = []; return +// jsYaml.safeLoadAll(yaml, function (doc) { // TOD this will not work in Promise environment!!! +// self.logger.trace(doc); jsDocs.push(doc); }); }); }); }; } export default { readJs, diff --git a/src/transformer.js b/src/transformer.js index 261918a..37557b4 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -3,17 +3,14 @@ import { TRANSFORMATIONS } from './constants'; import { ensureMiddleware } from './middleware'; import { readJs, readYaml } from './reader'; import Joi from './validation/joi-extensions'; -//import { LogWrapper } from './log-wrapper'; import { transformerOptionsSchema } from './validation/options-schema'; import { writeJs, writeJson, writeYaml } from './writer'; /** - * This method validates the transformation process described by the given - * options and provides the validate and enriched options and according name - * to resolve a proper function. + * This method creates the transformation described by the given options resolving a name to get the proper function. * * @param {Options} options - The configuration for a transformation. - * @returns {Promise} - A Promise containing the passed `options` object and a 'transformation' string in an array. + * @returns {Promise.} The transformation name. * @example * var OptionsHandler = require('./options-handler.js'); * var logger = ...; @@ -26,12 +23,8 @@ import { writeJs, writeJson, writeYaml } from './writer'; * @see {@link transformations} * @public */ -async function createAndValidateTransformation(options) { +async function createTransformation(options) { const transformation = options.origin + '2' + options.target; - if (TRANSFORMATIONS.indexOf(transformation) < 0) { - throw new Error('Unsupported target type transformation \'' + options.origin + ' -> ' - + options.target + '\' configured in options.'); - } console.log('Transformation: ' + transformation) return transformation; } @@ -380,7 +373,7 @@ const transformations = { export async function transform(options, middleware) { logger.debug('transform'); const ensuredOptions = await Joi.validate(options, transformerOptionsSchema); - const transformation = await createAndValidateTransformation(ensuredOptions); + const transformation = await createTransformation(ensuredOptions); logger.debug('Calling transformation: ' + transformation); return await transformations[transformation](ensuredOptions, middleware); } diff --git a/test/unit/test-reader.js b/test/unit/test-reader.js index 55f2a35..3842a09 100644 --- a/test/unit/test-reader.js +++ b/test/unit/test-reader.js @@ -263,5 +263,16 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { it('should fail YAML read by missing options.src', () => { return expectReaderError({}, readYaml, { name: 'ValidationError', isJoi: true }); }); + + it('should fail read YAML from configured directory source', async () => + await expectReaderError({ src: './test/data' }, readYaml, { name: 'ValidationError', isJoi: true }) + ); + + it('should fail read YAML from file', async () => + await expectReaderError({ src: './test/data/non-existing.yml' }, readYaml, { + name: 'ValidationError', + isJoi: true + }) + ); }); }); From 86f6eb76ae6423b76fa2b9b0b786a26856f89c79 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Wed, 21 Jun 2017 01:13:21 +0200 Subject: [PATCH 06/58] Add more tests and api-doc --- README.md | 218 +++++++++++------- docs/CHANGELOG.md | 16 +- package.json | 2 +- src/middleware.js | 3 +- src/reader.js | 10 + src/type-definitions.js | 2 +- src/validation/joi-extensions-file-helper.js | 4 +- .../joi-extensions-identifier-helper.js | 2 +- src/validation/joi-extensions.js | 2 +- src/validation/options-schema-helper.js | 27 ++- src/validation/options-schema.js | 2 +- src/writer.js | 23 +- 12 files changed, 193 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index 6e6cb45..c54a763 100644 --- a/README.md +++ b/README.md @@ -133,20 +133,40 @@ npm test - [bluebird](https://github.com/petkaantonov/bluebird): Full featured Promises/A+ implementation with exceptionally good performance - [cli](https://github.com/node-js-libs/cli): A tool for rapidly building command line apps +- [cwd](https://github.com/jonschlinkert/cwd): Easily get the CWD (current working directory) of a project based on package.json, optionally starting from a given path. (node.js/javascript util) - [is-stream](https://github.com/sindresorhus/is-stream): Check if something is a Node.js stream +- [joi](https://github.com/hapijs/joi): Object schema validation - [js-yaml](https://github.com/nodeca/js-yaml): YAML 1.2 parser and serializer - [json-stringify-safe](https://github.com/isaacs/json-stringify-safe): Like JSON.stringify, but doesn't blow up on circular refs. - [mkdirp-then](https://github.com/fs-utils/mkdirp-then): mkdirp as promised +- [promisify-es6](https://github.com/manuel-di-iorio/promisify-es6): Promisify callback-style functions to ES6 promises - [serialize-js](https://github.com/RReverser/serialize-js): User-readable object serialization for JavaScript. ## Dev Dependencies +- [babel-cli](https://github.com/babel/babel/tree/master/packages): Babel command line. +- [babel-core](https://github.com/babel/babel/tree/master/packages): Babel compiler core. +- [babel-eslint](https://github.com/babel/babel-eslint): Custom parser for ESLint +- [babel-plugin-transform-builtin-extend](https://github.com/loganfsmyth/babel-plugin-transform-builtin-extend): A plugin for Babel 6 supports extending from builtin types based on static analysis. +- [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. +- [babel-preset-stage-0](https://github.com/babel/babel/tree/master/packages): Babel preset for stage 0 plugins +- [babel-watch](https://github.com/kmagiera/babel-watch): Reload your babel-node app on JS source file changes. And do it *fast*. +- [chalk](https://github.com/chalk/chalk): Terminal string styling done right. Much color. - [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects - [codecov](https://github.com/codecov/codecov-node): Uploading report to Codecov: https://codecov.io +- [combarnea-winston-console-formatter](https://github.com/combarnea/winston-console-formatter): Pretty print console formatter in yaml like style - [coveralls](https://github.com/nickmerwin/node-coveralls): takes json-cov output into stdin and POSTs to coveralls.io - [doctoc](https://github.com/thlorenz/doctoc): Generates TOC for markdown files of local git repo. +- [eslint](https://github.com/eslint/eslint): An AST-based pattern checker for JavaScript. +- [eslint-config-airbnb-base](https://github.com/airbnb/javascript): Airbnb's base JS ESLint config, following our styleguide +- [eslint-plugin-filenames](https://github.com/selaux/eslint-plugin-filenames): Eslint rule for consistent filenames. +- [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import): Import with sanity. +- [eslint-plugin-jest](https://github.com/facebook/jest): Eslint rules for Jest +- [eslint-plugin-jest-async](https://github.com/deadratfink/jy-transform.git): ESLint plugin to detect improper Jest test assertions for asynchronous (Promise-based) actions +- [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc): JSDoc linting rules for ESLint. - [fs-extra](https://github.com/jprichardson/node-fs-extra): fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf. - [istanbul](https://github.com/gotwarlost/istanbul): Yet another JS code coverage tool that computes statement, line, function and branch coverage with module loader hooks to transparently add coverage when running tests. Supports all JS coverage use cases including unit tests, server side functional tests +- [jest](https://github.com/facebook/jest): Delightful JavaScript Testing. - [jsdoc-parse](https://github.com/jsdoc2md/jsdoc-parse): Transforms jsdoc data into something more suitable for use as template input - [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown): Generates markdown API documentation from jsdoc annotated source code - [mocha](https://github.com/mochajs/mocha): simple, flexible, fun test framework @@ -154,6 +174,7 @@ npm test - [object-path](https://github.com/mariocasciaro/object-path): Access deep object properties using a path - [package-json-to-readme](https://github.com/zeke/package-json-to-readme): Generate a README.md from package.json contents - [winston](https://github.com/winstonjs/winston): A multi-transport async logging library for Node.js +- [winston-console-formatter](https://github.com/eugeny-dementev/winston-console-formatter): Pretty print console formatter in yaml like style ## License @@ -334,7 +355,7 @@ The OPTIONS are more formally defined in the following table: | --- | --- | --- | --- | --- | --- | | `-o` | `--origin` | string of: [ _js_ | _json_ | _yaml_ ] | The transformation origin type. | if not given, the type is tried to be inferred from the extension of source path, else it is _yaml_ | no | | `-t` | `--target` | string of: [ _js_ | _json_ | _yaml_ ] | The transformation target type. | if not given, the type is tried to be inferred from the extension of destination path, else it is _js_ | no | -| `-i` | `--indent` | integer
[ 1 - 8 ]
| The code indention used in destination files. | 4 | no | +| `-i` | `--indent` | integer
[ 1 - 8 ]
| The code indention used in destination files. | 2 | no | | `-f` | `--force` | n/a | Force overwriting of existing output files on write phase. When files are not overwritten (which is default), then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | | `-m` | `--imports` | string | Define a 'module.exports[.identifier] = ' identifier (to read from JS _source_ file only, must be a valid JS identifier!) | _undefined_ | no | | `-x` | `--exports` | string | Define a 'module.exports[.identifier] = ' identifier (for usage in JS _destination_ file only, must be a valid JS identifier!) | _undefined_ | no | @@ -367,11 +388,11 @@ Then we can transform it to a JSON content as _foo.json_ file: simply by using this command: ``` -$ jyt.js foo.yaml -t json -i 2 +$ jyt.js foo.yaml -t json -i 4 ``` In this example we have overwritten the standard target type (which is `js`) -and applying an indent of 2 SPACEs instead of the default (4). As default the output +and applying an indent of 4 SPACEs instead of the default (2). As default the output file _foo.json_ is written relative to the input file (by omitting the `dest` option here). @@ -380,7 +401,7 @@ default `js` would have been applied! If the source would have been a `js` type like ``` -$ jyt.js foo.js -t json -i 2 +$ jyt.js foo.js -t json -i 4 ``` then the `js` value for `origin` is automatically inferred from file extension. @@ -389,12 +410,12 @@ Accordingly, this is also true for the `target` option. #### Example: JSON ⇒ JS The command ``` -$ jyt.js foo.json -i 2 +$ jyt.js foo.json -i 4 ``` results in _foo.js_: ```javascript module.exports = { - foo: "bar" + foo: "bar" } ``` @@ -434,11 +455,11 @@ In this this example we have a _foo.js_ file exporting _two_ objects: ```javascript module.exports.foo = { - foo: 'bar' + foo: 'bar' }; module.exports.bar = { - bar: 'foo' + bar: 'foo' }; ``` but you want to convert only `bar` object, then call: @@ -454,14 +475,14 @@ bar: foo ```javascript var fooBar = { - foo: 'bar', - bar: 'foo' + foo: 'bar', + bar: 'foo' }; var options = { - src: fooBar, - dest: {}, - exports: 'bar' + src: fooBar, + dest: {}, + exports: 'bar' }; //...transform @@ -471,8 +492,8 @@ The transformation will result in this in-memory object: ```javascript bar: { - foo: 'bar', - bar: 'foo' + foo: 'bar', + bar: 'foo' } ``` Of course, as sub-node of `options.dest`. @@ -491,7 +512,7 @@ $ jyt.js foo.yaml foobar.js -x foobar This generates the following output in JS file using `foobar` as identifier: ```javascript module.exports.foobar = { - foo: "bar" + foo: "bar" } ``` @@ -537,16 +558,19 @@ specify the origin or target type! Since the usage on CLI is a 2-step process: - 1. Read from source file to JS object ⇒ 2. Write out (maybe to other type) + 1. Read from source file to JS object ⇒ + 2. Write out (maybe to other type) the direct API calls additionally provide the usage of a _middleware_ function where you can alter the input JS object before it is written and therefore, which turns this into a 3-step process: - 1. Read from source ⇒ 2. Alter the JS object ⇒ 3. Write out (maybe to other type) + 1. Read from source ⇒ + 2. Alter the JS object ⇒ + 3. Write out (maybe to other type) For more details about this and all the functions provided by this module please refer to the -[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v2). +[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v3). The `origin` and `target` type inference is also standard for the API level. @@ -556,52 +580,52 @@ The `Transformer` exposes the following function which takes besides an (optiona `middleware` function the necessary `options` for the transformation: ```javascript -function transform(options, middleware) { - //... -} +async function transform(options, middleware) ``` +#### Transformer Options + The `options` object has to follow this key-values table: | Option | Type | Description | Default | Required | | --- | --- | --- | --- | --- | -| origin | string | The origin type. | If not given, the type is tried to be inferred from the extension of source path, else it is _yaml_. | no | -| target | string | The target type. | If not given, the type is tried to be inferred from the extension of destination path, else it is _js_ | no | -| src | string | Readable | object | The source information object: `string` is used as file path, `Readable` stream provides a stringified source and `object` is used as direct JS source. | - | yes | -| dest | string | Writable | object | The destination information object: `string` is used as file path, `Writable` stream writes a stringified source and `object` is used as direct JS object for assignment. | The output file is stored relative to the input file (same base name but with another extension if type differs). If input and output type are the same then the file overwriting is handled depending on the 'force' value! | no | -| indent | number | The indention in files. | 4 | no | -| force | boolean | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | -| imports | string | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | -| exports | string | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | +| `origin` | _String_ | The origin type. | If not given, the type is tried to be inferred from the extension of source path, else it is _yaml_. | no | +| `target` | _String_ | The target type. | If not given, the type is tried to be inferred from the extension of destination path, else it is _js_. | no | +| `src` | [ _String_ | _Stream.Readable_ | _Object_ ] | The source information object: `String` is used as file path, `Stream.Readable` provides a stringified source and `object` is used as direct JS source. | - | yes | +| `dest` | [ _String_ | _Stream.Writable_ | _Object_ ] | The destination information object: `String` is used as file path, `Stream.Writable` writes a stringified source and `object` is used for direct JS object assignment of the (stringified or JS object) source. If a string is set as file path then the output and if input and output file path are the same, then the file overwriting is handled depending on the `force` value! | - | yes | +| `indent` | _Number_ | The indention in files. | 2 | no | +| `force` | _Boolean_ | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | +| `imports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | +| `exports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | -**NOTE:** an invalid indention setting (1 > indent > 8) does not raise an error but a default of 4 SPACEs is applied instead. +**NOTE:** an invalid indention setting (1 > indent > 8) does not raise an error but a default of 2 SPACEs is applied instead. #### Example ```javascript var options = { - origin: 'json', - target: 'yaml', - src: 'foo.json', - dest: './foo/bar.yaml', - indent: 2 + origin: 'json', + target: 'yaml', + src: 'foo.json', + dest: './foo/bar.yaml', + indent: 4 } ``` ### Using Middleware The `middleware` is optional but if provided it must be of type `Function` and -a [Promise](http://bluebirdjs.com/docs/api-reference.html). One of the easiest -ones is the identity function +a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). +One of the easiest ones is the identity function _f(data) → data_ -which could be expressed as -[Promise](http://bluebirdjs.com/docs/api-reference.html) function as follows: +which could be expressed as [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) +function as follows: ```javascript -var identity = function (data) { - return Promise.resolve(data); +const identity = async (data) => { + return data; } ``` @@ -617,28 +641,29 @@ object as input: foo: old bar ``` -Applying this [Promise](http://bluebirdjs.com/docs/api-reference.html) as middleware +Applying this [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) +as middleware ```javascript -var middleware = function (data) { - data.foo = 'new bar'; - return Promise.resolve(data); -} +const middleware = async (data) => { + data.foo = 'new bar'; + return data; +}; -transformer.transform(options, middleware) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); +transform(options, middleware) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); ``` will result in such JSON file: ```json { - "foo": "new bar" + "foo": "new bar" } ``` @@ -654,35 +679,31 @@ given (initially empty) JS object. **NOTE:** each of them has to resolve with the `data` object! - ```javascript -function key1(data) { - objectPath.set(data, 'key1', 'value1'); - return Promise.resolve(data); -} +const key1 = async (data) => { + objectPath.set(data, 'key1', 'value1'); + return data; +}; -function key2(data) { - objectPath.set(data, 'key2', 'value2'); - return Promise.resolve(data); -} +const key2 = async (data) => { + objectPath.set(data, 'key2', 'value2'); + return data; +}; -function key3(data) { +const key3 = async (data) => { objectPath.set(data, 'key3', 'value3'); - return Promise.resolve(data); -} + return data; +}; ``` These can be collected by different aggregation or composition functions of the underlying -Promise framework, e.g. using the [`Promise.all([...])`](http://bluebirdjs.com/docs/api/promise.all.html) +Promise framework, e.g. using the [`Promise.all([...])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) function. This one can collect all three functions above and ensure their proper subsequent execution: - ```javascript -var middleware = function (data) { - return Promise.all([key1(data), key2(data), key3(data)]) - .then(function(result) { - return Promise.resolve(result[result.length - 1]); - }); +const middleware = (data) => { + return Promise.all([key1(data), key2(data), key3(data)]) + .then(result => result[result.length - 1]); }; var logger = new Logger(); @@ -691,13 +712,9 @@ var options = { src: {} }; -return transformer.transform(options, middleware) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); +return transform(options, middleware) + .then(msg => logger.info(msg)) + .catch(err => logger.error(err.stack)); ``` Then the result in the `middleware` function can be retrieved from the returned @@ -708,9 +725,9 @@ From our example above it would be result in this object ```javascript { - key1: 'value1', - key2: 'value2', - key3: 'value3' + key1: 'value1', + key2: 'value2', + key3: 'value3' } ``` @@ -756,7 +773,7 @@ Anyway, there are some fallbacks if a level is not supported: # API Reference For more details on how to use the API, please refer to the -[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v2) +[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v3) wiki which describes the full API and provides more examples. # Contributing @@ -770,6 +787,37 @@ section for more details about conventions. # Changelog +#### v3.0.0 + +- **CLI & API Changes (Backwards Imcompatible!):** + - Removed support for Node.js < v4.0 + - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community + +- **API Changes only (Backwards Imcompatible!):** + - Prototype removal from `Transformer`, `Reader` and `Writer`, turning it to simple exports of functions + - Easier usage by using named imports only for all classes (i.e. also for `Transformer`) + - The formerly exported `middleware` is not public anymore + - The `options.imports/exports` are not allowed to be empty strings anymore (just leave it out) + - The exported constants `YAML`, `JS` and `JSON` (usable for `options.origin/target`) are renamed respectively to `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` + - `options.dest` is required for `Transfomer` and `Writer` on API usage + - Removal of `LogWrapper` prevents from injecting a logger into `Transformer`, `Reader` and `Writer` + - Instead of a message success string the `Transformer.transform` and all `Writer.writeXXX` functions return now + the `dest` result object passed in with `options.dest` because during + the validation process the framework will decouple `dest` from the reference of the `options` by creating a + new options object (in case of `Stream.Writable` and it is the same object as passed in as options.dest but it + matters in case of `Object` where the altered object is returned) + +- Internal Changes & Improvements: + - Removal of _development_ branch + - Usage of [babel](https://babeljs.io/) and therefore most modern language features + - Code base could be shrinked and readabilty improved + - Usage of _native promises_ instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html) + - Tests re-written in [Jest](https://facebook.github.io/jest) (could get rid of [assert](https://github.com/defunctzombie/commonjs-assert), + [mocha](https://mochajs.org/) and [istanbul](https://github.com/gotwarlost/istanbul)) + - Add travis build for Node.js v8.x + - Remove travis build for Node.js < v4.x + - Removal of `OptionsHandler` and `Validator` (replaced validation stuff by [joi](https://github.com/hapijs/joi/tree/v10.5.0)) + #### v2.0.1 - [[#39](https://github.com/deadratfink/jy-transform/issues/39)] Maintenance release diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index cd55faf..eb9a564 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,22 +1,20 @@ #### v3.0.0 -- **CLI & API Changes (Backwards Imcompatible!):** +- **CLI & API Changes (Backwards Incompatible!):** - Removed support for Node.js < v4.0 - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community -- **API Changes only (Backwards Imcompatible!):** +- **API Changes only (Backwards Incompatible!):** - Prototype removal from `Transformer`, `Reader` and `Writer`, turning it to simple exports of functions - Easier usage by using named imports only for all classes (i.e. also for `Transformer`) - The formerly exported `middleware` is not public anymore + - Eased interfaces: + - The formerly exported `Reader.readJs(...)|readYaml(...)` functions are not public anymore and replaced by a more simple to use `read(options)` function + - The formerly exported `Writer.writeJs(...)|writeJson(...)|readYaml(...)` functions are not public anymore and replaced by a more simple to use `write(options)` function - The `options.imports/exports` are not allowed to be empty strings anymore (just leave it out) - The exported constants `YAML`, `JS` and `JSON` (usable for `options.origin/target`) are renamed respectively to `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` - - `options.dest` is required for `Transfomer` and `Writer` on API usage - - Removal of `LogWrapper` prevents from injecting a logger into `Transformer`, `Reader` and `Writer` - - Instead of a message success string the `Transformer.transform` and all `Writer.writeXXX` functions return now - the `dest` result object passed in with `options.dest` because during - the validation process the framework will decouple `dest` from the reference of the `options` by creating a - new options object (in case of `Stream.Writable` and it is the same object as passed in as options.dest but it - matters in case of `Object` where the altered object is returned) + - `options.dest` is required for `Transfomer` and `Writer` on API usage now + - Removal of `LogWrapper` (no more logger injection possible/needed) - Internal Changes & Improvements: - Removal of _development_ branch diff --git a/package.json b/package.json index c543a11..f5f8dd3 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "private": false, "scripts": { "build": "babel src -d lib", - "docs": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", + "readme": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", "pretest": "mkdir -pv test/tmp", "test": "JYT_DEBUG=true JYT_ERROR=true jest --expand --no-cache --coverage --runInBand --config=./.jestrc.js", diff --git a/src/middleware.js b/src/middleware.js index f71d737..29f7fc8 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -1,7 +1,6 @@ /** * @module jy-transform:middleware - * @description The module exporting the {@link external:joi.Extension}s for option validations. - * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension Joi.extend(extension) method}. + * @description The middleware ensuring functions module. * @public */ diff --git a/src/reader.js b/src/reader.js index ebad6ee..6031285 100644 --- a/src/reader.js +++ b/src/reader.js @@ -15,6 +15,16 @@ import { TYPE_JSON, } from './constants'; +/** + * @module jy-transform:reader + * @description This module provides the _read_ functionality for YAML, JS or JSON sources. + * @public + */ + +/** + * Promisified `fs` module. + * @private + */ const fsPromisified = promisify(fs); /** diff --git a/src/type-definitions.js b/src/type-definitions.js index e2debc3..968de91 100644 --- a/src/type-definitions.js +++ b/src/type-definitions.js @@ -1,5 +1,5 @@ /** - * @module type-definitions + * @module jy-transform:type-definitions * @description The type definitions for this module. */ diff --git a/src/validation/joi-extensions-file-helper.js b/src/validation/joi-extensions-file-helper.js index fa8f17c..9079dd6 100644 --- a/src/validation/joi-extensions-file-helper.js +++ b/src/validation/joi-extensions-file-helper.js @@ -3,7 +3,7 @@ import path from 'path'; import { debug } from '../debug-log'; /** - * @module validation:joi-extensions-file-helper + * @module jy-transform:validation:joi-extensions-file-helper * @description An (extended) Joi validation schema helper functions for the module options on FS validation. * @protected */ @@ -16,7 +16,7 @@ import { debug } from '../debug-log'; * @protected */ export function isExistingFile(pathStr) { - debug('>>>>>>>>>>>>>DEBUG ===================================') + //debug('>>>>>>>>>>>>>DEBUG ===================================') //error('>>>>>>>>>>>>>ERROR ===================================' + new Error('JYT Error')) const filePath = path.resolve(pathStr); try { diff --git a/src/validation/joi-extensions-identifier-helper.js b/src/validation/joi-extensions-identifier-helper.js index 71686bc..0b88d84 100644 --- a/src/validation/joi-extensions-identifier-helper.js +++ b/src/validation/joi-extensions-identifier-helper.js @@ -1,5 +1,5 @@ /** - * @module validation:joi-extensions-identifier-helper + * @module jy-transform:validation:joi-extensions-identifier-helper * @description An (extended) Joi validation schema helper function for the module options to validate ES6 identifiers. * @protected */ diff --git a/src/validation/joi-extensions.js b/src/validation/joi-extensions.js index a35e68a..dd75abd 100644 --- a/src/validation/joi-extensions.js +++ b/src/validation/joi-extensions.js @@ -4,7 +4,7 @@ import { isExistingFile } from './joi-extensions-file-helper'; import { isValidEs6Identifier } from './joi-extensions-identifier-helper'; /** - * @module validation:joi-extension + * @module jy-transform:validation:joi-extension * @description The module exporting the {@link external:joi.Extension}s for option validations. * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension Joi.extend(extension) method}. * @public diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-helper.js index e43e2c8..69742db 100644 --- a/src/validation/options-schema-helper.js +++ b/src/validation/options-schema-helper.js @@ -8,11 +8,12 @@ import { } from '../constants'; /** - * @module validation:options-schema-helper - * @description The module options schema used in {@link module:options-validator}. + * @module jy-transform:validation:options-schema-helper + * @description Provides some helper functions used in {@link module:validation:options-schema} to resolve default + * values for origin and target depending on the `options.src` or `options.dest` value. * @type {Object} * @protected - * @see {@link module:validation:options-validator} + * @see {@link module:validation:options-schema} */ /** @@ -43,9 +44,10 @@ const getTypeFromFilePath = (pathStr, origin, defaultValue) => { }; /** + * TODO describe me. * - * @param context - * @returns {string} + * @param {Object} context - TODO describe me. + * @returns {string} The origin type. * @protected */ export const inferOriginDefaultFromStreamReadableFilePath = (context) => { @@ -59,9 +61,10 @@ export const inferOriginDefaultFromStreamReadableFilePath = (context) => { }; /** + * TODO describe me. * - * @param context - * @returns {string} + * @param {Object} context - TODO describe me. + * @returns {string} The origin type. * @protected */ export const inferOriginDefaultFromFilePath = (context) => { @@ -69,9 +72,10 @@ export const inferOriginDefaultFromFilePath = (context) => { }; /** + * TODO describe me. * - * @param context - * @returns {string} + * @param {Object} context - TODO describe me. + * @returns {string} The target type. * @protected */ export const inferTargetDefaultFromStreamWritableFilePath = (context) => { @@ -85,9 +89,10 @@ export const inferTargetDefaultFromStreamWritableFilePath = (context) => { }; /** + * TODO describe me. * - * @param context - * @returns {string} + * @param {Object} context - TODO describe me. + * @returns {string} The target type. * @protected */ export const inferTargetDefaultFromFilePath = (context) => { diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js index d66403e..bbd9af2 100644 --- a/src/validation/options-schema.js +++ b/src/validation/options-schema.js @@ -17,7 +17,7 @@ import { } from '../constants'; /** - * @module validation:options-schema + * @module jy-transform:validation:options-schema * @description The module options schema used in {@link module:options-validator}. * @type {Object} * @protected diff --git a/src/writer.js b/src/writer.js index d893705..599d985 100644 --- a/src/writer.js +++ b/src/writer.js @@ -8,10 +8,25 @@ import os from 'os'; import path from 'path'; import promisify from 'promisify-es6'; import serializeJs from 'serialize-js'; -import { TYPE_JS, TYPE_JSON, TYPE_YAML, UTF8 } from './constants'; +import { + TYPE_JS, + TYPE_JSON, + TYPE_YAML, + UTF8 +} from './constants'; import Joi from './validation/joi-extensions'; import { transformerOptionsSchema } from './validation/options-schema'; +/** + * @module jy-transform:writer + * @description This module provides the _write_ functionality for YAML, JS or JSON targets. + * @public + */ + +/** + * Promisified `fs` module. + * @private + */ const fsPromisified = promisify(fs); // //////////////////////////////////////////////////////////////////////////// @@ -96,9 +111,9 @@ function getConsecutiveDestName(dest) { * @param {Function} resolve - The Promise `resolve` callback. * @param {Function} reject - The Promise `reject` callback. * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. - * @see {@link Constants#TYPE_YAML} - * @see {@link Constants#TYPE_JSON} - * @see {@link Constants#TYPE_JS} + * @see {@link TYPE_YAML} + * @see {@link TYPE_JSON} + * @see {@link TYPE_JS} * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). * @throws {Error} If serialized JSON file could not be written due to any reason. * @private From 1dcc5721feefa00c1701bf7cee4b97e0c345bfa8 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Wed, 21 Jun 2017 02:16:19 +0200 Subject: [PATCH 07/58] Better readme creation --- .eslintrc.js | 57 +- .gitignore | 3 +- .jsdoc.json | 26 + API-PRIVATE.md | 1813 +++++++++++++++++ API-PUBLIC.md | 702 +++++++ MAKE.md | 11 + PACKAGE.md | 73 + README.md | 149 +- package.json | 4 +- readme/API.md | 1371 +++++++++++++ {docs => readme}/BADGES.md | 0 {docs => readme}/CHANGELOG.md | 0 {docs => readme}/CONTRIBUTING.md | 0 docs/USAGE.md => readme/DOCUMENTATION.md | 0 {docs => readme}/LOGO.md | 0 {docs => readme}/TOC.md | 0 src/debug-log.js | 2 +- src/jyt.js | 10 +- src/middleware.js | 2 +- src/transformer.js | 6 + src/type-definitions.js | 1 + src/validation/joi-extensions-file-helper.js | 2 +- .../joi-extensions-identifier-helper.js | 2 +- src/validation/joi-extensions.js | 2 +- src/validation/options-schema-helper.js | 2 +- src/validation/options-schema.js | 2 +- test/helper-constants.js | 4 +- test/logger.js | 9 +- test/unit/test-index.js | 14 +- test/unit/test-middleware.js | 1 + test/unit/test-reader.js | 1 + test/unit/test-transformer.js | 1 + test/unit/test-writer.js | 1 + .../test-joi-extensions-file-helper.js | 1 + .../test-joi-extensions-identifier-helper.js | 1 + .../validation/test-options-schema-helper.js | 1 + test/unit/validation/test-options-schema.js | 3 +- 37 files changed, 4142 insertions(+), 135 deletions(-) create mode 100644 .jsdoc.json create mode 100644 API-PRIVATE.md create mode 100644 API-PUBLIC.md create mode 100644 MAKE.md create mode 100644 PACKAGE.md create mode 100644 readme/API.md rename {docs => readme}/BADGES.md (100%) rename {docs => readme}/CHANGELOG.md (100%) rename {docs => readme}/CONTRIBUTING.md (100%) rename docs/USAGE.md => readme/DOCUMENTATION.md (100%) rename {docs => readme}/LOGO.md (100%) rename {docs => readme}/TOC.md (100%) diff --git a/.eslintrc.js b/.eslintrc.js index 53ff894..3200edc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,37 +5,48 @@ module.exports = { ecmaVersion: 8, }, plugins: [ + 'jsdoc', 'filenames', 'jest', - 'jest-async', - 'jsdoc', + 'jest-async' ], + env: { + 'jest/globals': true + }, rules: { - 'comma-dangle': ['error', { - 'arrays': 'only-multiline', - 'objects': 'only-multiline', - 'imports': 'only-multiline', - 'exports': 'only-multiline', - 'functions': 'ignore', - }], + 'comma-dangle': [ + 'error', { + arrays: 'only-multiline', + objects: 'only-multiline', + imports: 'only-multiline', + exports: 'only-multiline', + functions: 'ignore', + }], 'prefer-template': 'off', 'consistent-return': 'error', 'no-case-declarations': 'error', - 'no-plusplus': ['error', { 'allowForLoopAfterthoughts': true }], + 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], 'arrow-body-style': 'off', 'import/no-commonjs': 'error', 'import/no-amd': 'error', 'import/prefer-default-export': 'off', strict: ['error', 'never'], - 'max-len': ['error', 120, 4], + 'max-len': [ + 'error', 120, 4, { + code: 120, + tabWidth: 4, + ignoreUrls: true, + ignoreRegExpLiterals: true, + }], 'no-param-reassign': ['error', { props: false }], - 'require-jsdoc': ['error', { - require: { - FunctionDeclaration: true, - MethodDefinition: true, - ClassDeclaration: true - } - }], + 'require-jsdoc': [ + 'error', { + require: { + FunctionDeclaration: true, + MethodDefinition: true, + ClassDeclaration: true, + } + }], 'jsdoc/check-param-names': 'error', 'jsdoc/check-tag-names': 'error', 'jsdoc/check-types': 'error', @@ -48,13 +59,5 @@ module.exports = { 'jsdoc/require-returns-description': 'error', 'jsdoc/require-returns-type': 'error', 'filenames/match-regex': ['error', '^[a-z0-9-]+$'], - 'jest/no-disabled-tests': 'warn', - 'jest/no-focused-tests': 'error', - 'jest/no-identical-title': 'error', - 'jest/valid-expect': 'error', - 'jest-async/expect-return': 'error', - }, - env: { - 'jest/globals': true - }, + } }; diff --git a/.gitignore b/.gitignore index 42aa08e..76ec754 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ -/bin -/docs/API.md +/readme/API.md .idea *.iml /VERSION.txt diff --git a/.jsdoc.json b/.jsdoc.json new file mode 100644 index 0000000..6835d20 --- /dev/null +++ b/.jsdoc.json @@ -0,0 +1,26 @@ +{ + "source": { + "include": [ + "src", + "test" + ], + "exclude": [ + "lib", + "node_modules" + ], + "includePattern": ".+\\.js(doc)?$", + "excludePattern": "(^|\\/|\\\\)(_|\\.)" + }, + "opts": { + "encoding": "utf8", + "recurse": true + }, + "plugins": [ + "node_modules/jsdoc-babel" + ], + "babel": { + "plugins": [ + "transform-async-to-generator" + ] + } +} diff --git a/API-PRIVATE.md b/API-PRIVATE.md new file mode 100644 index 0000000..c1449d8 --- /dev/null +++ b/API-PRIVATE.md @@ -0,0 +1,1813 @@ +## Modules + +

+
jy-transform:constants
+

Useful constants used for the module and its usage.

+
+
jy-transform:debug-log
+

The debug logger. Can be enabled via environment variables (set to true):

+
    +
  • JYT_DEBUG: (only DEBUG logging via console.log)
  • +
  • JYT_DEBUG: (only ERROR logging via console.error)
  • +
+
+
jy-transform:jyt
+

The command line interface.

+
+
jy-transform:middleware
+

The middleware ensuring functions module.

+
+
jy-transform:reader
+

This module provides the read functionality for YAML, JS or JSON sources.

+
+
jy-transform:transformer
+

This module provides the transform functionality for YAML, JS or JSON source to destination mapping.

+
+
jy-transform:type-definitions
+

The type definitions for this module.

+
+
jy-transform:validation:joi-extensions-file-helper
+

An (extended) Joi validation schema helper functions for the module options on FS validation.

+
+
jy-transform:validation:joi-extensions-identifier-helper
+

An (extended) Joi validation schema helper function for the module options to validate ES6 identifiers.

+
+
jy-transform:validation:joi-extension
+

The module exporting the Extensions for option validations.

+
+
jy-transform:validation:options-schema-helper : Object
+

Provides some helper functions used in module:validation:options-schema to resolve default +values for origin and target depending on the options.src or options.dest value.

+
+
jy-transform:validation:options-schema : Object
+

The module options schema used in module:options-validator.

+
+
jy-transform:writer
+

This module provides the write functionality for YAML, JS or JSON targets.

+
+
jy-transform:unit:helper-constants : Object
+

The test suite constants definitions.

+
+
jy-transform:unit:logger : Object
+

The test suite logger.

+
+
jy-transform:test-unit:index
+

This unit test module tests the correct exporting from ./index.js.

+
+
jy-transform:unit-test:test-middleware
+

This unit test suite checks the validity and correctness of middleware module.

+
+
jy-transform:unit-test:test-reader
+

This unit test suite checks the validity and correctness of ./src/reader.js module.

+
+
jy-transform:unit-test:test-transformer
+

This unit test suite checks the correct transformation behaviour of Transformer class.

+
+
jy-transform:unit-test:test-writer
+

This unit test suite checks the validity and correctness of ./src/js module.

+
+
jy-transform:test-unit:test-joi-extension-file-helper
+

This unit test module tests validation FS helper method.

+
+
jy-transform:unit-test:test-joi-extensions-identifier-helper
+

This unit test suite checks validity and correctness of ES6 identifiers.

+
+
jy-transform:unit-test:test-options-schema-helper
+

This unit test suite checks the validity and correctness of options schema helper methods.

+
+
jy-transform:unit-test:test-options-schema
+

This unit test suite checks the validity and correctness of options schema.

+
+
+ +## Members + +
+
readJsPromise.<Object>
+

Reads the data from a given JS or JSON source.

+
+
readYamlPromise.<Object>
+

Loads a single YAML source containing document and returns a JS object. +NOTE: this function does not understand multi-document sources, it throws +exception on those.

+
+
writeYamlPromise.<string>
+

Writes a JS object to a YAML destination.

+
+
writeJsonPromise.<string>
+

Writes a JS object to a JSON destination.

+
+
writeJsPromise
+

Writes a JS object to a JS destination. The object is prefixed by module.exports[.${options.exports}] =.

+
+
+ + + +## jy-transform:constants +Useful constants used for the module and its usage. + +**Access:** public + +* [jy-transform:constants](#module_jy-transform_constants) + * [~DEFAULT_OPTIONS](#module_jy-transform_constants..DEFAULT_OPTIONS) : object + * [~UTF8](#module_jy-transform_constants..UTF8) : string + * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string + * [~TYPE_JSON](#module_jy-transform_constants..TYPE_JSON) : string + * [~TYPE_JS](#module_jy-transform_constants..TYPE_JS) : string + * [~TYPES](#module_jy-transform_constants..TYPES) : Array.<string> + * [~TYPE_MAP](#module_jy-transform_constants..TYPE_MAP) : Object + * [~DEFAULT_INDENT](#module_jy-transform_constants..DEFAULT_INDENT) : number + * [~MIN_INDENT](#module_jy-transform_constants..MIN_INDENT) : number + * [~MAX_INDENT](#module_jy-transform_constants..MAX_INDENT) : number + * [~DEFAULT_ORIGIN](#module_jy-transform_constants..DEFAULT_ORIGIN) : string + * [~DEFAULT_TARGET](#module_jy-transform_constants..DEFAULT_TARGET) : string + * [~DEFAULT_FORCE_FILE_OVERWRITE](#module_jy-transform_constants..DEFAULT_FORCE_FILE_OVERWRITE) : boolean + * [~ORIGIN_DESCRIPTION](#module_jy-transform_constants..ORIGIN_DESCRIPTION) : string + * [~TARGET_DESCRIPTION](#module_jy-transform_constants..TARGET_DESCRIPTION) : string + * [~DEST_DESCRIPTION](#module_jy-transform_constants..DEST_DESCRIPTION) : string + * [~DEFAULT_JS_IMPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_IMPORTS_IDENTIFIER) : string + * [~DEFAULT_JS_EXPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_EXPORTS_IDENTIFIER) : string + * [~YAML_TO_JS](#module_jy-transform_constants..YAML_TO_JS) : string + * [~YAML_TO_JSON](#module_jy-transform_constants..YAML_TO_JSON) : string + * [~JS_TO_YAML](#module_jy-transform_constants..JS_TO_YAML) : string + * [~JSON_TO_YAML](#module_jy-transform_constants..JSON_TO_YAML) : string + * [~JSON_TO_JS](#module_jy-transform_constants..JSON_TO_JS) : string + * [~JS_TO_JSON](#module_jy-transform_constants..JS_TO_JSON) : string + * [~YAML_TO_YAML](#module_jy-transform_constants..YAML_TO_YAML) : string + * [~JSON_TO_JSON](#module_jy-transform_constants..JSON_TO_JSON) : string + * [~JS_TO_JS](#module_jy-transform_constants..JS_TO_JS) : string + * [~TRANSFORMATIONS](#module_jy-transform_constants..TRANSFORMATIONS) : Array.<string> + + + +### jy-transform:constants~DEFAULT_OPTIONS : object +The default options. + +**Kind**: inner namespace of [jy-transform:constants](#module_jy-transform_constants) +**See** + +- [ORIGIN_DESCRIPTION](ORIGIN_DESCRIPTION) +- [TARGET_DESCRIPTION](TARGET_DESCRIPTION) +- [DEST_DESCRIPTION](DEST_DESCRIPTION) + +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| origin | string | "yaml" | The default origin type. | +| target | string | "js" | The default target type. | +| dest | string | "relative_to_input_file" | The default dest description. | +| indent | number | 4 | The default indention for files. | +| force | boolean | false | Whether to overwrite existing file on output. | +| imports | string | | The exports name for reading from JS source file or objects only. | +| exports | string | | The exports name for usage in JS file or object only. | + + + +### jy-transform:constants~UTF8 : string +The 'utf8' constant. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPE_YAML : string +The 'yaml' type constant. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPE_JSON : string +The 'json' type constant. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPE_JS : string +The 'js' type constant. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPES : Array.<string> +The type constants assembled in an array: `[ 'yaml', 'json', 'js' ]`. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPE_MAP : Object +A map for extensions to type. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_INDENT : number +The default file indention (4 SPACEs). + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~MIN_INDENT : number +The minimum file indention (0 SPACE). + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~MAX_INDENT : number +The maximum file indention (8 SPACEs). + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_ORIGIN : string +The default `origin` value: 'yaml'. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_TARGET : string +The default `origin` value: 'js'. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_FORCE_FILE_OVERWRITE : boolean +Whether to overwrite existing file or object on output. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~ORIGIN_DESCRIPTION : string +The `origin` description value. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TARGET_DESCRIPTION : string +The `target` description value. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEST_DESCRIPTION : string +The `dest` description value. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_JS_IMPORTS_IDENTIFIER : string +The `src` exports identifier value to read. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public +**Example** +```js +module.exports.foo = {...}; // here 'foo' is the identifier for an object to read from the source! +``` + + +### jy-transform:constants~DEFAULT_JS_EXPORTS_IDENTIFIER : string +The `dest` exports identifier value to write. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~YAML_TO_JS : string +The transformation direction YAML ⇒ JS. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~YAML_TO_JSON : string +The transformation direction YAML ⇒ JSON. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JS_TO_YAML : string +The transformation direction JS ⇒ YAML. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JSON_TO_YAML : string +The transformation direction JSON ⇒ YAML. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JSON_TO_JS : string +The transformation direction JSON ⇒ JS. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JS_TO_JSON : string +The transformation direction JS ⇒ JSON. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~YAML_TO_YAML : string +The transformation direction YAML ⇒ YAML. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JSON_TO_JSON : string +The transformation direction JSON ⇒ JSON. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JS_TO_JS : string +The transformation direction JS ⇒ JS. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TRANSFORMATIONS : Array.<string> +The transformation directions. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +## jy-transform:debug-log ℗ +The debug logger. Can be enabled via environment variables (set to `true`): +- `JYT_DEBUG`: (only DEBUG logging via `console.log`) +- `JYT_DEBUG`: (only ERROR logging via `console.error`) + +**Access:** private + + +### jy-transform:debug-log~debug +DEBUG function. + +**Kind**: inner constant of [jy-transform:debug-log](#module_jy-transform_debug-log) +**Access:** protected + + +## jy-transform:jyt ℗ +The command line interface. + +**Access:** private + +* [jy-transform:jyt](#module_jy-transform_jyt) ℗ + * [~usage](#module_jy-transform_jyt..usage) : string ℗ + * [~packagePath](#module_jy-transform_jyt..packagePath) : string ℗ + * [~options](#module_jy-transform_jyt..options) : Object ℗ + * [~error(err)](#module_jy-transform_jyt..error) ℗ + * [~main(args, cliOptions)](#module_jy-transform_jyt..main) ℗ + + + +### jy-transform:jyt~usage : string ℗ +How to use the CLI. + +**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + + +### jy-transform:jyt~packagePath : string ℗ +The path to package.json. + +**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + + +### jy-transform:jyt~options : Object ℗ +The options description for parsing the command line input, must be an object with opts defined like: +``` +long_tag: [short_tag, description, value_type, default_value]; +``` + +**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + + +### jy-transform:jyt~error(err) ℗ +Prints the error to console and exit with 1. + +**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| err | string | Error | The error to print. | + + + +### jy-transform:jyt~main(args, cliOptions) ℗ +The main entry callback. When calling `cli.main()` this receives the `options` +given on CLI, then does the transformation with these options and finally, it +prints the result to the CLI. + +**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| args | Array | The first mandatory argument is the input file (`args[0]`), the second (optional) argument is the output file (`args[1]`). | +| cliOptions | module:type-definitions~Options | The options provided via CLI. | + + + +## jy-transform:middleware ℗ +The middleware ensuring functions module. + +**Access:** private + +* [jy-transform:middleware](#module_jy-transform_middleware) ℗ + * [~identity(object)](#module_jy-transform_middleware..identity) ⇒ Promise.<object> ℗ + * [~identityMiddleware(object)](#module_jy-transform_middleware..identityMiddleware) ⇒ Promise.<object> + * [~ensureMiddleware(middleware)](#module_jy-transform_middleware..ensureMiddleware) ⇒ function + + + +### jy-transform:middleware~identity(object) ⇒ Promise.<object> ℗ +Promise which reflects the identity of passed JSON: `f(object) → object`. + +**Kind**: inner method of [jy-transform:middleware](#module_jy-transform_middleware) +**Returns**: Promise.<object> - - A Promise resolving the passed JS `object`. +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JS object which is resolved by Promise. | + + + +### jy-transform:middleware~identityMiddleware(object) ⇒ Promise.<object> +Middleware Promise which reflects the identity of passed JS: `f(object) → object`. + +**Kind**: inner method of [jy-transform:middleware](#module_jy-transform_middleware) +**Returns**: Promise.<object> - A Promise resolving the passed JS object. +**Access:** protected + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The object which is returned in Promise. | + +**Example** +```js +import { identityMiddleware } from './lib/middleware'; +transformer.transform(options, identityMiddleware) + .then((object) => { + // ... + }): +``` + + +### jy-transform:middleware~ensureMiddleware(middleware) ⇒ function +Ensure that the given middleware Promise is a function if set. +If not set a new JSON 'identity' Promise is returned which simply passes +a JSON object. + +**Kind**: inner method of [jy-transform:middleware](#module_jy-transform_middleware) +**Returns**: function - - The given middleware Promise or a new JSON 'identity' middleware Promise function. +**Throws**: + +- TypeError - Will throw this error when the passed `middleware` is not type of `Function`. + +**Access:** protected + +| Param | Type | Description | +| --- | --- | --- | +| middleware | function | This middleware Promise can be used to intercept the JSON object for altering he passed JSON, the function signature is `function(object).` **NOTE:** the Promise has to return the processed JSON. | + +**Example** +```js +import { ensureMiddleware } from './lib/middleware'; +const myMiddleware = async (object) => { + //...do something with object + return object; +}; +transformer.transform(options, ensureMiddleware(myMiddleware)) + .then((transformedObject) => { + //... + }): +``` + + +## jy-transform:reader +This module provides the _read_ functionality for YAML, JS or JSON sources. + +**Access:** public + +* [jy-transform:reader](#module_jy-transform_reader) + * [~fsPromisified](#module_jy-transform_reader..fsPromisified) ℗ + * [~createReadableFunction(readable, bufs)](#module_jy-transform_reader..createReadableFunction) ⇒ function ℗ + * [~readFromStream(readable, resolve, reject, origin)](#module_jy-transform_reader..readFromStream) ℗ + + + +### jy-transform:reader~fsPromisified ℗ +Promisified `fs` module. + +**Kind**: inner constant of [jy-transform:reader](#module_jy-transform_reader) +**Access:** private + + +### jy-transform:reader~createReadableFunction(readable, bufs) ⇒ function ℗ +Creates a function to read from the passed source in to the given buffer array. + +**Kind**: inner method of [jy-transform:reader](#module_jy-transform_reader) +**Returns**: function - - The function which reads and buffers. +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| readable | stream.Readable | The source to read from. | +| bufs | Array | The temporary buffer array. | + + + +### jy-transform:reader~readFromStream(readable, resolve, reject, origin) ℗ +Reads from a passed stream and resolves by callback. + +**Kind**: inner method of [jy-transform:reader](#module_jy-transform_reader) +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| readable | Stream.Readable | The source to read from. | +| resolve | function | Callback for success case. | +| reject | function | Callback for Error case. | +| origin | string | Origin type, must be 'yaml' or 'json'/'js'. | + + + +## jy-transform:transformer +This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. + +**Access:** public + +* [jy-transform:transformer](#module_jy-transform_transformer) + * [~createTransformation](#module_jy-transform_transformer..createTransformation) ⇒ Promise.<string> + * [~itmo](#module_jy-transform_transformer..itmo) ℗ + * [~transform](#module_jy-transform_transformer..transform) ⇒ Promise.<String> + * [~transformations](#module_jy-transform_transformer..transformations) : object ℗ + * [~yamlToJson(options, [middleware])](#module_jy-transform_transformer..yamlToJson) ⇒ Promise ℗ + * [~jsToYaml(options, [middleware])](#module_jy-transform_transformer..jsToYaml) ⇒ Promise ℗ + * [~jsonToJs(options, [middleware])](#module_jy-transform_transformer..jsonToJs) ⇒ Promise ℗ + * [~jsToJson(options, [middleware])](#module_jy-transform_transformer..jsToJson) ⇒ Promise ℗ + * [~yamlToYaml(options, [middleware])](#module_jy-transform_transformer..yamlToYaml) ⇒ Promise ℗ + * [~jsonToJson(options, [middleware])](#module_jy-transform_transformer..jsonToJson) ⇒ Promise ℗ + * [~jsToJs(options, [middleware])](#module_jy-transform_transformer..jsToJs) ⇒ Promise.<String> ℗ + + + +### jy-transform:transformer~createTransformation ⇒ Promise.<string> +This method creates the transformation described by the given options resolving a name to get the proper function. + +**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise.<string> - The transformation name. +**Access:** public +**See**: [transformations](transformations) + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.validateTransformation(options) + .spread(function (validatedOptions, transformation) { + ... + )): +``` + + +### jy-transform:transformer~itmo ℗ +Internal delegate function to execute transformation logic (ITMO): +- Input (read) +- Transform + Middleware +- Output (write) + +**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| read | function | The reader function. | +| [middleware] | function | The middleware to apply. | +| write | function | The writer functions. | + +**Example** +```js +var options = {...}; +var middleware = async (obj) => { + ... +}; +const result = await itmo(options, readYaml, middleware, writeJson); +``` + + +### jy-transform:transformer~transform ⇒ Promise.<String> +The entry method for all transformation accepting a configuration object and +an (optional) middleware function. + +**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError Will throw this error when the passed `middleware` is not type of `Function`. +- Error Will throw plain error when writing to file failed due to any reason. + +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(json) ```

**NOTE:** the Promise has to return the processed JSON! | + +**Example** +```js +import { transform } from 'jy-transform'; +const options = {...}; +const middleware = async (json) { + json.myproperty = 'new value'; + return json; +}; + +transform(options, middleware) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +### jy-transform:transformer~transformations : object ℗ +A transformation name to internal function mapping. + +**Kind**: inner namespace of [jy-transform:transformer](#module_jy-transform_transformer) +**Access:** private +**Properties** + +| Name | Type | Description | +| --- | --- | --- | +| yaml2js | function | The transformation function for YAML ⇒ JS. | +| yaml2json | function | The transformation function for YAML ⇒ JSON. | +| yaml2yaml | function | The transformation function for YAML ⇒ YAML. | +| json2yaml | function | The transformation function for JSON ⇒ YAML. | +| json2js | function | The transformation function for JSON ⇒ JS. | +| json2json | function | The transformation function for JSON ⇒ JSON. | +| js2yaml | function | The transformation function for JS ⇒ YAML. | +| js2json | function | The transformation function for JS ⇒ JSON. | +| js2js | function | The transformation function for JS ⇒ JS. | + + + +### jy-transform:transformer~yamlToJson(options, [middleware]) ⇒ Promise ℗ +Convert YAML to JSON. + +**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError - Will throw this error when the passed `middleware` + is not type of `Function`. +- Error - Will throw plain error when writing to JSON destination failed due to any reason. + +**Access:** private +**See**: itmo + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | + +**Example** +```js +var logger = ...; +var options = {...}; +var middleware = function (obj) { + ... +}; +var Transformer = require('jy-transform'); +var transformer = new Transformer(logger); +transformer.yamlToJson(options, middleware); +``` + + +### jy-transform:transformer~jsToYaml(options, [middleware]) ⇒ Promise ℗ +Convert JS to YAML. + +**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError - Will throw this error when the passed `middleware` + is not type of `Function`. +- Error - Will throw plain error when writing to YAML destination failed due to any reason. + +**Access:** private +**See**: itmo + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | + +**Example** +```js +var logger = ...; +var options = {...}; +var middleware = function (obj) { + ... +}; +var Transformer = require('jy-transform'); +var transformer = new Transformer(logger); +transformer.jsToYaml(options, middleware); +``` + + +### jy-transform:transformer~jsonToJs(options, [middleware]) ⇒ Promise ℗ +Convert JSON to JS. + +**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError - Will throw this error when the passed `middleware` + is not type of `Function`. +- Error - Will throw plain error when writing to JS destination failed due to any reason. + +**Access:** private +**See**: itmo + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | + +**Example** +```js +var logger = ...; +var options = {...}; +var middleware = function (obj) { + ... +}; +var Transformer = require('jy-transform'); +var transformer = new Transformer(logger); +transformer.jsonToJs(options, middleware); +``` + + +### jy-transform:transformer~jsToJson(options, [middleware]) ⇒ Promise ℗ +Convert JS to JSON. + +**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError - Will throw this error when the passed `middleware` + is not type of `Function`. +- Error - Will throw plain error when writing to JSON destination failed due to any reason. + +**Access:** private +**See**: itmo + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | + +**Example** +```js +var logger = ...; +var options = {...}; +var middleware = function (obj) { + ... +}; +var Transformer = require('jy-transform'); +var transformer = new Transformer(logger); +transformer.jsToJson(options, middleware); +``` + + +### jy-transform:transformer~yamlToYaml(options, [middleware]) ⇒ Promise ℗ +Convert YAML to YAML. + +**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError - Will throw this error when the passed `middleware` + is not type of `Function`. +- Error - Will throw plain error when writing to YAML destination failed due to any reason. + +**Access:** private +**See**: itmo + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | + +**Example** +```js +var logger = ...; +var options = {...}; +var middleware = function (obj) { + ... +}; +var Transformer = require('jy-transform'); +var transformer = new Transformer(logger); +transformer.yamlToYaml(options, middleware); +``` + + +### jy-transform:transformer~jsonToJson(options, [middleware]) ⇒ Promise ℗ +Convert JSON to JSON. + +**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError - Will throw this error when the passed `middleware` + is not type of `Function`. +- Error - Will throw plain error when writing to JSON destination failed due to any reason. + +**Access:** private +**See**: itmo + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | + +**Example** +```js +var logger = ...; +var options = {...}; +var middleware = function (obj) { + ... +}; +var Transformer = require('jy-transform'); +var transformer = new Transformer(logger); +transformer.jsonToJson(options, middleware); +``` + + +### jy-transform:transformer~jsToJs(options, [middleware]) ⇒ Promise.<String> ℗ +Convert JS to JS. + +**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError Will throw this error when the passed `middleware` is not type of `Function`. +- Error Will throw plain error when writing to JS destination failed due to any reason. + +**Access:** private +**See**: itmo + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | + +**Example** +```js +var options = {...}; +var middleware = async (json) { + ... +}; +jsToJs(options, middleware); +``` + + +## jy-transform:type-definitions ℗ +The type definitions for this module. + +**Access:** private + +* [jy-transform:type-definitions](#module_jy-transform_type-definitions) ℗ + * [~Options](#module_jy-transform_type-definitions..Options) : object + * [~joi](#external_joi) + * [.ValidationError](#external_joi.ValidationError) + * [.Schema](#external_joi.Schema) + * [.Extension](#external_joi.Extension) + * [.validate](#external_joi.validate) : function + + + +### jy-transform:type-definitions~Options : object +The configuration properties provided to the framework functions. + +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**Access:** public +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| origin | string | "yaml" | The origin type. | +| target | string | "js" | The target type. | +| src | string | Stream.Readable | object | | The source (if `string` type is treated as a file path). | +| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | +| indent | number | 2 | The indention in files. | +| imports | string | | The exports name for reading from JS source files or objects only. | +| exports | string | | The exports name for usage in JS destination files only. | +| force | string | false | Force overwriting of existing output files on write phase. | + + + +### jy-transform:type-definitions~joi +Hapi.js Joi. + +**Kind**: inner external of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**See**: [Hapi Joi](https://github.com/hapijs/joi) + +* [~joi](#external_joi) + * [.ValidationError](#external_joi.ValidationError) + * [.Schema](#external_joi.Schema) + * [.Extension](#external_joi.Extension) + * [.validate](#external_joi.validate) : function + + + +#### joi.ValidationError +Joi validation error. + +**Kind**: static typedef of [joi](#external_joi) +**See**: [Joi errors](hhttps://github.com/hapijs/joi/blob/v10.2.0/API.md#errors) + + +#### joi.Schema +The validation schema. Can be a [joi](#external_joi) type object or a plain object +where every key is assigned a [joi](#external_joi) type object. + +**Kind**: static typedef of [joi](#external_joi) +**See**: [Joi API](https://github.com/hapijs/joi/blob/v10.2.2/API.md#joi) + + +#### joi.Extension +Hapi.js Joi schema extension. + +**Kind**: static typedef of [joi](#external_joi) +**See**: [Hapi Joi Extension](https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension) + + +#### joi.validate : function +Joi `validate` method. + +**Kind**: static typedef of [joi](#external_joi) +**See**: [Joi.validate](https://github.com/hapijs/joi/blob/master/API.md#validatevalue-schema-options-callback) + + +## jy-transform:validation:joi-extensions-file-helper ℗ +An (extended) Joi validation schema helper functions for the module options on FS validation. + +**Access:** private + + +### jy-transform:validation:joi-extensions-file-helper~isExistingFile(pathStr) ⇒ boolean +Checks if given `pathStr` is an existing file after resolving `pathStr` relative to CWD. + +**Kind**: inner method of [jy-transform:validation:joi-extensions-file-helper](#module_jy-transform_validation_joi-extensions-file-helper) +**Returns**: boolean - Value `true` if it is a file and exists, else `false`. +**Access:** protected + +| Param | Type | Description | +| --- | --- | --- | +| pathStr | string | The string to check for being a file. | + + + +## jy-transform:validation:joi-extensions-identifier-helper ℗ +An (extended) Joi validation schema helper function for the module options to validate ES6 identifiers. + +**Access:** private + +* [jy-transform:validation:joi-extensions-identifier-helper](#module_jy-transform_validation_joi-extensions-identifier-helper) ℗ + * [~identifierRegExpECMAScript6](#module_jy-transform_validation_joi-extensions-identifier-helper..identifierRegExpECMAScript6) : RegExp ℗ + * [~isValidEs6Identifier(identifier)](#module_jy-transform_validation_joi-extensions-identifier-helper..isValidEs6Identifier) ⇒ boolean + + + +### jy-transform:validation:joi-extensions-identifier-helper~identifierRegExpECMAScript6 : RegExp ℗ +Created at [Generating a regular expression to match valid JavaScript identifiers] +(https://mathiasbynens.be/demo/javascript-identifier-regex). + +**Kind**: inner constant of [jy-transform:validation:joi-extensions-identifier-helper](#module_jy-transform_validation_joi-extensions-identifier-helper) +**Access:** private + + +### jy-transform:validation:joi-extensions-identifier-helper~isValidEs6Identifier(identifier) ⇒ boolean +This method checks if a given `identifier` is a valid ECMAScript 6 identifier. + +**Kind**: inner method of [jy-transform:validation:joi-extensions-identifier-helper](#module_jy-transform_validation_joi-extensions-identifier-helper) +**Returns**: boolean - A `true` if valid, else `false`. +**Access:** protected + +| Param | Type | Description | +| --- | --- | --- | +| identifier | string | The identifier to check. | + +**Example** +```js +import { isValidEs6Identifier } from './validation/joi-extensions-identifier-helper.js'; +const identifier = 'foo'; +console.log('valid = ' + isValidEs6Identifier(identifier)); +``` + + +## jy-transform:validation:joi-extension ℗ +The module exporting the [Extension](#external_joi.Extension)s for option validations. + +**Access:** private +**See**: [Joi.extend(extension) method](https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension). + + +### jy-transform:validation:joi-extension~EXTENSIONS : [Extension](#external_joi.Extension) ℗ +The common [Schema](#external_joi.Schema) validation extensions: +- `existingFile` - needs to be an absolute or relative path to an existing file. +- `validEs6Identifier` - needs to be a valid ECMAScript 6 identifier. + +**Kind**: inner constant of [jy-transform:validation:joi-extension](#module_jy-transform_validation_joi-extension) +**Access:** private + + +## jy-transform:validation:options-schema-helper : Object ℗ +Provides some helper functions used in [module:validation:options-schema](module:validation:options-schema) to resolve default +values for origin and target depending on the `options.src` or `options.dest` value. + +**Access:** private +**See**: [module:validation:options-schema](module:validation:options-schema) + +* [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) : Object ℗ + * [~inferOriginDefaultFromStreamReadableFilePath](#module_jy-transform_validation_options-schema-helper..inferOriginDefaultFromStreamReadableFilePath) ⇒ string + * [~inferOriginDefaultFromFilePath](#module_jy-transform_validation_options-schema-helper..inferOriginDefaultFromFilePath) ⇒ string + * [~inferTargetDefaultFromStreamWritableFilePath](#module_jy-transform_validation_options-schema-helper..inferTargetDefaultFromStreamWritableFilePath) ⇒ string + * [~inferTargetDefaultFromFilePath](#module_jy-transform_validation_options-schema-helper..inferTargetDefaultFromFilePath) ⇒ string + * [~getTypeFromFilePath(pathStr, origin, defaultValue)](#module_jy-transform_validation_options-schema-helper..getTypeFromFilePath) ⇒ string ℗ + + + +### jy-transform:validation:options-schema-helper~inferOriginDefaultFromStreamReadableFilePath ⇒ string +TODO describe me. + +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Returns**: string - The origin type. +**Access:** protected + +| Param | Type | Description | +| --- | --- | --- | +| context | Object | TODO describe me. | + + + +### jy-transform:validation:options-schema-helper~inferOriginDefaultFromFilePath ⇒ string +TODO describe me. + +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Returns**: string - The origin type. +**Access:** protected + +| Param | Type | Description | +| --- | --- | --- | +| context | Object | TODO describe me. | + + + +### jy-transform:validation:options-schema-helper~inferTargetDefaultFromStreamWritableFilePath ⇒ string +TODO describe me. + +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Returns**: string - The target type. +**Access:** protected + +| Param | Type | Description | +| --- | --- | --- | +| context | Object | TODO describe me. | + + + +### jy-transform:validation:options-schema-helper~inferTargetDefaultFromFilePath ⇒ string +TODO describe me. + +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Returns**: string - The target type. +**Access:** protected + +| Param | Type | Description | +| --- | --- | --- | +| context | Object | TODO describe me. | + + + +### jy-transform:validation:options-schema-helper~getTypeFromFilePath(pathStr, origin, defaultValue) ⇒ string ℗ +Infer from path extension to a type using default value as fallback. + +**Kind**: inner method of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Returns**: string - A type value. +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| pathStr | string | The file path with or without extension. | +| origin | boolean | If the type is origin (true) or target (false). | +| defaultValue | string | The default value to use if type cannot be inferred from path. | + + + +## jy-transform:validation:options-schema : Object ℗ +The module options schema used in [module:options-validator](module:options-validator). + +**Access:** private +**See**: [module:options-validator](module:options-validator) + +* [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) : Object ℗ + * [~readerOptionsSchema](#module_jy-transform_validation_options-schema..readerOptionsSchema) : JoiSchema ℗ + * [~writerOptionsSchema](#module_jy-transform_validation_options-schema..writerOptionsSchema) : JoiSchema ℗ + * [~transformerOptionsSchema](#module_jy-transform_validation_options-schema..transformerOptionsSchema) : JoiSchema ℗ + + + +### jy-transform:validation:options-schema~readerOptionsSchema : JoiSchema ℗ +The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [Reader](Reader) options. + +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access:** private + + +### jy-transform:validation:options-schema~writerOptionsSchema : JoiSchema ℗ +The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [Writer](Writer) options. + +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access:** private + + +### jy-transform:validation:options-schema~transformerOptionsSchema : JoiSchema ℗ +The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [Transformer](Transformer) options. + +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access:** private + + +## jy-transform:writer +This module provides the _write_ functionality for YAML, JS or JSON targets. + +**Access:** public + +* [jy-transform:writer](#module_jy-transform_writer) + * [~fsPromisified](#module_jy-transform_writer..fsPromisified) ℗ + * [~createExportsString([exportsTo])](#module_jy-transform_writer..createExportsString) ⇒ Promise.<string> ℗ + * [~serializeJsToString(object, indent, [exportsTo])](#module_jy-transform_writer..serializeJsToString) ⇒ Promise ℗ + * [~serializeJsToJsonString(object, indent)](#module_jy-transform_writer..serializeJsToJsonString) ⇒ string ℗ + * [~getConsecutiveDestName(dest)](#module_jy-transform_writer..getConsecutiveDestName) ⇒ string ℗ + * [~writeToFile(object, dest, target, resolve, reject, [forceOverwrite])](#module_jy-transform_writer..writeToFile) ⇒ Promise.<string> ℗ + * [~mkdirAndWrite()](#module_jy-transform_writer..writeToFile..mkdirAndWrite) ℗ + * [~writeToStream(object, dest, target, resolve, reject)](#module_jy-transform_writer..writeToStream) ⇒ Promise.<string> ℗ + + + +### jy-transform:writer~fsPromisified ℗ +Promisified `fs` module. + +**Kind**: inner constant of [jy-transform:writer](#module_jy-transform_writer) +**Access:** private + + +### jy-transform:writer~createExportsString([exportsTo]) ⇒ Promise.<string> ℗ +Creates a potential named `'module.exports[.exportsTo]'` string. + +**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) +**Returns**: Promise.<string> - Resolves with the exports string. +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| [exportsTo] | string | The export name. | + + + +### jy-transform:writer~serializeJsToString(object, indent, [exportsTo]) ⇒ Promise ℗ +Serialize a JS object to string. + +**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) +**Returns**: Promise - - Promise resolve with the serialized JS object. +**Access:** private +**Todo** + +- [ ] [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> + `'\'use strict\';' + os.EOL + os.EOL + ...`)? + + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JS Object to serialize. | +| indent | number | The indention. | +| [exportsTo] | string | Name for export (*IMPORTANT:* must be a valid ES6 identifier). | + + + +### jy-transform:writer~serializeJsToJsonString(object, indent) ⇒ string ℗ +Serialize a JS object to JSON string. + +**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) +**Returns**: string - The serialized JSON. +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | Object to serialize. | +| indent | number | Indention. | + + + +### jy-transform:writer~getConsecutiveDestName(dest) ⇒ string ℗ +Turns the destination file name into a name containing a consecutive +number if it exists. It iterates over the files until it finds a file +name which does not exist. + +**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) +**Returns**: string - - A consecutive file name or the original one if + `dest` file does not exist. +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| dest | string | The destination file. | + + + +### jy-transform:writer~writeToFile(object, dest, target, resolve, reject, [forceOverwrite]) ⇒ Promise.<string> ℗ +Writes a serialized object to file. + +**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) +**Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). +**Throws**: + +- Error If serialized JSON file could not be written due to any reason. + +**Access:** private +**See** + +- [TYPE_YAML](TYPE_YAML) +- [TYPE_JSON](TYPE_JSON) +- [TYPE_JS](TYPE_JS) + + +| Param | Type | Description | +| --- | --- | --- | +| object | string | The object to write into file. | +| dest | string | The file destination path. | +| target | string | The target type, one of [ 'yaml' | 'json' | 'js' ]. | +| resolve | function | The Promise `resolve` callback. | +| reject | function | The Promise `reject` callback. | +| [forceOverwrite] | boolean | Forces overwriting the destination file if `true`. | + + + +#### writeToFile~mkdirAndWrite() ℗ +Ensures that all dirs exists for `dest` and writes the file. + +**Kind**: inner method of [writeToFile](#module_jy-transform_writer..writeToFile) +**Access:** private + + +### jy-transform:writer~writeToStream(object, dest, target, resolve, reject) ⇒ Promise.<string> ℗ +Writes a string serialized data object to a stream. + +**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) +**Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). +**Throws**: + +- Error If serialized JS object could not be written due to any reason. + +**Access:** private +**See** + +- [Constants#TYPE_YAML](Constants#TYPE_YAML) +- [Constants#TYPE_JSON](Constants#TYPE_JSON) +- [Constants#TYPE_JS](Constants#TYPE_JS) + + +| Param | Type | Description | +| --- | --- | --- | +| object | string | The data to write into stream. | +| dest | string | The stream destination. | +| target | string | The target type, one of [ 'yaml' | 'json' | 'js' ]. | +| resolve | function | The Promise `resolve` callback. | +| reject | function | The Promise `reject` callback. | + + + +## jy-transform:unit:helper-constants : Object ℗ +The test suite constants definitions. + +**Access:** private + +* [jy-transform:unit:helper-constants](#module_jy-transform_unit_helper-constants) : Object ℗ + * [~TEST_SUITE_DESCRIPTION_UNIT](#module_jy-transform_unit_helper-constants..TEST_SUITE_DESCRIPTION_UNIT) : string + * [~TEST_SUITE_DESCRIPTION_FUNCTIONAL](#module_jy-transform_unit_helper-constants..TEST_SUITE_DESCRIPTION_FUNCTIONAL) : string + + + +### jy-transform:unit:helper-constants~TEST_SUITE_DESCRIPTION_UNIT : string +The unit test suite description for the plugin. + +**Kind**: inner constant of [jy-transform:unit:helper-constants](#module_jy-transform_unit_helper-constants) +**Access:** public + + +### jy-transform:unit:helper-constants~TEST_SUITE_DESCRIPTION_FUNCTIONAL : string +The unit test suite description for the plugin. + +**Kind**: inner constant of [jy-transform:unit:helper-constants](#module_jy-transform_unit_helper-constants) +**Access:** public + + +## jy-transform:unit:logger : Object ℗ +The test suite logger. + +**Access:** private + +* [jy-transform:unit:logger](#module_jy-transform_unit_logger) : Object ℗ + * [~INDENT](#module_jy-transform_unit_logger..INDENT) : string ℗ + * [~TEST_TMP_DIR](#module_jy-transform_unit_logger..TEST_TMP_DIR) : string ℗ + * [~winstonFileOptions](#module_jy-transform_unit_logger..winstonFileOptions) : Object ℗ + * [.timestamp()](#module_jy-transform_unit_logger..winstonFileOptions.timestamp) ⇒ string + * [~winstonConsoleOptions](#module_jy-transform_unit_logger..winstonConsoleOptions) : Object ℗ + * [.timestamp()](#module_jy-transform_unit_logger..winstonConsoleOptions.timestamp) ⇒ string + * [~logger](#module_jy-transform_unit_logger..logger) + * [~formatter(options)](#module_jy-transform_unit_logger..formatter) ⇒ string ℗ + + + +### jy-transform:unit:logger~INDENT : string ℗ +An indent of 0 SPACEs. + +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access:** private + + +### jy-transform:unit:logger~TEST_TMP_DIR : string ℗ +A temporary test directory. + +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access:** private + + +### jy-transform:unit:logger~winstonFileOptions : Object ℗ +Options for winston file logging. + +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access:** private + + +#### winstonFileOptions.timestamp() ⇒ string +Formats the timestamp as [Date](Date) ISO string prefixed by an indent. + +**Kind**: static method of [winstonFileOptions](#module_jy-transform_unit_logger..winstonFileOptions) +**Returns**: string - - The [Date](Date) ISO string. +**See**: #INDENT + + +### jy-transform:unit:logger~winstonConsoleOptions : Object ℗ +Options for winston console logging. + +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access:** private + + +#### winstonConsoleOptions.timestamp() ⇒ string +Overwrites the timestamp by indent. + +**Kind**: static method of [winstonConsoleOptions](#module_jy-transform_unit_logger..winstonConsoleOptions) +**Returns**: string - - The indent only. +**See**: #INDENT + + +### jy-transform:unit:logger~logger +The winston logger. + +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access:** protected + + +### jy-transform:unit:logger~formatter(options) ⇒ string ℗ +This function formats the log string by given options to log. + +**Kind**: inner method of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Returns**: string - The log string. +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| options | Object | The formatter options. | + + + +## jy-transform:test-unit:index ℗ +This unit test module tests the correct exporting from _./index.js_. + +**Access:** private + + +## jy-transform:unit-test:test-middleware ℗ +This unit test suite checks the validity and correctness of [middleware](middleware) module. + +**Access:** private + + +## jy-transform:unit-test:test-reader ℗ +This unit test suite checks the validity and correctness of _./src/reader.js_ module. + +**Access:** private + + +## jy-transform:unit-test:test-transformer ℗ +This unit test suite checks the correct transformation behaviour of [Transformer](Transformer) class. + +**Access:** private + + +## jy-transform:unit-test:test-writer ℗ +This unit test suite checks the validity and correctness of _./src/js_ module. + +**Access:** private + + +## jy-transform:test-unit:test-joi-extension-file-helper ℗ +This unit test module tests validation FS helper method. + +**Access:** private + + +## jy-transform:unit-test:test-joi-extensions-identifier-helper ℗ +This unit test suite checks validity and correctness of ES6 identifiers. + +**Access:** private + + +## jy-transform:unit-test:test-options-schema-helper ℗ +This unit test suite checks the validity and correctness of options schema helper methods. + +**Access:** private + + +## jy-transform:unit-test:test-options-schema ℗ +This unit test suite checks the validity and correctness of options schema. + +**Access:** private + + +## readJs ⇒ Promise.<Object> +Reads the data from a given JS or JSON source. + +**Kind**: global variable +**Returns**: Promise.<Object> - Contains the read JS object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | Contains the JS/JSON source reference to read from. | + +**Example** +```js +var Reader = require('jy-transform').Reader; +var logger = ...; +var reader = new Reader(logger); + +// --- from file path + +var options = { + src: 'foo.js' +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// --- from Readable + +options = { + src: fs.createReadStream('foo.js') +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// --- from object + +options = { + src: { + foo: 'bar' + } +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## readYaml ⇒ Promise.<Object> +Loads a single YAML source containing document and returns a JS object. +*NOTE:* this function does not understand multi-document sources, it throws +exception on those. + +**Kind**: global variable +**Returns**: Promise.<Object> - Contains the read JS object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | Contains the YAML source reference to read from. | + +**Example** +```js +var Reader = require('jy-transform').Reader; +var logger = ...; +var reader = new Reader(logger); + +// --- from file path + +options = { + src: 'foo.yml' +}; + +reader.readYaml(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// --- from Readable + +options = { + src: fs.createReadStream('foo.yml') +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## writeYaml ⇒ Promise.<string> +Writes a JS object to a YAML destination. + +**Kind**: global variable +**Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). +**Throws**: + +- Error If YAML destination could not be written due to any reason. + +**Access:** public +**See** + +- [Constants#MIN_INDENT](Constants#MIN_INDENT) +- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) +- [Constants#MAX_INDENT](Constants#MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JS object to write into YAML destination. | +| options | Options | The write destination and indention. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; +var writer = new Writer(logger); + +// ---- write obj to file + +var obj = {...}, +var options = { + dest: 'result.yml', + indent: 2 +} + +writer.writeYaml(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to Writable + +options = { + dest: fs.createWriteStream('result.yml'), + indent: 4 +} + +writer.writeYaml(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## writeJson ⇒ Promise.<string> +Writes a JS object to a JSON destination. + +**Kind**: global variable +**Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). +**Access:** public +**See** + +- [Constants#MIN_INDENT](Constants#MIN_INDENT) +- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) +- [Constants#MAX_INDENT](Constants#MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JS object to write into JSON destination. | +| options | Options | The write destination and indention. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; +var writer = new Writer(logger); + +// ---- write obj to file + +var obj = {...}; +var options = { + dest: 'result.json', + indent: 2 +} + +writer.writeJson(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to Writable + +options = { + dest: fs.createWriteStream('result.json'), + indent: 4 +} + +writer.writeJson(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + +// ---- write obj to object + +options = { + dest: {}, + indent: 4 +} + +writer.writeJson(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## writeJs ⇒ Promise +Writes a JS object to a JS destination. The object is prefixed by `module.exports[.${options.exports}] = `. + +**Kind**: global variable +**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). +**Access:** public +**See** + +- [Constants#MIN_INDENT](Constants#MIN_INDENT) +- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) +- [Constants#MAX_INDENT](Constants#MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JSON to write into JS destination. | +| options | Options | The write destination and indention. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; +var writer = new Writer(logger); + +// ---- write obj to file + +var obj = {...}; +var options = { + dest: 'result.js', + indent: 2 +} + +writer.writeJs(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to Writable + +options = { + dest: fs.createWriteStream('result.json'), + indent: 4 +} + +writer.writeJs(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to object + +options = { + dest: {}, + indent: 2 +} + +writer.writeJs(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` diff --git a/API-PUBLIC.md b/API-PUBLIC.md new file mode 100644 index 0000000..c343992 --- /dev/null +++ b/API-PUBLIC.md @@ -0,0 +1,702 @@ +## Modules + +

+
jy-transform:constants
+

Useful constants used for the module and its usage.

+
+
jy-transform:reader
+

This module provides the read functionality for YAML, JS or JSON sources.

+
+
jy-transform:transformer
+

This module provides the transform functionality for YAML, JS or JSON source to destination mapping.

+
+
jy-transform:writer
+

This module provides the write functionality for YAML, JS or JSON targets.

+
+
+ +## Members + +
+
readJsPromise.<Object>
+

Reads the data from a given JS or JSON source.

+
+
readYamlPromise.<Object>
+

Loads a single YAML source containing document and returns a JS object. +NOTE: this function does not understand multi-document sources, it throws +exception on those.

+
+
writeYamlPromise.<string>
+

Writes a JS object to a YAML destination.

+
+
writeJsonPromise.<string>
+

Writes a JS object to a JSON destination.

+
+
writeJsPromise
+

Writes a JS object to a JS destination. The object is prefixed by module.exports[.${options.exports}] =.

+
+
+ + + +## jy-transform:constants +Useful constants used for the module and its usage. + +**Access:** public + +* [jy-transform:constants](#module_jy-transform_constants) + * [~DEFAULT_OPTIONS](#module_jy-transform_constants..DEFAULT_OPTIONS) : object + * [~UTF8](#module_jy-transform_constants..UTF8) : string + * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string + * [~TYPE_JSON](#module_jy-transform_constants..TYPE_JSON) : string + * [~TYPE_JS](#module_jy-transform_constants..TYPE_JS) : string + * [~TYPES](#module_jy-transform_constants..TYPES) : Array.<string> + * [~TYPE_MAP](#module_jy-transform_constants..TYPE_MAP) : Object + * [~DEFAULT_INDENT](#module_jy-transform_constants..DEFAULT_INDENT) : number + * [~MIN_INDENT](#module_jy-transform_constants..MIN_INDENT) : number + * [~MAX_INDENT](#module_jy-transform_constants..MAX_INDENT) : number + * [~DEFAULT_ORIGIN](#module_jy-transform_constants..DEFAULT_ORIGIN) : string + * [~DEFAULT_TARGET](#module_jy-transform_constants..DEFAULT_TARGET) : string + * [~DEFAULT_FORCE_FILE_OVERWRITE](#module_jy-transform_constants..DEFAULT_FORCE_FILE_OVERWRITE) : boolean + * [~ORIGIN_DESCRIPTION](#module_jy-transform_constants..ORIGIN_DESCRIPTION) : string + * [~TARGET_DESCRIPTION](#module_jy-transform_constants..TARGET_DESCRIPTION) : string + * [~DEST_DESCRIPTION](#module_jy-transform_constants..DEST_DESCRIPTION) : string + * [~DEFAULT_JS_IMPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_IMPORTS_IDENTIFIER) : string + * [~DEFAULT_JS_EXPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_EXPORTS_IDENTIFIER) : string + * [~YAML_TO_JS](#module_jy-transform_constants..YAML_TO_JS) : string + * [~YAML_TO_JSON](#module_jy-transform_constants..YAML_TO_JSON) : string + * [~JS_TO_YAML](#module_jy-transform_constants..JS_TO_YAML) : string + * [~JSON_TO_YAML](#module_jy-transform_constants..JSON_TO_YAML) : string + * [~JSON_TO_JS](#module_jy-transform_constants..JSON_TO_JS) : string + * [~JS_TO_JSON](#module_jy-transform_constants..JS_TO_JSON) : string + * [~YAML_TO_YAML](#module_jy-transform_constants..YAML_TO_YAML) : string + * [~JSON_TO_JSON](#module_jy-transform_constants..JSON_TO_JSON) : string + * [~JS_TO_JS](#module_jy-transform_constants..JS_TO_JS) : string + * [~TRANSFORMATIONS](#module_jy-transform_constants..TRANSFORMATIONS) : Array.<string> + + + +### jy-transform:constants~DEFAULT_OPTIONS : object +The default options. + +**Kind**: inner namespace of [jy-transform:constants](#module_jy-transform_constants) +**See** + +- [ORIGIN_DESCRIPTION](ORIGIN_DESCRIPTION) +- [TARGET_DESCRIPTION](TARGET_DESCRIPTION) +- [DEST_DESCRIPTION](DEST_DESCRIPTION) + +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| origin | string | "yaml" | The default origin type. | +| target | string | "js" | The default target type. | +| dest | string | "relative_to_input_file" | The default dest description. | +| indent | number | 4 | The default indention for files. | +| force | boolean | false | Whether to overwrite existing file on output. | +| imports | string | | The exports name for reading from JS source file or objects only. | +| exports | string | | The exports name for usage in JS file or object only. | + + + +### jy-transform:constants~UTF8 : string +The 'utf8' constant. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPE_YAML : string +The 'yaml' type constant. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPE_JSON : string +The 'json' type constant. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPE_JS : string +The 'js' type constant. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPES : Array.<string> +The type constants assembled in an array: `[ 'yaml', 'json', 'js' ]`. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TYPE_MAP : Object +A map for extensions to type. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_INDENT : number +The default file indention (4 SPACEs). + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~MIN_INDENT : number +The minimum file indention (0 SPACE). + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~MAX_INDENT : number +The maximum file indention (8 SPACEs). + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_ORIGIN : string +The default `origin` value: 'yaml'. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_TARGET : string +The default `origin` value: 'js'. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_FORCE_FILE_OVERWRITE : boolean +Whether to overwrite existing file or object on output. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~ORIGIN_DESCRIPTION : string +The `origin` description value. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TARGET_DESCRIPTION : string +The `target` description value. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEST_DESCRIPTION : string +The `dest` description value. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~DEFAULT_JS_IMPORTS_IDENTIFIER : string +The `src` exports identifier value to read. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public +**Example** +```js +module.exports.foo = {...}; // here 'foo' is the identifier for an object to read from the source! +``` + + +### jy-transform:constants~DEFAULT_JS_EXPORTS_IDENTIFIER : string +The `dest` exports identifier value to write. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~YAML_TO_JS : string +The transformation direction YAML ⇒ JS. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~YAML_TO_JSON : string +The transformation direction YAML ⇒ JSON. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JS_TO_YAML : string +The transformation direction JS ⇒ YAML. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JSON_TO_YAML : string +The transformation direction JSON ⇒ YAML. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JSON_TO_JS : string +The transformation direction JSON ⇒ JS. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JS_TO_JSON : string +The transformation direction JS ⇒ JSON. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~YAML_TO_YAML : string +The transformation direction YAML ⇒ YAML. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JSON_TO_JSON : string +The transformation direction JSON ⇒ JSON. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~JS_TO_JS : string +The transformation direction JS ⇒ JS. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +### jy-transform:constants~TRANSFORMATIONS : Array.<string> +The transformation directions. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access:** public + + +## jy-transform:reader +This module provides the _read_ functionality for YAML, JS or JSON sources. + +**Access:** public + + +## jy-transform:transformer +This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. + +**Access:** public + +* [jy-transform:transformer](#module_jy-transform_transformer) + * [~createTransformation](#module_jy-transform_transformer..createTransformation) ⇒ Promise.<string> + * [~transform](#module_jy-transform_transformer..transform) ⇒ Promise.<String> + + + +### jy-transform:transformer~createTransformation ⇒ Promise.<string> +This method creates the transformation described by the given options resolving a name to get the proper function. + +**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise.<string> - The transformation name. +**Access:** public +**See**: [transformations](transformations) + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.validateTransformation(options) + .spread(function (validatedOptions, transformation) { + ... + )): +``` + + +### jy-transform:transformer~transform ⇒ Promise.<String> +The entry method for all transformation accepting a configuration object and +an (optional) middleware function. + +**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError Will throw this error when the passed `middleware` is not type of `Function`. +- Error Will throw plain error when writing to file failed due to any reason. + +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(json) ```

**NOTE:** the Promise has to return the processed JSON! | + +**Example** +```js +import { transform } from 'jy-transform'; +const options = {...}; +const middleware = async (json) { + json.myproperty = 'new value'; + return json; +}; + +transform(options, middleware) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## jy-transform:writer +This module provides the _write_ functionality for YAML, JS or JSON targets. + +**Access:** public + + +## readJs ⇒ Promise.<Object> +Reads the data from a given JS or JSON source. + +**Kind**: global variable +**Returns**: Promise.<Object> - Contains the read JS object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | Contains the JS/JSON source reference to read from. | + +**Example** +```js +var Reader = require('jy-transform').Reader; +var logger = ...; +var reader = new Reader(logger); + +// --- from file path + +var options = { + src: 'foo.js' +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// --- from Readable + +options = { + src: fs.createReadStream('foo.js') +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// --- from object + +options = { + src: { + foo: 'bar' + } +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## readYaml ⇒ Promise.<Object> +Loads a single YAML source containing document and returns a JS object. +*NOTE:* this function does not understand multi-document sources, it throws +exception on those. + +**Kind**: global variable +**Returns**: Promise.<Object> - Contains the read JS object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | Options | Contains the YAML source reference to read from. | + +**Example** +```js +var Reader = require('jy-transform').Reader; +var logger = ...; +var reader = new Reader(logger); + +// --- from file path + +options = { + src: 'foo.yml' +}; + +reader.readYaml(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// --- from Readable + +options = { + src: fs.createReadStream('foo.yml') +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## writeYaml ⇒ Promise.<string> +Writes a JS object to a YAML destination. + +**Kind**: global variable +**Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). +**Throws**: + +- Error If YAML destination could not be written due to any reason. + +**Access:** public +**See** + +- [Constants#MIN_INDENT](Constants#MIN_INDENT) +- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) +- [Constants#MAX_INDENT](Constants#MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JS object to write into YAML destination. | +| options | Options | The write destination and indention. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; +var writer = new Writer(logger); + +// ---- write obj to file + +var obj = {...}, +var options = { + dest: 'result.yml', + indent: 2 +} + +writer.writeYaml(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to Writable + +options = { + dest: fs.createWriteStream('result.yml'), + indent: 4 +} + +writer.writeYaml(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## writeJson ⇒ Promise.<string> +Writes a JS object to a JSON destination. + +**Kind**: global variable +**Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). +**Access:** public +**See** + +- [Constants#MIN_INDENT](Constants#MIN_INDENT) +- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) +- [Constants#MAX_INDENT](Constants#MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JS object to write into JSON destination. | +| options | Options | The write destination and indention. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; +var writer = new Writer(logger); + +// ---- write obj to file + +var obj = {...}; +var options = { + dest: 'result.json', + indent: 2 +} + +writer.writeJson(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to Writable + +options = { + dest: fs.createWriteStream('result.json'), + indent: 4 +} + +writer.writeJson(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + +// ---- write obj to object + +options = { + dest: {}, + indent: 4 +} + +writer.writeJson(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## writeJs ⇒ Promise +Writes a JS object to a JS destination. The object is prefixed by `module.exports[.${options.exports}] = `. + +**Kind**: global variable +**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). +**Access:** public +**See** + +- [Constants#MIN_INDENT](Constants#MIN_INDENT) +- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) +- [Constants#MAX_INDENT](Constants#MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JSON to write into JS destination. | +| options | Options | The write destination and indention. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; +var writer = new Writer(logger); + +// ---- write obj to file + +var obj = {...}; +var options = { + dest: 'result.js', + indent: 2 +} + +writer.writeJs(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to Writable + +options = { + dest: fs.createWriteStream('result.json'), + indent: 4 +} + +writer.writeJs(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to object + +options = { + dest: {}, + indent: 2 +} + +writer.writeJs(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` diff --git a/MAKE.md b/MAKE.md new file mode 100644 index 0000000..5e11af3 --- /dev/null +++ b/MAKE.md @@ -0,0 +1,11 @@ +Target Call | Description | Dependencies +---|---|--- +`$ make` | This calls the default target `help`. | +`$ make build` | Babel transpiles files from _./src_ to _./lib_. | +`$ make clean` | Removes generated files in folders ./node_modules, ./lib and ./coverage" | +`$ make eslint` | Runs ESLint. | +`$ make help` | Prints the help about targets. | +`$ make install` | Installs all modules | +`$ make publish` | Publishes module to NPM registry. | `test readme` +`$ make readme` | Creates all the documentation parts of the project | +`$ make test` | Runs the test suite and ESLint. | diff --git a/PACKAGE.md b/PACKAGE.md new file mode 100644 index 0000000..7c1134f --- /dev/null +++ b/PACKAGE.md @@ -0,0 +1,73 @@ +# jy-transform + +This project aims to read, write and transform YAML, JS or JSON objects into each other using CLI or API, while the source and destination resources can be files on CLI and additionally, objects or streams on API level. + +## Installation + +Download node at [nodejs.org](http://nodejs.org) and install it, if you haven't already. + +```sh +npm install jy-transform --global +``` + + +## Tests + +```sh +npm install +npm test +``` + +## Dependencies + +- [bluebird](https://github.com/petkaantonov/bluebird): Full featured Promises/A+ implementation with exceptionally good performance +- [cli](https://github.com/node-js-libs/cli): A tool for rapidly building command line apps +- [cwd](https://github.com/jonschlinkert/cwd): Easily get the CWD (current working directory) of a project based on package.json, optionally starting from a given path. (node.js/javascript util) +- [is-stream](https://github.com/sindresorhus/is-stream): Check if something is a Node.js stream +- [joi](https://github.com/hapijs/joi): Object schema validation +- [js-yaml](https://github.com/nodeca/js-yaml): YAML 1.2 parser and serializer +- [json-stringify-safe](https://github.com/isaacs/json-stringify-safe): Like JSON.stringify, but doesn't blow up on circular refs. +- [mkdirp-then](https://github.com/fs-utils/mkdirp-then): mkdirp as promised +- [promisify-es6](https://github.com/manuel-di-iorio/promisify-es6): Promisify callback-style functions to ES6 promises +- [serialize-js](https://github.com/RReverser/serialize-js): User-readable object serialization for JavaScript. + +## Dev Dependencies + +- [babel-cli](https://github.com/babel/babel/tree/master/packages): Babel command line. +- [babel-core](https://github.com/babel/babel/tree/master/packages): Babel compiler core. +- [babel-eslint](https://github.com/babel/babel-eslint): Custom parser for ESLint +- [babel-plugin-transform-builtin-extend](https://github.com/loganfsmyth/babel-plugin-transform-builtin-extend): A plugin for Babel 6 supports extending from builtin types based on static analysis. +- [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. +- [babel-preset-stage-0](https://github.com/babel/babel/tree/master/packages): Babel preset for stage 0 plugins +- [babel-watch](https://github.com/kmagiera/babel-watch): Reload your babel-node app on JS source file changes. And do it *fast*. +- [chalk](https://github.com/chalk/chalk): Terminal string styling done right. Much color. +- [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects +- [codecov](https://github.com/codecov/codecov-node): Uploading report to Codecov: https://codecov.io +- [combarnea-winston-console-formatter](https://github.com/combarnea/winston-console-formatter): Pretty print console formatter in yaml like style +- [coveralls](https://github.com/nickmerwin/node-coveralls): takes json-cov output into stdin and POSTs to coveralls.io +- [doctoc](https://github.com/thlorenz/doctoc): Generates TOC for markdown files of local git repo. +- [eslint](https://github.com/eslint/eslint): An AST-based pattern checker for JavaScript. +- [eslint-config-airbnb-base](https://github.com/airbnb/javascript): Airbnb's base JS ESLint config, following our styleguide +- [eslint-plugin-filenames](https://github.com/selaux/eslint-plugin-filenames): Eslint rule for consistent filenames. +- [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import): Import with sanity. +- [eslint-plugin-jest](https://github.com/facebook/jest): Eslint rules for Jest +- [eslint-plugin-jest-async](https://github.com/deadratfink/jy-transform.git): ESLint plugin to detect improper Jest test assertions for asynchronous (Promise-based) actions +- [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc): JSDoc linting rules for ESLint. +- [fs-extra](https://github.com/jprichardson/node-fs-extra): fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf. +- [istanbul](https://github.com/gotwarlost/istanbul): Yet another JS code coverage tool that computes statement, line, function and branch coverage with module loader hooks to transparently add coverage when running tests. Supports all JS coverage use cases including unit tests, server side functional tests +- [jest](https://github.com/facebook/jest): Delightful JavaScript Testing. +- [jsdoc-babel](https://github.com/ctumolosus/jsdoc-babel): A JSDoc plugin that transforms ES6 source files with Babel before they are processsed. +- [jsdoc-parse](https://github.com/jsdoc2md/jsdoc-parse): Transforms jsdoc data into something more suitable for use as template input +- [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown): Generates markdown API documentation from jsdoc annotated source code +- [mocha](https://github.com/mochajs/mocha): simple, flexible, fun test framework +- [mocha-lcov-reporter](https://github.com/StevenLooman/mocha-lcov-reporter): LCOV reporter for Mocha +- [object-path](https://github.com/mariocasciaro/object-path): Access deep object properties using a path +- [package-json-to-readme](https://github.com/zeke/package-json-to-readme): Generate a README.md from package.json contents +- [winston](https://github.com/winstonjs/winston): A multi-transport async logging library for Node.js +- [winston-console-formatter](https://github.com/eugeny-dementev/winston-console-formatter): Pretty print console formatter in yaml like style + + +## License + +SEE LICENSE IN [LICENSE.md](https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md) + diff --git a/README.md b/README.md index c54a763..f9dc471 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,16 @@ -![jy-transform logo](https://github.com/deadratfink/jy-transform/blob/master/image/jytransform.png) +# jy-transform + +This project aims to read, write and transform YAML, JS or JSON objects into each other using CLI or API, while the source and destination resources can be files on CLI and additionally, objects or streams on API level. + +## Installation + +Download node at [nodejs.org](http://nodejs.org) and install it, if you haven't already. + +```sh +npm install jy-transform --global +``` + + # Stats @@ -86,14 +98,8 @@ -# TOC +## TOC -- [jy-transform](#jy-transform) - - [Installation](#installation) - - [Tests](#tests) - - [Dependencies](#dependencies) - - [Dev Dependencies](#dev-dependencies) - - [License](#license) - [Motivation](#motivation) - [Usage](#usage) - [Usage Types](#usage-types) @@ -105,82 +111,11 @@ - [Using Custom Logger](#using-custom-logger) - [API Reference](#api-reference) - [Contributing](#contributing) -- [Changelog](#changelog) + - [Further information](#further-information) + - [License](#license) -# jy-transform - -This project aims to read, write and transform YAML, JS or JSON objects into each other using CLI or API, while the source and destination resources can be files on CLI and additionally, objects or streams on API level. - -## Installation - -Download node at [nodejs.org](http://nodejs.org) and install it, if you haven't already. - -```sh -npm install jy-transform --global -``` - - -## Tests - -```sh -npm install -npm test -``` - -## Dependencies - -- [bluebird](https://github.com/petkaantonov/bluebird): Full featured Promises/A+ implementation with exceptionally good performance -- [cli](https://github.com/node-js-libs/cli): A tool for rapidly building command line apps -- [cwd](https://github.com/jonschlinkert/cwd): Easily get the CWD (current working directory) of a project based on package.json, optionally starting from a given path. (node.js/javascript util) -- [is-stream](https://github.com/sindresorhus/is-stream): Check if something is a Node.js stream -- [joi](https://github.com/hapijs/joi): Object schema validation -- [js-yaml](https://github.com/nodeca/js-yaml): YAML 1.2 parser and serializer -- [json-stringify-safe](https://github.com/isaacs/json-stringify-safe): Like JSON.stringify, but doesn't blow up on circular refs. -- [mkdirp-then](https://github.com/fs-utils/mkdirp-then): mkdirp as promised -- [promisify-es6](https://github.com/manuel-di-iorio/promisify-es6): Promisify callback-style functions to ES6 promises -- [serialize-js](https://github.com/RReverser/serialize-js): User-readable object serialization for JavaScript. - -## Dev Dependencies - -- [babel-cli](https://github.com/babel/babel/tree/master/packages): Babel command line. -- [babel-core](https://github.com/babel/babel/tree/master/packages): Babel compiler core. -- [babel-eslint](https://github.com/babel/babel-eslint): Custom parser for ESLint -- [babel-plugin-transform-builtin-extend](https://github.com/loganfsmyth/babel-plugin-transform-builtin-extend): A plugin for Babel 6 supports extending from builtin types based on static analysis. -- [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. -- [babel-preset-stage-0](https://github.com/babel/babel/tree/master/packages): Babel preset for stage 0 plugins -- [babel-watch](https://github.com/kmagiera/babel-watch): Reload your babel-node app on JS source file changes. And do it *fast*. -- [chalk](https://github.com/chalk/chalk): Terminal string styling done right. Much color. -- [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects -- [codecov](https://github.com/codecov/codecov-node): Uploading report to Codecov: https://codecov.io -- [combarnea-winston-console-formatter](https://github.com/combarnea/winston-console-formatter): Pretty print console formatter in yaml like style -- [coveralls](https://github.com/nickmerwin/node-coveralls): takes json-cov output into stdin and POSTs to coveralls.io -- [doctoc](https://github.com/thlorenz/doctoc): Generates TOC for markdown files of local git repo. -- [eslint](https://github.com/eslint/eslint): An AST-based pattern checker for JavaScript. -- [eslint-config-airbnb-base](https://github.com/airbnb/javascript): Airbnb's base JS ESLint config, following our styleguide -- [eslint-plugin-filenames](https://github.com/selaux/eslint-plugin-filenames): Eslint rule for consistent filenames. -- [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import): Import with sanity. -- [eslint-plugin-jest](https://github.com/facebook/jest): Eslint rules for Jest -- [eslint-plugin-jest-async](https://github.com/deadratfink/jy-transform.git): ESLint plugin to detect improper Jest test assertions for asynchronous (Promise-based) actions -- [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc): JSDoc linting rules for ESLint. -- [fs-extra](https://github.com/jprichardson/node-fs-extra): fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf. -- [istanbul](https://github.com/gotwarlost/istanbul): Yet another JS code coverage tool that computes statement, line, function and branch coverage with module loader hooks to transparently add coverage when running tests. Supports all JS coverage use cases including unit tests, server side functional tests -- [jest](https://github.com/facebook/jest): Delightful JavaScript Testing. -- [jsdoc-parse](https://github.com/jsdoc2md/jsdoc-parse): Transforms jsdoc data into something more suitable for use as template input -- [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown): Generates markdown API documentation from jsdoc annotated source code -- [mocha](https://github.com/mochajs/mocha): simple, flexible, fun test framework -- [mocha-lcov-reporter](https://github.com/StevenLooman/mocha-lcov-reporter): LCOV reporter for Mocha -- [object-path](https://github.com/mariocasciaro/object-path): Access deep object properties using a path -- [package-json-to-readme](https://github.com/zeke/package-json-to-readme): Generate a README.md from package.json contents -- [winston](https://github.com/winstonjs/winston): A multi-transport async logging library for Node.js -- [winston-console-formatter](https://github.com/eugeny-dementev/winston-console-formatter): Pretty print console formatter in yaml like style - - -## License - -SEE LICENSE IN [LICENSE.md](https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md) - ## Motivation Why this module? After struggling with some huge YAML file and accidentally @@ -785,27 +720,34 @@ See the wiki [Contributing](https://github.com/deadratfink/jy-transform/wiki/Cha section for more details about conventions. -# Changelog + +## Further information + +- [Module Details](./PACKAGE.md) + +- [Public Api Reference](./API-PUBLIC.md) + +- [Private Api Reference](./API-PRIVATE.md) + +- [Makefile Reference](./MAKE.md) #### v3.0.0 -- **CLI & API Changes (Backwards Imcompatible!):** +- **CLI & API Changes (Backwards Incompatible!):** - Removed support for Node.js < v4.0 - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community -- **API Changes only (Backwards Imcompatible!):** +- **API Changes only (Backwards Incompatible!):** - Prototype removal from `Transformer`, `Reader` and `Writer`, turning it to simple exports of functions - Easier usage by using named imports only for all classes (i.e. also for `Transformer`) - The formerly exported `middleware` is not public anymore + - Eased interfaces: + - The formerly exported `Reader.readJs(...)|readYaml(...)` functions are not public anymore and replaced by a more simple to use `read(options)` function + - The formerly exported `Writer.writeJs(...)|writeJson(...)|readYaml(...)` functions are not public anymore and replaced by a more simple to use `write(options)` function - The `options.imports/exports` are not allowed to be empty strings anymore (just leave it out) - The exported constants `YAML`, `JS` and `JSON` (usable for `options.origin/target`) are renamed respectively to `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` - - `options.dest` is required for `Transfomer` and `Writer` on API usage - - Removal of `LogWrapper` prevents from injecting a logger into `Transformer`, `Reader` and `Writer` - - Instead of a message success string the `Transformer.transform` and all `Writer.writeXXX` functions return now - the `dest` result object passed in with `options.dest` because during - the validation process the framework will decouple `dest` from the reference of the `options` by creating a - new options object (in case of `Stream.Writable` and it is the same object as passed in as options.dest but it - matters in case of `Object` where the altered object is returned) + - `options.dest` is required for `Transfomer` and `Writer` on API usage now + - Removal of `LogWrapper` (no more logger injection possible/needed) - Internal Changes & Improvements: - Removal of _development_ branch @@ -861,3 +803,28 @@ Initial public release. This covers the basic implementation and tests. The foll - [[#5](https://github.com/deadratfink/jy-transform/issues/5)] Write unit tests - [[#4](https://github.com/deadratfink/jy-transform/issues/4)] Export variable for JS output - [[#3](https://github.com/deadratfink/jy-transform/issues/3)] Promise array as middleware solved with `Promise.all([...])` + + +## License + +The MIT License (MIT) + +Copyright (c) 2016 [Jens Krefeldt](https://github.com/deadratfink) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/package.json b/package.json index f5f8dd3..ad20b1c 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "private": false, "scripts": { "build": "babel src -d lib", - "readme": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", + "doc": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", + "readme": "bin/create-readme.sh -a true -c true -m true", "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", "pretest": "mkdir -pv test/tmp", "test": "JYT_DEBUG=true JYT_ERROR=true jest --expand --no-cache --coverage --runInBand --config=./.jestrc.js", @@ -66,6 +67,7 @@ "fs-extra": "^1.0.0", "istanbul": "^0.4.5", "jest": "~20.0.4", + "jsdoc-babel": "^0.3.0", "jsdoc-parse": "^2.0.5", "jsdoc-to-markdown": "^2.0.1", "mocha": "^3.1.2", diff --git a/readme/API.md b/readme/API.md new file mode 100644 index 0000000..fa33aca --- /dev/null +++ b/readme/API.md @@ -0,0 +1,1371 @@ + + +### TOC + +- [Classes](#classes) +- [Typedefs](#typedefs) +- [Constants](#constants) +- [LogWrapper](#logwrapper) +- [Middleware](#middleware) +- [OptionsHandler](#optionshandler) +- [Reader](#reader) +- [Transformer](#transformer) +- [Validator](#validator) +- [Writer](#writer) +- [Options : object](#options--codeobjectcode) + + + +## Classes + +

+
Constants
+

Class which defines all constants usable in or with this module.

+
+
LogWrapper
+

Class which defines a logger wrapper usable in this module. +

+ NOTE: This class is not to be intended to be called from + outside this module!

+
+
Middleware
+

Class which defines middleware Promises usable in or with this module.

+
+
OptionsHandler
+

Class which defines some useful methods to initialize and prepare the + transformation options used in this module. +

+ NOTE: This class is not to be intended to be called from + outside this module!

+
+
Reader
+

This class provides utility methods usable to read YAML, JSON or JS + from a source (file, {object} or stream.Readable) to JS memory objects.

+
+
Transformer
+

This class provides all methods usable to handle YAML, JSON and JS and + their transformations.

+
+
Validator
+

This class validates JS identifier. +

+ NOTE: this class is intended for internal use only!

+
+
Writer
+

This class provides utility methods usable to write JS objects + from memory to a JSON/JS/YAML destination + (file, object or stream.Readable).

+
+
+ +## Typedefs + +
+
Options : object
+
+
+ + + +## Constants +Class which defines all constants usable in or with this module. + +**Kind**: global class + +* [Constants](#Constants) + * [new Constants()](#new_Constants_new) + * [.DEFAULT_OPTIONS](#Constants+DEFAULT_OPTIONS) : object + * [.UTF8](#Constants+UTF8) : string + * [.YAML](#Constants+YAML) : string + * [.JSON](#Constants+JSON) : string + * [.JS](#Constants+JS) : string + * [.TYPES](#Constants+TYPES) : Array.<string> + * [.DEFAULT_INDENT](#Constants+DEFAULT_INDENT) : number + * [.MIN_INDENT](#Constants+MIN_INDENT) : number + * [.MAX_INDENT](#Constants+MAX_INDENT) : number + * [.DEFAULT_ORIGIN](#Constants+DEFAULT_ORIGIN) : string + * [.DEFAULT_TARGET](#Constants+DEFAULT_TARGET) : string + * [.DEFAULT_FORCE_FILE_OVERWRITE](#Constants+DEFAULT_FORCE_FILE_OVERWRITE) : boolean + * [.ORIGIN_DESCRIPTION](#Constants+ORIGIN_DESCRIPTION) : string + * [.TARGET_DESCRIPTION](#Constants+TARGET_DESCRIPTION) : string + * [.DEST_DESCRIPTION](#Constants+DEST_DESCRIPTION) : string + * [.DEFAULT_JS_IMPORTS_IDENTIFIER](#Constants+DEFAULT_JS_IMPORTS_IDENTIFIER) : string + * [.DEFAULT_JS_EXPORTS_IDENTIFIER](#Constants+DEFAULT_JS_EXPORTS_IDENTIFIER) : string + * [.YAML_TO_JS](#Constants+YAML_TO_JS) : string + * [.YAML_TO_JSON](#Constants+YAML_TO_JSON) : string + * [.JS_TO_YAML](#Constants+JS_TO_YAML) : string + * [.JSON_TO_YAML](#Constants+JSON_TO_YAML) : string + * [.JSON_TO_JS](#Constants+JSON_TO_JS) : string + * [.JS_TO_JSON](#Constants+JS_TO_JSON) : string + * [.YAML_TO_YAML](#Constants+YAML_TO_YAML) : string + * [.JSON_TO_JSON](#Constants+JSON_TO_JSON) : string + * [.JS_TO_JS](#Constants+JS_TO_JS) : string + * [.TRANSFORMATIONS](#Constants+TRANSFORMATIONS) : Array.<string> + + + +### new Constants() +Constructs the constants. + +**Returns**: [Constants](#Constants) - - The instance. + + +### constants.DEFAULT_OPTIONS : object +The default options. + +**Kind**: instance namespace of [Constants](#Constants) +**See** + +- [ORIGIN_DESCRIPTION](ORIGIN_DESCRIPTION) +- [TARGET_DESCRIPTION](TARGET_DESCRIPTION) +- [DEST_DESCRIPTION](DEST_DESCRIPTION) + +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| origin | string | "yaml" | The default origin type. | +| target | string | "js" | The default target type. | +| dest | string | "relative_to_input_file" | The default dest description. | +| indent | number | 4 | The default indention for files. | +| force | boolean | false | Whether to overwrite existing file on output. | +| imports | string | | The exports name for reading from JS source file or objects only. | +| exports | string | | The exports name for usage in JS file or object only. | + + + +### constants.UTF8 : string +The 'utf8' constant. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.YAML : string +The 'yaml' type constant. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.JSON : string +The 'json' type constant. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.JS : string +The 'js' type constant. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.TYPES : Array.<string> +The type constants assembled in an array: `[ 'yaml', 'json', 'js' ]`. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.DEFAULT_INDENT : number +The default file indention (4 SPACEs). + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.MIN_INDENT : number +The minimum file indention (0 SPACE). + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.MAX_INDENT : number +The maximum file indention (8 SPACEs). + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.DEFAULT_ORIGIN : string +The default `origin` value: 'yaml'. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.DEFAULT_TARGET : string +The default `origin` value: 'js'. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.DEFAULT_FORCE_FILE_OVERWRITE : boolean +Whether to overwrite existing file or object on output. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.ORIGIN_DESCRIPTION : string +The `origin` description value. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.TARGET_DESCRIPTION : string +The `target` description value. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.DEST_DESCRIPTION : string +The `dest` description value. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.DEFAULT_JS_IMPORTS_IDENTIFIER : string +The `src` exports identifier value to read. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public +**Example** +```js +module.exports.foo = {...}; // here 'foo' is the identifier for an object to read from the source! +``` + + +### constants.DEFAULT_JS_EXPORTS_IDENTIFIER : string +The `dest` exports identifier value to write. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.YAML_TO_JS : string +The transformation direction YAML ⇒ JS. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.YAML_TO_JSON : string +The transformation direction YAML ⇒ JSON. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.JS_TO_YAML : string +The transformation direction JS ⇒ YAML. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.JSON_TO_YAML : string +The transformation direction JSON ⇒ YAML. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.JSON_TO_JS : string +The transformation direction JSON ⇒ JS. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.JS_TO_JSON : string +The transformation direction JS ⇒ JSON. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.YAML_TO_YAML : string +The transformation direction YAML ⇒ YAML. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.JSON_TO_JSON : string +The transformation direction JSON ⇒ JSON. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.JS_TO_JS : string +The transformation direction JS ⇒ JS. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +### constants.TRANSFORMATIONS : Array.<string> +The transformation directions. + +**Kind**: instance constant of [Constants](#Constants) +**Access:** public + + +## LogWrapper +Class which defines a `logger` wrapper usable in this module. +

+ **NOTE:** This class is not to be intended to be called from + outside this module! + +**Kind**: global class + +* [LogWrapper](#LogWrapper) + * [new LogWrapper([logger])](#new_LogWrapper_new) + * [.info(msg)](#LogWrapper+info) + * [.debug(msg)](#LogWrapper+debug) + * [.trace(msg)](#LogWrapper+trace) + * [.error(msg)](#LogWrapper+error) + * [.verboseOptions(options)](#LogWrapper+verboseOptions) ⇒ + + + +### new LogWrapper([logger]) +Constructs the `LogWrapper`. + +**Returns**: [LogWrapper](#LogWrapper) - - The instance. + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| [logger] | logger | cli | console | console | Logger object. | + +**Example** +```js +var logger = ...; +var logWrapper = new LogWrapper(logger); +``` + + +### logWrapper.info(msg) +Log the options with INFO level. + +**Kind**: instance method of [LogWrapper](#LogWrapper) +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| msg | string | The message to log. | + +**Example** +```js +var logger = ...; +var logWrapper = new LogWrapper(logger); +var msg = '...'; +logWrapper.info(msg); +``` + + +### logWrapper.debug(msg) +Log the options with DEBUG level (if logger supports it, else with INFO). + +**Kind**: instance method of [LogWrapper](#LogWrapper) +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| msg | string | The message to log. | + +**Example** +```js +var logger = ...; +var logWrapper = new LogWrapper(logger); +var msg = '...'; +logWrapper.debug(msg); +``` + + +### logWrapper.trace(msg) +Log the options with TRACE level (if logger supports it, else with DEBUG). + +**Kind**: instance method of [LogWrapper](#LogWrapper) +**Access:** public +**See**: [#debug](#debug) + +| Param | Type | Description | +| --- | --- | --- | +| msg | string | The message to log. | + +**Example** +```js +var logger = ...; +var logWrapper = new LogWrapper(logger); +var msg = '...'; +logWrapper.trace(msg); +``` + + +### logWrapper.error(msg) +Log the options with ERROR level. + +**Kind**: instance method of [LogWrapper](#LogWrapper) +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| msg | string | The message to log. | + +**Example** +```js +var logger = ...; +var logWrapper = new LogWrapper(logger); +var msg = '...'; +logWrapper.error(msg); +``` + + +### logWrapper.verboseOptions(options) ⇒ +Log the options with INFO level. + +**Kind**: instance method of [LogWrapper](#LogWrapper) +**Returns**: A Promise containing the passed `options` object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The properties to log with INFO. | + +**Example** +```js +var logger = ...; +var logWrapper = new LogWrapper(logger); +var options = { + ... +}; +logWrapper.verboseOptions(options) + .then(function (options) { + ... + }); +``` + + +## Middleware +Class which defines middleware Promises usable in or with this module. + +**Kind**: global class + +* [Middleware](#Middleware) + * [new Middleware()](#new_Middleware_new) + * [.identityMiddleware](#Middleware+identityMiddleware) ⇒ Promise.<object> + * [.ensureMiddleware(middleware)](#Middleware+ensureMiddleware) ⇒ Promise + + + +### new Middleware() +Constructs the `Middleware`. + +**Returns**: [Middleware](#Middleware) - - The instance. +**Example** +```js +var middleware = require('./lib/middleware.js'); +``` + + +### middleware.identityMiddleware ⇒ Promise.<object> +Middleware Promise which reflects the identity of passed JSON: `f(object) → object`. + +**Kind**: instance property of [Middleware](#Middleware) +**Returns**: Promise.<object> - - A Promise resolving the passed `json` object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| object | object | The object which is returned in Promise. | + +**Example** +```js +var middleware = require('./lib/middleware.js'); +var identityMiddleware = middleware.identityMiddleware; +transformer.transform(options, identityMiddleware) + .then(function(object) { + ... + }): +``` + + +### middleware.ensureMiddleware(middleware) ⇒ Promise +Ensure that the given middleware Promise is a function if set. +If not set a new JSON 'identity' Promise is returned which simply passes +a JSON object. + +**Kind**: instance method of [Middleware](#Middleware) +**Returns**: Promise - - The given middleware Promise or a new JSON 'identity' middleware Promise. +**Throws**: + +- TypeError - Will throw this error when the passed `middleware` + is not type of `Function`. + +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| middleware | function | This middleware Promise can be used to intercept the JSON object for altering he passed JSON, the function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JSON! | + +**Example** +```js +var middleware = require('./lib/middleware.js'); +var myMiddleware = function(object) { + //... +}; +transformer.transform(options, middleware.ensureMiddleware(myMiddleware)) + .then(function(object) { + //... + }): +``` + + +## OptionsHandler +Class which defines some useful methods to initialize and prepare the + transformation options used in this module. +

+ **NOTE:** This class is not to be intended to be called from + outside this module! + +**Kind**: global class + +* [OptionsHandler](#OptionsHandler) + * [new OptionsHandler([logger])](#new_OptionsHandler_new) + * [.assertOptions](#OptionsHandler+assertOptions) ⇒ Promise + * [.completeOptions(options)](#OptionsHandler+completeOptions) ⇒ Promise + * [.ensureSrc(options)](#OptionsHandler+ensureSrc) ⇒ Promise + * [.ensureDest(options)](#OptionsHandler+ensureDest) ⇒ Promise + * [.assertOrigin(options)](#OptionsHandler+assertOrigin) ⇒ Promise + * [.assertTarget(options)](#OptionsHandler+assertTarget) ⇒ Promise + * [.ensureIndent(options)](#OptionsHandler+ensureIndent) ⇒ Promise + * [.ensureOptions(options)](#OptionsHandler+ensureOptions) ⇒ Promise + * [.validateTransformation(options)](#OptionsHandler+validateTransformation) ⇒ Promise + + + +### new OptionsHandler([logger]) +Constructs the `OptionsHandler` with an (optional) logger. + +**Returns**: [OptionsHandler](#OptionsHandler) - The instance. + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| [logger] | logger | cli | console | console | Logger instance. | + +**Example** +```js +var OptionsHandler = require('./options-handler'); +var logger = ...; + +var optionsHandler = new OptionsHandler(logger); +``` + + +### optionsHandler.assertOptions ⇒ Promise +Asserts that the given `options` and (optionally) the given properties are +inside the options. If not, the Promise rejects with proper error message. + +**Kind**: instance property of [OptionsHandler](#OptionsHandler) +**Returns**: Promise - - Promise which contains the `options` as result. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | object | The objects which should be set. | +| [properties] | Array.<string> | Properties which should exist in `options`. | + +**Example** +```js +var options = {...}; + +assertOptions(options, ['src', 'origin']) + .then(function (assertedOptions) { + ... + }); +``` + + +### optionsHandler.completeOptions(options) ⇒ Promise +Completes the given `options` object by enriching from default values or using +type inference if something required is "missing" (a missing `options.src` cannot +be completed becaue this is mandatory). + +**Kind**: instance method of [OptionsHandler](#OptionsHandler) +**Returns**: Promise - - A Promise containing the passed `options` object. +**Throws**: + +- Error - If `options` or `options.src` not passed. + +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var options = {...}; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.completeOptions(options) + .then(function (copiedOptions) { + ... + }); +``` + + +### optionsHandler.ensureSrc(options) ⇒ Promise +Ensures that the given input source is valid. + +**Kind**: instance method of [OptionsHandler](#OptionsHandler) +**Returns**: Promise - - A Promise containing the passed `options` object. +**Throws**: + +- Error - If the `options.src` is not defined or the file represented by `options.src` does not exist. + +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var options = {...}; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.ensureSrc(options) + .then(function (ensuredOptions) { + ... + }); +``` + + +### optionsHandler.ensureDest(options) ⇒ Promise +This method ensures that destination file path is created if not set in +options. If not, then it creates the path relative to the source file using +its name and appending a proper extension depending on the `json` +property of `options` (if `true` then '.js', else '.json'). + +**Kind**: instance method of [OptionsHandler](#OptionsHandler) +**Returns**: Promise - - A Promise containing the passed `options` object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var options = {...}; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.ensureDest(options) + .then(function (ensuredOptions) { + ... + }); +``` + + +### optionsHandler.assertOrigin(options) ⇒ Promise +Checks if the given origin is valid. + +**Kind**: instance method of [OptionsHandler](#OptionsHandler) +**Returns**: Promise - - A Promise containing the passed `options` object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var options = {...}; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.assertOrigin(options) + .then(function (ensuredOptions) { + ... + }); +``` + + +### optionsHandler.assertTarget(options) ⇒ Promise +Checks if the given target is valid. + +**Kind**: instance method of [OptionsHandler](#OptionsHandler) +**Returns**: Promise - - A Promise containing the passed `options` object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var options = {...}; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.assertTarget(options) + .then(function (ensuredOptions) { + ... + }); +``` + + +### optionsHandler.ensureIndent(options) ⇒ Promise +Checks if a valid indention value is given and corrects values if invalid (with default value: 4 SPACEs). + +**Kind**: instance method of [OptionsHandler](#OptionsHandler) +**Returns**: Promise - - A Promise containing the passed `options` object. +**Access:** public +**See** + +- [MIN_INDENT](#Constants+MIN_INDENT) +- [DEFAULT_INDENT](#Constants+DEFAULT_INDENT) +- [MAX_INDENT](#Constants+MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var options = {...}; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.ensureIndent(options) + .then(function (ensuredOptions) { + ... + }); +``` + + +### optionsHandler.ensureOptions(options) ⇒ Promise +This method ensures that the options object is set with all necessary and +correct values. The method does not alter the given object, but creates +and fills a new instance from the given values and/or default ones. + +**Kind**: instance method of [OptionsHandler](#OptionsHandler) +**Returns**: Promise - - A Promise containing a new and complete `options` object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var options = {...}; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.ensureOptions(options) + .then(function (ensuredOptions) { + ... + }); +``` + + +### optionsHandler.validateTransformation(options) ⇒ Promise +This method validates the transformation process described by the given +options and provides the validate and enriched options and according name +to resolve a proper function. + +**Kind**: instance method of [OptionsHandler](#OptionsHandler) +**Returns**: Promise - - A Promise containing the passed `options` object and a 'transformation' string in an array. +**Access:** public +**See**: [transformations](transformations) + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The configuration for a transformation. | + +**Example** +```js +var OptionsHandler = require('./options-handler.js'); +var logger = ...; +var optionsHandler = new OptionsHandler(logger); + +optionsHandler.validateTransformation(options) + .spread(function (validatedOptions, transformation) { + ... + )): +``` + + +## Reader +This class provides utility methods usable to read YAML, JSON or JS + from a source (file, {object} or [stream.Readable](stream.Readable)) to JS memory objects. + +**Kind**: global class + +* [Reader](#Reader) + * [new Reader([logger])](#new_Reader_new) + * [.validator](#Reader+validator) : [Validator](#Validator) + * [.readJs(options)](#Reader+readJs) ⇒ Promise + * [.readYaml(options)](#Reader+readYaml) ⇒ Promise + + + +### new Reader([logger]) +Constructs the `Reader` with an (optional) logger. + +**Returns**: [Reader](#Reader) - The instance. + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| [logger] | logger | cli | console | console | Logger instance. | + +**Example** +```js +var Reader = require('jy-transform').Reader; +var logger = ...; + +var reader = new Reader(logger); +``` + + +### reader.validator : [Validator](#Validator) +The validator. + +**Kind**: instance property of [Reader](#Reader) + + +### reader.readJs(options) ⇒ Promise +Reads the data from a given JS or JSON source. + +**Kind**: instance method of [Reader](#Reader) +**Returns**: Promise - - Contains the read JS object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | Contains the JS/JSON source reference to read from. | + +**Example** +```js +var Reader = require('jy-transform').Reader; +var logger = ...; +var reader = new Reader(logger); + +// --- from file path + +var options = { + src: 'foo.js' +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// --- from Readable + +options = { + src: fs.createReadStream('foo.js') +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// --- from object + +options = { + src: { + foo: 'bar' + } +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +### reader.readYaml(options) ⇒ Promise +Loads a single YAML source containing document and returns a JS object. + +*NOTE:* This function does not understand multi-document sources, it throws +exception on those. + +**Kind**: instance method of [Reader](#Reader) +**Returns**: Promise - - Contains the read JS object. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | Contains the YAML source reference to read from. | + +**Example** +```js +var Reader = require('jy-transform').Reader; +var logger = ...; +var reader = new Reader(logger); + +// --- from file path + +options = { + src: 'foo.yml' +}; + +reader.readYaml(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// --- from Readable + +options = { + src: fs.createReadStream('foo.yml') +}; + +reader.readJs(options) + .then(function (obj){ + logger.info(JSON.stringify(obj)); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## Transformer +This class provides all methods usable to handle YAML, JSON and JS and + their transformations. + +**Kind**: global class + +* [Transformer](#Transformer) + * [new Transformer([logger])](#new_Transformer_new) + * _instance_ + * [.transform(options, [middleware])](#Transformer+transform) ⇒ Promise + * _inner_ + * [~ensureMiddleware](#Transformer..ensureMiddleware) + + + +### new Transformer([logger]) +Constructs the `Transformer` with options and an (optional) logger. + +**Returns**: [Transformer](#Transformer) - - The instance. + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| [logger] | logger | cli | console | console | Logger instance. | + +**Example** +```js +var logger = ...; +var Transformer = require('jy-transform'); +var transformer = new Transformer(logger); +``` + + +### transformer.transform(options, [middleware]) ⇒ Promise +The entry method for all transformation accepting a configuration object and +an (optional) middleware function. + +**Kind**: instance method of [Transformer](#Transformer) +**Returns**: Promise - - Containing the transformation result as message (e.g. + to be logged by caller). +**Throws**: + +- TypeError - Will throw this error when the passed `middleware` + is not type of `Function`. +- Error - Will throw plain error when writing to file failed due to any reason. + +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [Options](#Options) | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(json) ``` **NOTE:** the Promise has to return the processed JSON! | + +**Example** +```js +var logger = ...; +var Transformer = require('jy-transform'); +var transformer = new Transformer(logger); +var Promise = require('bluebird'); +var options = {...}; +var middleware = function (json) { + json.myproperty = 'new value'; + return Promise.resolve(json); +}; + +transformer.transform(options, middleware) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +### Transformer~ensureMiddleware +Ensures that basic middleware is set. + +**Kind**: inner property of [Transformer](#Transformer) + + +## Validator +This class validates JS identifier. +

+ **NOTE:** this class is intended for internal use only! + +**Kind**: global class + +* [Validator](#Validator) + * [new Validator([logger])](#new_Validator_new) + * [.validateIdentifier(identifier)](#Validator+validateIdentifier) ⇒ boolean + + + +### new Validator([logger]) +Constructs the `Validator` with an (optional) logger. + +**Returns**: [Writer](#Writer) - The instance. + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| [logger] | logger | cli | console | console | Logger instance. | + +**Example** +```js +var Validator = require('./validator.js'); +var logger = ...; + +var validator = new Validator(logger); +``` + + +### validator.validateIdentifier(identifier) ⇒ boolean +This method checks if a given `identifier` is a valid ECMAScript 6 identifier. + +**Kind**: instance method of [Validator](#Validator) +**Returns**: boolean - - `true` if valid, else `false`. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| identifier | string | The identifier to check. | + +**Example** +```js +var Validator = require('./validator.js'); +var logger = ...; +var validator = new Validator(logger); +var identifier = 'foo'; + +logger.info('valid = ' + validator.validateIdentifier(identifier)); +``` + + +## Writer +This class provides utility methods usable to write JS objects + from memory to a JSON/JS/YAML destination + (file, object or [stream.Readable](stream.Readable)). + +**Kind**: global class + +* [Writer](#Writer) + * [new Writer([logger])](#new_Writer_new) + * [.validator](#Writer+validator) : [Validator](#Validator) + * [.writeYaml(object, options)](#Writer+writeYaml) ⇒ Promise + * [.writeJson(object, options)](#Writer+writeJson) ⇒ Promise + * [.writeJs(object, options)](#Writer+writeJs) ⇒ Promise + + + +### new Writer([logger]) +Constructs the `Writer` with an (optional) logger. + +**Returns**: [Writer](#Writer) - The instance. + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| [logger] | logger | cli | console | console | Logger instance. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; + +var writer = new Writer(logger); +``` + + +### writer.validator : [Validator](#Validator) +The validator. + +**Kind**: instance property of [Writer](#Writer) + + +### writer.writeYaml(object, options) ⇒ Promise +Writes a JS object to a YAML destination. + +**Kind**: instance method of [Writer](#Writer) +**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). +**Throws**: + +- Error - If YAML destination could not be written due to any reason. + +**Access:** public +**See** + +- [MIN_INDENT](#Constants+MIN_INDENT) +- [DEFAULT_INDENT](#Constants+DEFAULT_INDENT) +- [MAX_INDENT](#Constants+MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| object | object | The JS object to write into YAML destination. | +| options | [Options](#Options) | The write destination and indention. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; +var writer = new Writer(logger); + +// ---- write obj to file + +var obj = {...}, +var options = { + dest: 'result.yml', + indent: 2 +} + +writer.writeYaml(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to Writable + +options = { + dest: fs.createWriteStream('result.yml'), + indent: 4 +} + +writer.writeYaml(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +### writer.writeJson(object, options) ⇒ Promise +Writes a JS object to a JSON destination. + +**Kind**: instance method of [Writer](#Writer) +**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). +**Access:** public +**See** + +- [MIN_INDENT](#Constants+MIN_INDENT) +- [DEFAULT_INDENT](#Constants+DEFAULT_INDENT) +- [MAX_INDENT](#Constants+MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| object | object | The JS object to write into JSON destination. | +| options | [Options](#Options) | The write destination and indention. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; +var writer = new Writer(logger); + +// ---- write obj to file + +var obj = {...}; +var options = { + dest: 'result.json', + indent: 2 +} + +writer.writeJson(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to Writable + +options = { + dest: fs.createWriteStream('result.json'), + indent: 4 +} + +writer.writeJson(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + +// ---- write obj to object + +options = { + dest: {}, + indent: 4 +} + +writer.writeJson(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +### writer.writeJs(object, options) ⇒ Promise +Writes a JS object to a JS destination. The object is prefixed by `module.exports = `. + +**Kind**: instance method of [Writer](#Writer) +**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). +**Access:** public +**See** + +- [MIN_INDENT](#Constants+MIN_INDENT) +- [DEFAULT_INDENT](#Constants+DEFAULT_INDENT) +- [MAX_INDENT](#Constants+MAX_INDENT) + + +| Param | Type | Description | +| --- | --- | --- | +| object | object | The JSON to write into JS destination. | +| options | [Options](#Options) | The write destination and indention. | + +**Example** +```js +var Writer = require('jy-transform').Writer; +var logger = ...; +var writer = new Writer(logger); + +// ---- write obj to file + +var obj = {...}; +var options = { + dest: 'result.js', + indent: 2 +} + +writer.writeJs(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to Writable + +options = { + dest: fs.createWriteStream('result.json'), + indent: 4 +} + +writer.writeJs(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); + + +// ---- write obj to object + +options = { + dest: {}, + indent: 2 +} + +writer.writeJs(obj, options) + .then(function (msg){ + logger.info(msg); + }) + .catch(function (err) { + logger.error(err.stack); + }); +``` + + +## Options : object +**Kind**: global typedef +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| origin | string | "yaml" | The origin type. | +| target | string | "js" | The target type. | +| src | string | Readable | object | | The source (`string` type is treated as a file path). | +| dest | string | Writable | object | | The destination (`string` type is treated as a file path). | +| indent | number | 4 | The indention in files. | +| imports | string | | The exports name for reading from JS source file or objects only. | +| exports | string | | The exports name for usage in JS destination files only. | + diff --git a/docs/BADGES.md b/readme/BADGES.md similarity index 100% rename from docs/BADGES.md rename to readme/BADGES.md diff --git a/docs/CHANGELOG.md b/readme/CHANGELOG.md similarity index 100% rename from docs/CHANGELOG.md rename to readme/CHANGELOG.md diff --git a/docs/CONTRIBUTING.md b/readme/CONTRIBUTING.md similarity index 100% rename from docs/CONTRIBUTING.md rename to readme/CONTRIBUTING.md diff --git a/docs/USAGE.md b/readme/DOCUMENTATION.md similarity index 100% rename from docs/USAGE.md rename to readme/DOCUMENTATION.md diff --git a/docs/LOGO.md b/readme/LOGO.md similarity index 100% rename from docs/LOGO.md rename to readme/LOGO.md diff --git a/docs/TOC.md b/readme/TOC.md similarity index 100% rename from docs/TOC.md rename to readme/TOC.md diff --git a/src/debug-log.js b/src/debug-log.js index 59f8e9d..046b072 100644 --- a/src/debug-log.js +++ b/src/debug-log.js @@ -5,7 +5,7 @@ * @description The debug logger. Can be enabled via environment variables (set to `true`): * - `JYT_DEBUG`: (only DEBUG logging via `console.log`) * - `JYT_DEBUG`: (only ERROR logging via `console.error`) - * @public + * @private */ /** diff --git a/src/jyt.js b/src/jyt.js index 94ab602..4dc0a4f 100755 --- a/src/jyt.js +++ b/src/jyt.js @@ -3,6 +3,12 @@ import cli from 'cli'; import { DEFAULT_INDENT, DEFAULT_OPTIONS, TYPE_JS, TYPE_JSON, TYPE_YAML } from './constants'; import { transform } from './transformer'; +/** + * @module jy-transform:jyt + * @description The command line interface. + * @private + */ + // //////////////////////////////////////////////////////////////////////////// // CLI INIT // //////////////////////////////////////////////////////////////////////////// @@ -28,8 +34,8 @@ const packagePath = path.join(__dirname, '../package.json'); * ``` * long_tag: [short_tag, description, value_type, default_value]; * ``` - * @type {{origin: *[], target: *[], src: string[], dest: *[], indent: *[], force: string[], imports: string, - * exports: string}} + * @type {{origin: Array, target: Array, src: Array, dest: Array, indent: Array, force: Array, imports: Array, exports: + * Array}} * @private */ const options = { diff --git a/src/middleware.js b/src/middleware.js index 29f7fc8..8aea156 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -1,7 +1,7 @@ /** * @module jy-transform:middleware * @description The middleware ensuring functions module. - * @public + * @private */ /** diff --git a/src/transformer.js b/src/transformer.js index 37557b4..20d24b2 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -6,6 +6,12 @@ import Joi from './validation/joi-extensions'; import { transformerOptionsSchema } from './validation/options-schema'; import { writeJs, writeJson, writeYaml } from './writer'; +/** + * @module jy-transform:transformer + * @description This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. + * @public + */ + /** * This method creates the transformation described by the given options resolving a name to get the proper function. * diff --git a/src/type-definitions.js b/src/type-definitions.js index 968de91..4b79f9f 100644 --- a/src/type-definitions.js +++ b/src/type-definitions.js @@ -1,6 +1,7 @@ /** * @module jy-transform:type-definitions * @description The type definitions for this module. + * @private */ // ///////////////////////////////////////////////////////////////////////////// diff --git a/src/validation/joi-extensions-file-helper.js b/src/validation/joi-extensions-file-helper.js index 9079dd6..73d1101 100644 --- a/src/validation/joi-extensions-file-helper.js +++ b/src/validation/joi-extensions-file-helper.js @@ -5,7 +5,7 @@ import { debug } from '../debug-log'; /** * @module jy-transform:validation:joi-extensions-file-helper * @description An (extended) Joi validation schema helper functions for the module options on FS validation. - * @protected + * @private */ /** diff --git a/src/validation/joi-extensions-identifier-helper.js b/src/validation/joi-extensions-identifier-helper.js index 0b88d84..397770c 100644 --- a/src/validation/joi-extensions-identifier-helper.js +++ b/src/validation/joi-extensions-identifier-helper.js @@ -1,7 +1,7 @@ /** * @module jy-transform:validation:joi-extensions-identifier-helper * @description An (extended) Joi validation schema helper function for the module options to validate ES6 identifiers. - * @protected + * @private */ /** diff --git a/src/validation/joi-extensions.js b/src/validation/joi-extensions.js index dd75abd..3e8d3da 100644 --- a/src/validation/joi-extensions.js +++ b/src/validation/joi-extensions.js @@ -7,7 +7,7 @@ import { isValidEs6Identifier } from './joi-extensions-identifier-helper'; * @module jy-transform:validation:joi-extension * @description The module exporting the {@link external:joi.Extension}s for option validations. * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension Joi.extend(extension) method}. - * @public + * @private */ /** diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-helper.js index 69742db..fb5a08a 100644 --- a/src/validation/options-schema-helper.js +++ b/src/validation/options-schema-helper.js @@ -12,8 +12,8 @@ import { * @description Provides some helper functions used in {@link module:validation:options-schema} to resolve default * values for origin and target depending on the `options.src` or `options.dest` value. * @type {Object} - * @protected * @see {@link module:validation:options-schema} + * @private */ /** diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js index bbd9af2..67cd262 100644 --- a/src/validation/options-schema.js +++ b/src/validation/options-schema.js @@ -20,8 +20,8 @@ import { * @module jy-transform:validation:options-schema * @description The module options schema used in {@link module:options-validator}. * @type {Object} - * @protected * @see {@link module:options-validator} + * @private */ // ///////////////////////////////////////////////////////////////////////////// diff --git a/test/helper-constants.js b/test/helper-constants.js index ed0c053..7863baa 100644 --- a/test/helper-constants.js +++ b/test/helper-constants.js @@ -3,10 +3,10 @@ import chalk from 'chalk'; import Package from '../package.json'; /** - * @module helper-constants + * @module jy-transform:unit:helper-constants * @description The test suite constants definitions. - * @public * @type {Object} + * @private */ /** diff --git a/test/logger.js b/test/logger.js index 1f1c510..d0446fe 100644 --- a/test/logger.js +++ b/test/logger.js @@ -6,6 +6,13 @@ import fsExtra from 'fs-extra'; // const ensureDir = promisify(fsExtra.ensureDir); // const emptyDir = promisify(fsExtra.emptyDir); +/** + * @module jy-transform:unit:logger + * @description The test suite logger. + * @type {Object} + * @private + */ + // //////////////////////////////////////////////////////////////////////////// // PRIVATES // //////////////////////////////////////////////////////////////////////////// @@ -31,7 +38,7 @@ const TEST_TMP_DIR = './test/tmp'; /** * This function formats the log string by given options to log. * - * @param {{timestamp: function, level: string, [message: string], [meta: object]}} options - The formatter options. + * @param {Object} options - The formatter options. * @returns {string} The log string. * @private */ diff --git a/test/unit/test-index.js b/test/unit/test-index.js index 48ab2df..301002f 100644 --- a/test/unit/test-index.js +++ b/test/unit/test-index.js @@ -4,6 +4,7 @@ import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; /** * @module jy-transform:test-unit:index * @description This unit test module tests the correct exporting from _./index.js_. + * @private */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - index - ', () => { @@ -68,7 +69,18 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - index - ', () => { expect(DEFAULT_FORCE_FILE_OVERWRITE).toBeDefined(); expect(DEFAULT_FORCE_FILE_OVERWRITE).toBeFalsy(); // TODO - // {DEFAULT_FORCE_FILE_OVERWRITE: false, DEFAULT_INDENT: 2, DEFAULT_JS_EXPORTS_IDENTIFIER: undefined, DEFAULT_JS_IMPORTS_IDENTIFIER: undefined, DEFAULT_OPTIONS: {dest: storing relative to input file, exports: undefined, force: false, imports: undefined, indent: 2, origin: if not given, the type is tried to be inferred from the extension of source path, else it is 'yaml', target: if not given, the type is tried to be inferred from the extension of destination path, else it is 'js'}, DEFAULT_ORIGIN: yaml, DEFAULT_TARGET: js, DEST_DESCRIPTION: storing relative to input file, JS: js, JSON: json, JSON_TO_JS: json2js, JSON_TO_JSON: json2json, JSON_TO_YAML: json2yaml, JS_TO_JS: js2js, JS_TO_JSON: js2json, JS_TO_YAML: js2yaml, MAX_INDENT: 8, MIN_INDENT: 0, ORIGIN_DESCRIPTION: if not given, the type is tried to be inferred from the extension of source path, else it is 'yaml', TARGET_DESCRIPTION: if not given, the type is tried to be inferred from the extension of destination path, else it is 'js', TRANSFORMATIONS: [yaml2js, yaml2json, js2yaml, json2yaml, json2js, js2json, yaml2yaml, json2json, js2js], TYPES: [yaml, json, js], UTF8: utf8, YAML: yaml, YAML_TO_JS: yaml2js, YAML_TO_JSON: yaml2json, YAML_TO_YAML: yaml2yaml} + // {DEFAULT_FORCE_FILE_OVERWRITE: false, DEFAULT_INDENT: 2, DEFAULT_JS_EXPORTS_IDENTIFIER: undefined, + // DEFAULT_JS_IMPORTS_IDENTIFIER: undefined, DEFAULT_OPTIONS: {dest: storing relative to input file, exports: + // undefined, force: false, imports: undefined, indent: 2, origin: if not given, the type is tried to be + // inferred from the extension of source path, else it is 'yaml', target: if not given, the type is tried to be + // inferred from the extension of destination path, else it is 'js'}, DEFAULT_ORIGIN: yaml, DEFAULT_TARGET: js, + // DEST_DESCRIPTION: storing relative to input file, JS: js, JSON: json, JSON_TO_JS: json2js, JSON_TO_JSON: + // json2json, JSON_TO_YAML: json2yaml, JS_TO_JS: js2js, JS_TO_JSON: js2json, JS_TO_YAML: js2yaml, MAX_INDENT: + // 8, MIN_INDENT: 0, ORIGIN_DESCRIPTION: if not given, the type is tried to be inferred from the extension of + // source path, else it is 'yaml', TARGET_DESCRIPTION: if not given, the type is tried to be inferred from the + // extension of destination path, else it is 'js', TRANSFORMATIONS: [yaml2js, yaml2json, js2yaml, json2yaml, + // json2js, js2json, yaml2yaml, json2json, js2js], TYPES: [yaml, json, js], UTF8: utf8, YAML: yaml, YAML_TO_JS: + // yaml2js, YAML_TO_JSON: yaml2json, YAML_TO_YAML: yaml2yaml} }) ); diff --git a/test/unit/test-middleware.js b/test/unit/test-middleware.js index e230e71..28a7084 100644 --- a/test/unit/test-middleware.js +++ b/test/unit/test-middleware.js @@ -7,6 +7,7 @@ import { logger } from '../logger'; /** * @module jy-transform:unit-test:test-middleware * @description This unit test suite checks the validity and correctness of {@link middleware} module. + * @private */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - middleware - ', () => { diff --git a/test/unit/test-reader.js b/test/unit/test-reader.js index 3842a09..fdad8a3 100644 --- a/test/unit/test-reader.js +++ b/test/unit/test-reader.js @@ -12,6 +12,7 @@ import { /** * @module jy-transform:unit-test:test-reader * @description This unit test suite checks the validity and correctness of _./src/reader.js_ module. + * @private */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { diff --git a/test/unit/test-transformer.js b/test/unit/test-transformer.js index 70d7ba3..b82f0bd 100644 --- a/test/unit/test-transformer.js +++ b/test/unit/test-transformer.js @@ -18,6 +18,7 @@ const fsPromised = promisify(fs); /** * @module jy-transform:unit-test:test-transformer * @description This unit test suite checks the correct transformation behaviour of {@link Transformer} class. + * @private */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js index dee83a6..a81bd72 100644 --- a/test/unit/test-writer.js +++ b/test/unit/test-writer.js @@ -18,6 +18,7 @@ const fsPromised = promisify(fs); /** * @module jy-transform:unit-test:test-writer * @description This unit test suite checks the validity and correctness of _./src/js_ module. + * @private */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { diff --git a/test/unit/validation/test-joi-extensions-file-helper.js b/test/unit/validation/test-joi-extensions-file-helper.js index 310e9f9..02995d3 100644 --- a/test/unit/validation/test-joi-extensions-file-helper.js +++ b/test/unit/validation/test-joi-extensions-file-helper.js @@ -4,6 +4,7 @@ import { isExistingFile } from '../../../src/validation/joi-extensions-file-help /** * @module jy-transform:test-unit:test-joi-extension-file-helper * @description This unit test module tests validation FS helper method. + * @private */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-file-helper - ', () => { diff --git a/test/unit/validation/test-joi-extensions-identifier-helper.js b/test/unit/validation/test-joi-extensions-identifier-helper.js index 4cf068d..df18fca 100644 --- a/test/unit/validation/test-joi-extensions-identifier-helper.js +++ b/test/unit/validation/test-joi-extensions-identifier-helper.js @@ -4,6 +4,7 @@ import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; /** * @module jy-transform:unit-test:test-joi-extensions-identifier-helper * @description This unit test suite checks validity and correctness of ES6 identifiers. + * @private */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-identifier-helper - ', () => { diff --git a/test/unit/validation/test-options-schema-helper.js b/test/unit/validation/test-options-schema-helper.js index d5f1813..415c132 100644 --- a/test/unit/validation/test-options-schema-helper.js +++ b/test/unit/validation/test-options-schema-helper.js @@ -24,6 +24,7 @@ import Joi from '../../../src/validation/joi-extensions'; /** * @module jy-transform:unit-test:test-options-schema-helper * @description This unit test suite checks the validity and correctness of options schema helper methods. + * @private */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { diff --git a/test/unit/validation/test-options-schema.js b/test/unit/validation/test-options-schema.js index 1b67de1..5e0a258 100644 --- a/test/unit/validation/test-options-schema.js +++ b/test/unit/validation/test-options-schema.js @@ -21,6 +21,7 @@ import Joi from '../../../src/validation/joi-extensions'; /** * @module jy-transform:unit-test:test-options-schema * @description This unit test suite checks the validity and correctness of options schema. + * @private */ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { @@ -175,7 +176,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { const validatedOptions = await Joi.validate(options, transformerOptionsSchema); expect(validatedOptions.origin).toBe(TYPE_JS); expect(validatedOptions.target).toBe(TYPE_YAML); - }); + }); it('should resolve default ' + DEFAULT_TARGET + ' output when options.dest is Stream.Writable (without path) ' + 'and options.target is not set)', async () => { From f773cc64233599b23d2b17f539ddf4e48dc24237 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Wed, 21 Jun 2017 02:16:46 +0200 Subject: [PATCH 08/58] Better readme creation --- bin/create-readme.sh | 165 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100755 bin/create-readme.sh diff --git a/bin/create-readme.sh b/bin/create-readme.sh new file mode 100755 index 0000000..4db74d5 --- /dev/null +++ b/bin/create-readme.sh @@ -0,0 +1,165 @@ +#!/bin/bash + +api="true" +package="true" +changelog="true" +license="true" +env="false" +makefile="true" + +while [[ $# -gt 1 ]]; do + key="$1" + + case $key in + -a|--api) + api="$2" + shift # past argument + ;; + -p|--package) + package="$2" + shift # past argument + ;; + -c|--changelog) + changelog="$2" + shift # past argument + ;; + -l|--license) + license="$2" + ;; + -e|--env) + env="$2" + ;; + -m|--makefile) + makefile="$2" + ;; + *) + # unknown option + ;; + esac + shift +done + +############################################################################### +# PACKAGE.md +############################################################################### + +if [ "$package" == "true" ]; then + printf "Create documentation (PACKAGE.md)\n" + touch PACKAGE.md + node node_modules/.bin/package-json-to-readme --no-footer package.json > PACKAGE.md + cat readme/LOGO.md >> README.md + printf "\n\n" >> README.md + head -12 PACKAGE.md > README.md + printf "\n\n" >> README.md +else + # to ensure that the README.md is always empty at the beginning + printf "" > README.md +fi + +############################################################################### +# README.md +############################################################################### + +cat readme/BADGES.md >> README.md + +printf "\n\n\n" >> README.md +cat readme/DOCUMENTATION.md >> README.md +printf "\n\n" >> README.md + +if [[ "$package" == "true" ]] || [[ "$api" == "true" ]] || [[ "$env" == "true" ]] || ([[ -f "$MAKE_FILE" ]] && [[ "$makefile" == "true" ]]); then + printf "## Further information" >> README.md + printf "\n\n" >> README.md +fi + +if [ "$package" == "true" ]; then + printf -- "- [Module Details](./PACKAGE.md)" >> README.md + printf "\n\n" >> README.md +fi + +############################################################################### +# API-PUBLIC.md +############################################################################### + +if [ "$api" == "true" ]; then + printf "Create documentation (API-PUBLIC.md)\n" + touch API-PUBLIC.md + node node_modules/.bin/jsdoc2md --no-cache --configure .jsdoc.json . > API-PUBLIC.md + + printf -- "- [Public Api Reference](./API-PUBLIC.md)" >> README.md + printf "\n\n" >> README.md +fi + +############################################################################### +# API-PRIVATE.md +############################################################################### + +if [ "$api" == "true" ]; then + printf "Create documentation (API-PRIVATE.md)\n" + touch API-PRIVATE.md + node node_modules/.bin/jsdoc2md --no-cache --private --configure .jsdoc.json . > API-PRIVATE.md + + printf -- "- [Private Api Reference](./API-PRIVATE.md)" >> README.md + printf "\n\n" >> README.md +fi + +############################################################################### +# MAKE.md +############################################################################### + +MAKE_FILE=Makefile + +if [[ -f "$MAKE_FILE" ]] && [[ "$makefile" == "true" ]]; then + printf "Create documentation (MAKE.md)\n" + + printf "Target Call | Description | Dependencies\n---|---|---\n" > MAKE.md + + # find out what is the default goal (if set) + DEFAULT_TARGET=$(awk 'BEGIN {FS = "^.DEFAULT_GOAL :=|^.DEFAULT_GOAL:= |^.DEFAULT_GOAL:="} /^.DEFAULT_GOAL.*/ {printf $2}' $MAKE_FILE | sed "s/ //g") + if [ ! -z "$DEFAULT_TARGET" ]; then + printf " - Print default target: ${DEFAULT_TARGET}\n" + printf "\`$ make\` | This calls the default target \`${DEFAULT_TARGET}\`. |\n" >> MAKE.md + else + printf " - No default target found\n" + fi + + # take care of all other targets + printf " - Print all targets" + awk 'BEGIN {FS = ": |## "} /^[a-zA-Z_-]+:.*?## / {printf "\`$ make %s\` | %s | `%s`\n", $1, $3, $2}' Makefile | sed "s/| \`\`$/|/g" | sed "s/ \`$/\`/g"| sort >> MAKE.md + + printf -- "- [Makefile Reference](./MAKE.md)" >> README.md + printf "\n\n" >> README.md +fi + +############################################################################### +# ENV.md +############################################################################### + +if [ "$env" == "true" ]; then + printf "Create documentation (ENV.md)\n" + + touch ENV.md + babel-node node_modules/.bin/cfg-combined markdown > ENV.md + + printf -- "- [Environment Reference](./ENV.md)" >> README.md + printf "\n\n" >> README.md +fi + +############################################################################### +# Finalize README.md +############################################################################### + +if [ "$changelog" == "true" ]; then + cat readme/CHANGELOG.md >> README.md + printf "\n\n" >> README.md +fi + +if [ "$license" == "true" ]; then + printf "## License\n\n" >> README.md + cat LICENSE.md >> README.md +fi + +############################################################################### +# Create the TOC in README.md +############################################################################### + +node node_modules/.bin/doctoc README.md --github --title "## TOC" --maxlevel 2 From b15d93f7f547b0510b59060b1c10e5ece5e7c024 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Wed, 21 Jun 2017 02:41:04 +0200 Subject: [PATCH 09/58] Better readme creation --- .jestrc.js | 4 +- src/writer.js | 85 ++++++++++++++++++++-------------------- test/unit/test-writer.js | 77 +++++++++++++++++------------------- 3 files changed, 81 insertions(+), 85 deletions(-) diff --git a/.jestrc.js b/.jestrc.js index 4f3cdbb..793cbdf 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -25,9 +25,9 @@ module.exports = { //'**/test/unit/test-transformer.js', // '**/test/unit/test-index.js', //'**/test/unit/test-reader.js', - //'**/test/unit/test-writer.js', + '**/test/unit/test-writer.js', // '**/test/unit/validation/test-options-schema.js', - '**/test/unit/**/*.js', + //'**/test/unit/**/*.js', // '**/test/test-log-wrapper.js', // '**/test/unit/test-middleware.js', diff --git a/src/writer.js b/src/writer.js index 599d985..cd279af 100644 --- a/src/writer.js +++ b/src/writer.js @@ -19,7 +19,8 @@ import { transformerOptionsSchema } from './validation/options-schema'; /** * @module jy-transform:writer - * @description This module provides the _write_ functionality for YAML, JS or JSON targets. + * @description This module provides the _write_ functionality to write JS objects from memory to a JSON/JS/YAML + * destination (file, object or {@link stream.Readable}). * @public */ @@ -166,9 +167,9 @@ function writeToFile(object, dest, target, resolve, reject, forceOverwrite) { * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. * @param {Function} resolve - The Promise `resolve` callback. * @param {Function} reject - The Promise `reject` callback. - * @see {@link Constants#TYPE_YAML} - * @see {@link Constants#TYPE_JSON} - * @see {@link Constants#TYPE_JS} + * @see {@link TYPE_YAML} + * @see {@link TYPE_JSON} + * @see {@link TYPE_JS} * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). * @throws {Error} If serialized JS object could not be written due to any reason. * @private @@ -183,37 +184,17 @@ function writeToStream(object, dest, target, resolve, reject) { dest.end(); } -/** - * @class This class provides utility methods usable to write JS objects - * from memory to a JSON/JS/YAML destination - * (file, object or {@link stream.Readable}). - * @example - * const { Writer } from 'jy-transform'); - * const logger = ...; - * const writer = new Writer(logger); - */ -// export class Writer { -// /** -// * Constructs the `Writer` with an (optional) logger. -// * -// * @param {(logger|cli|console)} [logger=console] - Logger instance. -// * @public -// */ -// constructor(logger) { -// this.logger = new LogWrapper(logger); -// } - /** * Writes a JS object to a YAML destination. * * @param {Object} object - The JS object to write into YAML destination. * @param {Options} options - The write destination and indention. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} + * @see {@link MIN_INDENT} + * @see {@link DEFAULT_INDENT} + * @see {@link MAX_INDENT} * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). * @throws {Error} If YAML destination could not be written due to any reason. - * @public + * @private * @example * var Writer = require('jy-transform').Writer; * var logger = ...; @@ -251,7 +232,7 @@ function writeToStream(object, dest, target, resolve, reject) { * logger.error(err.stack); * }); */ -export async function writeYaml(object, options) { +async function writeYaml(object, options) { const assertedOptions = await Joi.validate(options, transformerOptionsSchema); return new Promise((resolve, reject) => { let yaml; @@ -279,11 +260,11 @@ export async function writeYaml(object, options) { * * @param {Object} object - The JS object to write into JSON destination. * @param {Options} options - The write destination and indention. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} + * @see {@link MIN_INDENT} + * @see {@link DEFAULT_INDENT} + * @see {@link MAX_INDENT} * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). - * @public + * @private * @example * var Writer = require('jy-transform').Writer; * var logger = ...; @@ -336,7 +317,7 @@ export async function writeYaml(object, options) { * logger.error(err.stack); * }); */ -export async function writeJson(object, options) { +async function writeJson(object, options) { const assertedOptions = await Joi.validate(options, transformerOptionsSchema); return new Promise((resolve, reject) => { if (typeof assertedOptions.dest === 'string') { // file @@ -357,11 +338,11 @@ export async function writeJson(object, options) { * * @param {Object} object - The JSON to write into JS destination. * @param {Options} options - The write destination and indention. - * @see {@link Constants#MIN_INDENT} - * @see {@link Constants#DEFAULT_INDENT} - * @see {@link Constants#MAX_INDENT} - * @returns {Promise} - Containing the write success message to handle by caller (e.g. for logging). - * @public + * @see {@link MIN_INDENT} + * @see {@link DEFAULT_INDENT} + * @see {@link MAX_INDENT} + * @returns {Promise.} - Containing the write success message to handle by caller (e.g. for logging). + * @private * @example * var Writer = require('jy-transform').Writer; * var logger = ...; @@ -415,7 +396,7 @@ export async function writeJson(object, options) { * logger.error(err.stack); * }); */ -export async function writeJs(object, options) { +async function writeJs(object, options) { //logger.debug('OPTIONS BEFORE ASSERTING IN writeJs:::' + JSON.stringify(options)); const assertedOptions = await Joi.validate(options, transformerOptionsSchema); return new Promise((resolve, reject) => { @@ -445,8 +426,26 @@ export async function writeJs(object, options) { }); } +/** + * TODO: doc me. + * + * @param {Options} options - The write options. + * @returns {Promise.} Resolves with write success message. + * @public + */ +export async function write(options) { + switch (options.target) { + case TYPE_JS: + return await writeJs(options); + case TYPE_JSON: + return await writeJson(options); + case TYPE_YAML: + return await writeYaml(options); + default: // TODO better handling + throw new Error('should not happen!'); + } +} + export default { - writeJs, - writeJson, - writeYaml, + write, }; diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js index a81bd72..9d2d52f 100644 --- a/test/unit/test-writer.js +++ b/test/unit/test-writer.js @@ -4,7 +4,7 @@ import YAMLException from 'js-yaml/lib/js-yaml/exception'; import fs from 'fs'; import os from 'os'; import stream from 'stream'; -import { writeJs, writeJson, writeYaml } from '../../src/writer'; +import { write } from '../../src/writer'; import { logger } from '../logger'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; import { @@ -92,36 +92,32 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { /** * Assert an `Error` for a given writer function. * - * @param {Object} json - The json to write. * @param {Object} options - The options which potentially produce the error. - * @param {Function} writerFunc - The function to call for assertion. * @param {Object} [match={name:'Error'}] - The propertie(s) error should contain. * @private */ - const expectWriteError = (json, options, writerFunc, match = { name: 'Error' }) => { + const expectWriteError = (options, match = { name: 'Error' }) => { expect.assertions(1); - return expect(writerFunc(json, options)).rejects.toMatchObject(match); + return expect(write(options)).rejects.toMatchObject(match); }; /** * Assert an `Error` for a given writer function. * - * @param {Object} json - The json to write. * @param {Object} options - The options which potentially produce the error. - * @param {Function} writerFunc - The function to call for assertion. * @param {Error} [match=Error'] - The error type to match. * @private */ - const expectWriteErrorByType = (json, options, writerFunc, match = Error) => { + const expectWriteErrorByType = (options, match = Error) => { expect.assertions(1); - return expect(writerFunc(json, options)).rejects.toBeInstanceOf(match); + return expect(write(options)).rejects.toBeInstanceOf(match); }; describe('Testing writeJs(...)', () => { it('should write JS to file', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.js'; - const msg = await writeJs(JSON_CONTENT, { src: JSON_CONTENT, dest: file }); + const msg = await write({ src: JSON_CONTENT, dest: file }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -129,7 +125,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JS to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.js'; - const msg = writeJs(JSON_CONTENT, { src: JSON_CONTENT, dest: fs.createWriteStream(file) }); + const msg = write({ src: JSON_CONTENT, dest: fs.createWriteStream(file) }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -143,7 +139,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: 'test', }; - const msg = writeJs(JSON_CONTENT, options); + const msg = write(options); expect(msg).toBeDefined(); await expectDestFileExists(file); // eslint-disable-next-line import/no-unresolved, global-require @@ -158,7 +154,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js', exports: '#3/-', }; - return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(options, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'#3/-\')', () => { @@ -168,7 +164,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: '#3/-', }; - return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(options, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'if\')', () => { @@ -178,7 +174,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: 'if', }; - return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(options, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by provoked error', () => { @@ -193,13 +189,13 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { src: JSON_CONTENT, target: TYPE_JS, dest: errorThrowingStream, - }, writeJs, Error); + }, Error); }); it('should write JS to JS object', async () => { expect.assertions(4); const options = { src: JSON_CONTENT, dest: {} }; - const msg = await writeJs(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(options.dest, 'test')).toBeTruthy(); @@ -212,7 +208,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: {}, exports: '', }; - return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(options, { name: 'ValidationError', isJoi: true }); }); const exports = 'foo'; @@ -223,7 +219,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: {}, exports, }; - const msg = await writeJs(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(options.dest, exports)).toBeTruthy(); @@ -238,11 +234,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: {}, exports: invalidIdentifier, }; - return expectWriteError(JSON_CONTENT, options, writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError(options, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS with Error on missing destination', () => { - return expectWriteError(JSON_CONTENT, { src: JSON_CONTENT }, writeJs, { name: 'ValidationError', isJoi: true }); + return expectWriteError({ src: JSON_CONTENT }, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS to file by invalid file path', (done) => { @@ -254,7 +250,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/test-data-by-js-to-file.js' }; - writeJs(JSON_CONTENT, options) + write(JSON_CONTENT, options) .then((msg) => { done(new Error('Error expected, but got success message: ' + msg)); }) @@ -283,7 +279,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { src: JSON_CONTENT, dest: WRITER_TEST_BASE_DIR + '/test-data-by-json-to-file.json' }; - const msg = await writeJson(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); await expectDestFileExists(options.dest); }); @@ -291,7 +287,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JSON to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-json-stream.json'; - const msg = await writeJson(JSON_CONTENT, { + const msg = await write({ src: JSON_CONTENT, target: TYPE_JSON, dest: fs.createWriteStream(file), @@ -306,7 +302,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { src: JSON_CONTENT, dest: {}, }; - const msg = await writeJson(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); const result = JSON.parse(options.dest); @@ -315,7 +311,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }); it('should reject with Error on missing destination', () => { - return expectWriteError(JSON_CONTENT, { src: JSON_CONTENT }, writeJson, { name: 'ValidationError', isJoi: true }); + return expectWriteError({ src: JSON_CONTENT }, { name: 'ValidationError', isJoi: true }); }); }); @@ -323,7 +319,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write YAML to file', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.yaml'; - const msg = await writeYaml(JSON_CONTENT, { + const msg = await write({ src: JSON_CONTENT, dest: file }); @@ -334,7 +330,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write YAML to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.yaml'; - const msg = await writeYaml(JSON_CONTENT, { + const msg = await write({ src: JSON_CONTENT, target: TYPE_YAML, dest: fs.createWriteStream(file), @@ -350,7 +346,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { target: TYPE_YAML, dest: {} }; - const msg = await writeYaml(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); const key = Object.keys(JSON_CONTENT)[0]; @@ -361,22 +357,23 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { const invalidYamlJson = () => { }; return expectWriteError(invalidYamlJson, { - src: JSON_CONTENT, + src: invalidYamlJson, dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file-invalid.yaml' - }, writeYaml, { name: 'YAMLException' }); + }, { name: 'YAMLException' }); }); it('should reject with Error on missing destination', () => { - return expectWriteError(JSON_CONTENT, { src: JSON_CONTENT }, writeYaml, { name: 'ValidationError', isJoi: true }); + return expectWriteError({ src: JSON_CONTENT }, { name: 'ValidationError', isJoi: true }); }); }); describe('Testing force overwrite file', () => { it('should reject when options.dest is a directory', () => { - return expectWriteErrorByType(JSON_CONTENT, { + return expectWriteErrorByType({ src: JSON_CONTENT, - dest: './test/data' - }, writeYaml, Error); + dest: './test/data', + target: TYPE_YAML + }, Error); }); it('should write YAML to stream, overwrite on 2nd write, ' + @@ -391,7 +388,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { const asyncFunctions = [ async () => { - const msg = await writeYaml(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); await expectDestFileExists(dest); return 'overwrite test #1 should initially write YAML to file \'' + dest + '\''; @@ -403,7 +400,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest, force: true }; - const msg = await writeYaml(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); await expectDestFileExists(dest); await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); @@ -416,7 +413,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest, force: false, }; - const msg = await writeYaml(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); return 'overwrite test #3 shouldn\'t overwrite existing YAML file \'' + dest + @@ -429,7 +426,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest, force: true }; - const msg = await writeYaml(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); @@ -441,7 +438,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { indent: 4, dest, }; - const msg = await writeYaml(JSON_CONTENT, options); + const msg = await write(options); expect(msg).toBeDefined(); await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); From 4cb1db21e628d24aba89174835263eb324723d6c Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Wed, 21 Jun 2017 09:26:42 +0200 Subject: [PATCH 10/58] Tests green --- .jestrc.js | 4 +- src/constants.js | 98 ---------- src/reader.js | 38 ++-- src/transformer.js | 378 ++++----------------------------------- src/writer.js | 71 ++++---- test/unit/test-index.js | 30 ++-- test/unit/test-reader.js | 72 ++++---- test/unit/test-writer.js | 96 ++++------ 8 files changed, 182 insertions(+), 605 deletions(-) diff --git a/.jestrc.js b/.jestrc.js index 793cbdf..4f3cdbb 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -25,9 +25,9 @@ module.exports = { //'**/test/unit/test-transformer.js', // '**/test/unit/test-index.js', //'**/test/unit/test-reader.js', - '**/test/unit/test-writer.js', + //'**/test/unit/test-writer.js', // '**/test/unit/validation/test-options-schema.js', - //'**/test/unit/**/*.js', + '**/test/unit/**/*.js', // '**/test/test-log-wrapper.js', // '**/test/unit/test-middleware.js', diff --git a/src/constants.js b/src/constants.js index 8ceeed1..0b92e79 100644 --- a/src/constants.js +++ b/src/constants.js @@ -38,14 +38,6 @@ export const TYPE_JSON = 'json'; */ export const TYPE_JS = 'js'; -/** - * The type constants assembled in an array: `[ 'yaml', 'json', 'js' ]`. - * @type {string[]} - * @constant - * @public - */ -export const TYPES = [TYPE_YAML, TYPE_JSON, TYPE_JS]; - /** * A map for extensions to type. * @@ -175,93 +167,3 @@ export const DEFAULT_OPTIONS = { imports: DEFAULT_JS_IMPORTS_IDENTIFIER, exports: DEFAULT_JS_EXPORTS_IDENTIFIER, }; - -/** - * The transformation direction YAML ⇒ JS. - * @type {string} - * @constant - * @public - */ -export const YAML_TO_JS = 'yaml2js'; - -/** - * The transformation direction YAML ⇒ JSON. - * @type {string} - * @constant - * @public - */ -export const YAML_TO_JSON = 'yaml2json'; - -/** - * The transformation direction JS ⇒ YAML. - * @type {string} - * @constant - * @public - */ -export const JS_TO_YAML = 'js2yaml'; - -/** - * The transformation direction JSON ⇒ YAML. - * @type {string} - * @constant - * @public - */ -export const JSON_TO_YAML = 'json2yaml'; - -/** - * The transformation direction JSON ⇒ JS. - * @type {string} - * @constant - * @public - */ -export const JSON_TO_JS = 'json2js'; - -/** - * The transformation direction JS ⇒ JSON. - * @type {string} - * @constant - * @public - */ -export const JS_TO_JSON = 'js2json'; - -/** - * The transformation direction YAML ⇒ YAML. - * @type {string} - * @constant - * @public - */ -export const YAML_TO_YAML = 'yaml2yaml'; - -/** - * The transformation direction JSON ⇒ JSON. - * @type {string} - * @constant - * @public - */ -export const JSON_TO_JSON = 'json2json'; - -/** - * The transformation direction JS ⇒ JS. - * @type {string} - * @constant - * @public - */ -export const JS_TO_JS = 'js2js'; - -/** - * The transformation directions. - * @type {string[]} - * @constant - * @public - */ -export const TRANSFORMATIONS = [ - YAML_TO_JS, - YAML_TO_JSON, - JS_TO_YAML, - JSON_TO_YAML, - JSON_TO_JS, - JS_TO_JSON, - YAML_TO_YAML, - JSON_TO_JSON, - JS_TO_JS -]; diff --git a/src/reader.js b/src/reader.js index 6031285..a11214e 100644 --- a/src/reader.js +++ b/src/reader.js @@ -85,7 +85,7 @@ function readFromStream(readable, resolve, reject, origin) { * * @param {Options} options - Contains the JS/JSON source reference to read from. * @returns {Promise.} Contains the read JS object. - * @public + * @private * @example * var Reader = require('jy-transform').Reader; * var logger = ...; @@ -137,7 +137,7 @@ function readFromStream(readable, resolve, reject, origin) { * logger.error(err.stack); * }); */ -export async function readJs(options) { +async function readJs(options) { logger.debug('OPTIONS BEFORE ASSERTING IN readJs:::' + JSON.stringify(options)); const assertedOptions = await Joi.validate(options, readerOptionsSchema); return new Promise((resolve, reject) => { @@ -173,10 +173,10 @@ export async function readJs(options) { reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object ' + 'but could not find this object, pls ensure that object source contains it.')); } else { - resolve(obj); + resolve(Object.assign({}, obj)); // clone, do not alter original object! } } else { - resolve(assertedOptions.src); + resolve(Object.assign({}, assertedOptions.src)); // clone, do not alter original object! } }); } @@ -188,7 +188,7 @@ export async function readJs(options) { * * @param {Options} options - Contains the YAML source reference to read from. * @returns {Promise.} Contains the read JS object. - * @public + * @private * @example * var Reader = require('jy-transform').Reader; * var logger = ...; @@ -223,10 +223,9 @@ export async function readJs(options) { * logger.error(err.stack); * }); */ -export async function readYaml(options) { +async function readYaml(options) { logger.debug('OPTIONS BEFORE ASSERTING IN readYaml::: ' + JSON.stringify(options)); const assertedOptions = await Joi.validate(options, readerOptionsSchema); - const self = this; return new Promise((resolve, reject) => { if (typeof assertedOptions.src === 'string') { // load source from YAML file @@ -243,7 +242,7 @@ export async function readYaml(options) { } else if (isStream.readable(assertedOptions.src)) { readFromStream(assertedOptions.src, resolve, reject, TYPE_YAML); } else { - resolve(assertedOptions.src); + resolve(assertedOptions.src); // TODO does that make sense? } }); } @@ -266,7 +265,26 @@ export async function readYaml(options) { // jsYaml.safeLoadAll(yaml, function (doc) { // TOD this will not work in Promise environment!!! // self.logger.trace(doc); jsDocs.push(doc); }); }); }); }; } +/** + * TODO: doc me. + * + * @param {Options} options - The read options. + * @returns {Promise.} Resolves with read success message. + * @public + */ +export async function read(options) { + const assertedOptions = await Joi.validate(options, readerOptionsSchema); + switch (assertedOptions.origin) { + case TYPE_JS: + case TYPE_JSON: + return await readJs(assertedOptions); + case TYPE_YAML: + return await readYaml(assertedOptions); + default: // TODO better handling + throw new Error('should not happen!'); + } +} + export default { - readJs, - readYaml, + read, }; diff --git a/src/transformer.js b/src/transformer.js index 20d24b2..468c18c 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -1,10 +1,7 @@ import logger from 'cli'; -import { TRANSFORMATIONS } from './constants'; import { ensureMiddleware } from './middleware'; -import { readJs, readYaml } from './reader'; -import Joi from './validation/joi-extensions'; -import { transformerOptionsSchema } from './validation/options-schema'; -import { writeJs, writeJson, writeYaml } from './writer'; +import { read } from './reader'; +import { write } from './writer'; /** * @module jy-transform:transformer @@ -12,350 +9,25 @@ import { writeJs, writeJson, writeYaml } from './writer'; * @public */ -/** - * This method creates the transformation described by the given options resolving a name to get the proper function. - * - * @param {Options} options - The configuration for a transformation. - * @returns {Promise.} The transformation name. - * @example - * var OptionsHandler = require('./options-handler.js'); - * var logger = ...; - * var optionsHandler = new OptionsHandler(logger); - * - * optionsHandler.validateTransformation(options) - * .spread(function (validatedOptions, transformation) { - * ... - * )): - * @see {@link transformations} - * @public - */ -async function createTransformation(options) { - const transformation = options.origin + '2' + options.target; - console.log('Transformation: ' + transformation) - return transformation; -} - -/** - * Internal delegate function to execute transformation logic (ITMO): - * - Input (read) - * - Transform + Middleware - * - Output (write) - * - * @param {Options} options - The configuration for a transformation. - * @param {Function} read - The reader function. - * @param {Function} [middleware] - The middleware to apply. - * @param {Function} write - The writer functions. - * @example - * var options = {...}; - * var middleware = async (obj) => { - * ... - * }; - * const result = await itmo(options, readYaml, middleware, writeJson); - * @private - */ -async function itmo(options, read, middleware, write) { - const ensuredMiddleware = ensureMiddleware(middleware); - let json = await read(options); - json = await ensuredMiddleware(json); - return await write(json, options); -} - -// //////////////////////////////////////////////////////////////////////////// -// TRANSFORMATION METHODS (DELEGATES) -// //////////////////////////////////////////////////////////////////////////// - -/** - * Convert YAML to JS. - * - * @param {Options} options - The configuration for a transformation. - * @param {Function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is `function(object)`. - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise.} Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. - * @throws {Error} Will throw plain error when writing to JS destination failed due to any reason. - * @example - * import { transformer } from 'jy-transform'; - * const options = {...}; - * var middleware = function (obj) { - * ... - * }; - * yamlToJs(options, middleware); - * @see itmo - * @private - */ -function yamlToJs(options, middleware) { - return itmo(options, readYaml, middleware, writeJs); -} - -/** - * Convert YAML to JSON. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.yamlToJson(options, middleware); - * @see itmo - * @private - */ -function yamlToJson(options, middleware) { - return itmo(options, readYaml, middleware, writeJson); -} - -/** - * Convert JS to YAML. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsToYaml(options, middleware); - * @see itmo - * @private - */ -function jsToYaml(options, middleware) { - return itmo(options, readJs, middleware, writeYaml); -} - -/** - * Convert JSON to JS. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JS destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsonToJs(options, middleware); - * @see itmo - * @private - */ -function jsonToJs(options, middleware) { - return itmo(options, readJs, middleware, writeJs); -} - -/** - * Convert JS to JSON. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsToJson(options, middleware); - * @see itmo - * @private - */ -function jsToJson(options, middleware) { - return itmo(options, readJs, middleware, writeJson); -} - -/** - * Convert YAML to YAML. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to YAML destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.yamlToYaml(options, middleware); - * @see itmo - * @private - */ -function yamlToYaml(options, middleware) { - return itmo(options, readYaml, middleware, writeYaml); -} - -/** - * Convert JSON to JSON. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise} - Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} - Will throw this error when the passed `middleware` - * is not type of `Function`. - * @throws {Error} - Will throw plain error when writing to JSON destination failed due to any reason. - * @example - * var logger = ...; - * var options = {...}; - * var middleware = function (obj) { - * ... - * }; - * var Transformer = require('jy-transform'); - * var transformer = new Transformer(logger); - * transformer.jsonToJson(options, middleware); - * @see itmo - * @private - */ -function jsonToJson(options, middleware) { - return itmo(options, readJs, middleware, writeJson); -} - -/** - * Convert JS to JS. - * - * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept - * the JS object for manipulation. The function signature is: - * ``` - * function(object) - * ``` - * **NOTE:** the Promise has to return the processed JS object! - * @returns {Promise.} Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. - * @throws {Error} Will throw plain error when writing to JS destination failed due to any reason. - * @example - * var options = {...}; - * var middleware = async (json) { - * ... - * }; - * jsToJs(options, middleware); - * @see itmo - * @private - */ -function jsToJs(options, middleware) { - return itmo(options, readJs, middleware, writeJs); -} - -/** - * A transformation name to internal function mapping. - * - * @namespace - * @property {Function} yaml2js - The transformation function for YAML ⇒ JS. - * @property {Function} yaml2json - The transformation function for YAML ⇒ JSON. - * @property {Function} yaml2yaml - The transformation function for YAML ⇒ YAML. - * @property {Function} json2yaml - The transformation function for JSON ⇒ YAML. - * @property {Function} json2js - The transformation function for JSON ⇒ JS. - * @property {Function} json2json - The transformation function for JSON ⇒ JSON. - * @property {Function} js2yaml - The transformation function for JS ⇒ YAML. - * @property {Function} js2json - The transformation function for JS ⇒ JSON. - * @property {Function} js2js - The transformation function for JS ⇒ JS. - * @private - */ -const transformations = { - yaml2js: yamlToJs, - yaml2json: yamlToJson, - yaml2yaml: yamlToYaml, - json2yaml: jsToYaml, - json2js: jsonToJs, - json2json: jsonToJson, - js2yaml: jsToYaml, - js2json: jsToJson, - js2js: jsToJs -}; - -// /** -// * Constructs the `Transformer` with options and an (optional) logger. -// * -// * @param {(logger|cli|console)} [logger=console] - Logger instance. -// * @returns {Transformer} - The instance. -// * @constructor -// * @class This class provides all methods usable to handle YAML, JSON and JS and -// * their transformations. -// * @example -// * var logger = ...; -// * var Transformer = require('jy-transform'); -// * var transformer = new Transformer(logger); -// */ -// export class Transformer { - -// const logger = new LogWrapper(logger); -// const writer = new Writer(logger); -// const reader = new Reader(logger); - // //////////////////////////////////////////////////////////////////////////// // PUBLIC API // //////////////////////////////////////////////////////////////////////////// /** * The entry method for all transformation accepting a configuration object and - * an (optional) middleware function. + * an (optional) middleware function. It executes the transformation logic: + * 1. Input (read) + * 2. Transform [ + Middleware] + * 3. Output (write). * * @param {Options} options - The configuration for a transformation. - * @param {function} [middleware] - This middleware Promise can be used to intercept + * @param {Function} [middleware] - This middleware Promise can be used to intercept * the JSON object for altering the passed JSON, the function signature is: * ``` * function(json) * ``` *

- * **NOTE:** the Promise has to return the processed JSON! + * **NOTE:** the Promise has to return the processed JSON. * @returns {Promise.} Containing the transformation result as message (e.g. to be logged by caller). * @throws {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. * @throws {Error} Will throw plain error when writing to file failed due to any reason. @@ -364,24 +36,34 @@ const transformations = { * import { transform } from 'jy-transform'; * const options = {...}; * const middleware = async (json) { - * json.myproperty = 'new value'; - * return json; + * json.myproperty = 'new value'; + * return json; * }; * + * // ---- Promise style: + * * transform(options, middleware) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); + * .then(function (msg){ + * console.log(msg); + * }) + * .catch(function (err) { + * console.error(err.stack); + * }); + * + * // ---- async/await style: + * try { + * const msg = await transform(options, middleware); + * console.log(msg); + * } catch (err) { + * console.error(err.stack); + * }; */ export async function transform(options, middleware) { logger.debug('transform'); - const ensuredOptions = await Joi.validate(options, transformerOptionsSchema); - const transformation = await createTransformation(ensuredOptions); - logger.debug('Calling transformation: ' + transformation); - return await transformations[transformation](ensuredOptions, middleware); + let object = await read(options); + const ensuredMiddleware = ensureMiddleware(middleware); + object = await ensuredMiddleware(object); + return await write(object, options); } export default { diff --git a/src/writer.js b/src/writer.js index cd279af..55a11d0 100644 --- a/src/writer.js +++ b/src/writer.js @@ -15,7 +15,7 @@ import { UTF8 } from './constants'; import Joi from './validation/joi-extensions'; -import { transformerOptionsSchema } from './validation/options-schema'; +import { writerOptionsSchema } from './validation/options-schema'; /** * @module jy-transform:writer @@ -233,23 +233,25 @@ function writeToStream(object, dest, target, resolve, reject) { * }); */ async function writeYaml(object, options) { - const assertedOptions = await Joi.validate(options, transformerOptionsSchema); return new Promise((resolve, reject) => { let yaml; try { - yaml = jsYaml.safeDump(object, { indent: assertedOptions.indent, noRefs: true }); + yaml = jsYaml.safeDump(object, { indent: options.indent, noRefs: true }); } catch (err) { - err.message = 'Could not write YAML file \'' + assertedOptions.dest + '\', cause: ' + err.message; + err.message = 'Could not write YAML to \'' + options.dest + '\', cause: ' + err.message; reject(err); return; } - if (typeof assertedOptions.dest === 'string') { // file - writeToFile(yaml, assertedOptions.dest, TYPE_YAML, resolve, reject, assertedOptions.force); - } else if (isStream.writable(assertedOptions.dest)) { // stream - writeToStream(yaml, assertedOptions.dest, TYPE_YAML, resolve, reject); + if (typeof options.dest === 'string') { // file + writeToFile(yaml, options.dest, TYPE_YAML, resolve, reject, options.force); + } else if (isStream.writable(options.dest)) { // stream + writeToStream(yaml, options.dest, TYPE_YAML, resolve, reject); } else { // object + console.log('YAAAMLLLLLL: ' + yaml) + console.log('options.dest1: ' + JSON.stringify(options.dest)) options.dest = yaml; + //options.dest.text = yaml; resolve('Writing YAML to options.dest successful.'); } }); @@ -318,16 +320,15 @@ async function writeYaml(object, options) { * }); */ async function writeJson(object, options) { - const assertedOptions = await Joi.validate(options, transformerOptionsSchema); return new Promise((resolve, reject) => { - if (typeof assertedOptions.dest === 'string') { // file - writeToFile(serializeJsToJsonString(object, assertedOptions.indent), assertedOptions.dest, TYPE_JSON, - resolve, reject, assertedOptions.force); - } else if (isStream.writable(assertedOptions.dest)) { // stream - writeToStream(serializeJsToJsonString(object, assertedOptions.indent), assertedOptions.dest, + if (typeof options.dest === 'string') { // file + writeToFile(serializeJsToJsonString(object, options.indent), options.dest, TYPE_JSON, + resolve, reject, options.force); + } else if (isStream.writable(options.dest)) { // stream + writeToStream(serializeJsToJsonString(object, options.indent), options.dest, TYPE_JSON, resolve, reject); } else { // object - options.dest = serializeJsToJsonString(object, assertedOptions.indent); + options.dest = serializeJsToJsonString(object, options.indent); resolve('Writing JSON to options.dest successful.'); } }); @@ -398,27 +399,26 @@ async function writeJson(object, options) { */ async function writeJs(object, options) { //logger.debug('OPTIONS BEFORE ASSERTING IN writeJs:::' + JSON.stringify(options)); - const assertedOptions = await Joi.validate(options, transformerOptionsSchema); return new Promise((resolve, reject) => { - if (typeof assertedOptions.dest === 'string') { // file - serializeJsToString(object, assertedOptions.indent, assertedOptions.exports) + if (typeof options.dest === 'string') { // file + serializeJsToString(object, options.indent, options.exports) .then((data) => { - writeToFile(data, assertedOptions.dest, TYPE_JS, resolve, reject, assertedOptions.force); + writeToFile(data, options.dest, TYPE_JS, resolve, reject, options.force); }) .catch(reject); - } else if (isStream.writable(assertedOptions.dest)) { // stream - serializeJsToString(object, assertedOptions.indent, assertedOptions.exports) + } else if (isStream.writable(options.dest)) { // stream + serializeJsToString(object, options.indent, options.exports) .then((data) => { - writeToStream(data, assertedOptions.dest, TYPE_JS, resolve, reject); + writeToStream(data, options.dest, TYPE_JS, resolve, reject); }) .catch(reject); } else { // object let msg; - if (assertedOptions.exports) { - assertedOptions.dest[assertedOptions.exports] = object; - msg = 'Writing JS to options.dest.' + assertedOptions.exports + ' successful.'; + if (options.exports) { + options.dest[options.exports] = object; + msg = 'Writing JS to options.dest.' + options.exports + ' successful.'; } else { - Object.assign(assertedOptions.dest, object); + Object.assign(options.dest, object); msg = 'Writing JS to options.dest successful.'; } resolve(msg); @@ -429,18 +429,27 @@ async function writeJs(object, options) { /** * TODO: doc me. * + * @param {Object} options - The source object to write. * @param {Options} options - The write options. * @returns {Promise.} Resolves with write success message. * @public */ -export async function write(options) { - switch (options.target) { +export async function write(object, options) { + const assertedOptions = await Joi.validate(options, writerOptionsSchema); + console.log('options after validation 1: ' + JSON.stringify(options)) + // HINT: we have to use the original options object because the caller must not loose the reference to options.dest, + // so we copy everything here except the assertedOptions.dest (joi does not return the original reference)! + Object.assign(options, { target: assertedOptions.target }, { exports: assertedOptions.exports }, + { indent: assertedOptions.indent }, { force: assertedOptions.force }); + console.log('options after validation 2: ' + JSON.stringify(options)) + assertedOptions.dest = options.dest; + switch (assertedOptions.target) { case TYPE_JS: - return await writeJs(options); + return await writeJs(object, options); case TYPE_JSON: - return await writeJson(options); + return await writeJson(object, options); case TYPE_YAML: - return await writeYaml(options); + return await writeYaml(object, options); default: // TODO better handling throw new Error('should not happen!'); } diff --git a/test/unit/test-index.js b/test/unit/test-index.js index 301002f..967c887 100644 --- a/test/unit/test-index.js +++ b/test/unit/test-index.js @@ -31,32 +31,26 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - index - ', () => { ); describe('Exported Reader', () => - it('should be an existing Reader object', () => { - expect.assertions(7); + it('should be an existing Reader object with read function', () => { + expect.assertions(5); expect(index.Reader).toBeDefined(); expect(index.Reader).toBeInstanceOf(Object); - expect(Object.keys(index.Reader)).toHaveLength(2); - const { readJs, readYaml } = index.Reader; - expect(readJs).toBeDefined(); - expect(readJs).toBeInstanceOf(Function); - expect(readYaml).toBeDefined(); - expect(readYaml).toBeInstanceOf(Function); + expect(Object.keys(index.Reader)).toHaveLength(1); + const { read } = index.Reader; + expect(read).toBeDefined(); + expect(read).toBeInstanceOf(Function); }) ); describe('Exported Writer', () => - it('should be an existing Writer object', () => { - expect.assertions(9); + it('should be an existing Writer object with write function', () => { + expect.assertions(5); expect(index.Writer).toBeDefined(); expect(index.Writer).toBeInstanceOf(Object); - expect(Object.keys(index.Writer)).toHaveLength(3); - const { writeJs, writeJson, writeYaml } = index.Writer; - expect(writeJs).toBeDefined(); - expect(writeJs).toBeInstanceOf(Function); - expect(writeJson).toBeDefined(); - expect(writeJson).toBeInstanceOf(Function); - expect(writeYaml).toBeDefined(); - expect(writeYaml).toBeInstanceOf(Function); + expect(Object.keys(index.Writer)).toHaveLength(1); + const { write } = index.Writer; + expect(write).toBeDefined(); + expect(write).toBeInstanceOf(Function); }) ); diff --git a/test/unit/test-reader.js b/test/unit/test-reader.js index fdad8a3..7423b1f 100644 --- a/test/unit/test-reader.js +++ b/test/unit/test-reader.js @@ -1,6 +1,6 @@ import YAMLException from 'js-yaml/lib/js-yaml/exception'; import fs from 'fs'; -import { readJs, readYaml } from '../../src/reader'; +import { read } from '../../src/reader'; import { logger } from '../logger'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; import { @@ -20,40 +20,37 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { * Assert a `Error` properties for a given reader function. * * @param {Object} options - The options which potentially produce the error. - * @param {Function} readerFunc - The function to call for assertion. * @param {Object} [match={name:'Error'}] - The propertie(s) error should contain. * @private */ - const expectReaderError = (options, readerFunc, match = { name: 'Error' }) => { + const expectReaderError = (options, match = { name: 'Error' }) => { expect.assertions(1); - return expect(readerFunc(options)).rejects.toMatchObject(match); + return expect(read(options)).rejects.toMatchObject(match); }; /** * Assert an `Error` for a given reader function. * * @param {Object} options - The options which potentially produce the error. - * @param {Function} readerFunc - The function to call for assertion. * @param {Error} [match=Error'] - The error type to match. * @private */ - const expectReaderErrorByType = (options, readerFunc, match = Error) => { + const expectReaderErrorByType = (options, match = Error) => { expect.assertions(1); - return expect(readerFunc(options)).rejects.toBeInstanceOf(match); + return expect(read(options)).rejects.toBeInstanceOf(match); }; /** * Assert a successful reading of input. * * @param {Object} options - The options. - * @param {Function} readerFunc - The function to call for assertion. * @param {string} key - The function to call for assertion. * @param {*} expectedValue - The expected value for `key`. * @private */ - const expectReaderSuccess = async (options, readerFunc, key, expectedValue) => { + const expectReaderSuccess = async (options, key, expectedValue) => { expect.assertions(2); - const json = await readerFunc(options); + const json = await read(options); expect(json).toBeDefined(); expect(json[key]).toBe(expectedValue); }; @@ -68,11 +65,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { src: './test/data/test-data.js', imports: '', }; - return expectReaderError(options, readJs, { name: 'ValidationError', isJoi: true }); + return expectReaderError(options, { name: 'ValidationError', isJoi: true }); }); it('should read JS from file', async () => - await expectReaderSuccess({ src: './test/data/test-data.js' }, readJs, 'myproperty', 'old value') + await expectReaderSuccess({ src: './test/data/test-data.js' }, 'myproperty', 'old value') ); it('should read JS from file with options.imports == \'' + exports + '\'', async () => { @@ -81,7 +78,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { src: './test/data/test-imports.js', imports: exports, }; - const json = await readJs(options); + const json = await read(options); expect(json).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(json, exports)).toBeFalsy(); expect(Object.prototype.hasOwnProperty.call(json, 'bar')).toBeFalsy(); @@ -97,7 +94,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { imports: exports, origin: TYPE_JS, }; - const json = await readJs(options); + const json = await read(options); expect(json).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(json, exports)).toBeFalsy(); expect(Object.prototype.hasOwnProperty.call(json, 'bar')).toBeFalsy(); @@ -111,7 +108,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { src: './test/data/test-imports.js', imports: invalidIdentifier, }; - return expectReaderError(options, readJs, { name: 'ValidationError', isJoi: true }); + return expectReaderError(options, { name: 'ValidationError', isJoi: true }); }); it('should reject read JS from file with Error on non-existent identifier for options.imports: ' @@ -120,11 +117,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { src: './test/data/test-imports.js', imports: exportsNotExists, }; - return expectReaderErrorByType(options, readJs, Error); + return expectReaderErrorByType(options, Error); }); it('should read JSON from file', async () => - await expectReaderSuccess({ src: './test/data/test-data.json' }, readJs, 'myproperty', 'old value') + await expectReaderSuccess({ src: './test/data/test-data.json' }, 'myproperty', 'old value') ); it('should read JS from object', async () => { @@ -133,7 +130,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { foo: 'bar', }, }; - await expectReaderSuccess(options, readJs, 'foo', 'bar'); + await expectReaderSuccess(options, 'foo', 'bar'); }); it('should read JS from Object with options.imports == \'' + exports + '\'', async () => { @@ -147,7 +144,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { }, imports: exports, }; - const json = await readJs(options); + const json = await read(options); expect(json).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(json, exports)).toBeFalsy(); expect(Object.prototype.hasOwnProperty.call(json, 'bar')).toBeTruthy(); @@ -167,7 +164,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { }, imports: invalidIdentifier, }; - return expectReaderError(options, readJs, { name: 'ValidationError', isJoi: true }); + return expectReaderError(options, { name: 'ValidationError', isJoi: true }); }); it('should reject read (deep) JS from file with Error on non-existent identifier for options.imports: ' @@ -181,28 +178,28 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { }, imports: exportsNotExists, }; - return expectReaderErrorByType(options, readJs, Error); + return expectReaderErrorByType(options, Error); }); it('should read JSON from stream', async () => await expectReaderSuccess({ origin: TYPE_JSON, src: fs.createReadStream('./test/data/test-data.json') - }, readJs, 'myproperty', 'old value') + }, 'myproperty', 'old value') ); it('should read corrupted JSON from file path and fail by SyntaxError', () => { return expectReaderErrorByType({ origin: TYPE_JSON, src: './test/data/test-data-corrupted.json' - }, readJs, SyntaxError); + }, SyntaxError); }); it('should read invalid JSON from file path and fail by SyntaxError', () => { return expectReaderErrorByType({ origin: TYPE_JSON, src: './test/data/test-data-wrong-syntax.json' - }, readJs, SyntaxError); + }, SyntaxError); }); it('should read corrupted JSON from stream and fail by SyntaxError', () => { @@ -210,67 +207,66 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { origin: TYPE_JSON, src: fs.createReadStream('./test/data/test-data-corrupted.json'), }; - return expectReaderErrorByType(options, readJs, SyntaxError); + return expectReaderErrorByType(options, SyntaxError); }); it('should read invalid JSON from stream and fail by SyntaxError', () => { return expectReaderErrorByType({ origin: TYPE_JSON, src: fs.createReadStream('./test/data/test-data-wrong-syntax.json') - }, readJs, SyntaxError); + }, SyntaxError); }); it('should fail JS(ON) read by missing options', () => { - return expectReaderError(null, readJs, { name: 'ValidationError', isJoi: true }); + return expectReaderError(null, { name: 'ValidationError', isJoi: true }); }); it('should fail JS(ON) read by missing options.src', () => { - return expectReaderError({}, readJs, { name: 'ValidationError', isJoi: true }); + return expectReaderError({}, { name: 'ValidationError', isJoi: true }); }); }); describe('Testing Reader.readYaml(...)', () => { it('should read YAML from file', async () => - await expectReaderSuccess({ src: './test/data/test-data.yaml' }, readYaml, 'myproperty', 'old value') + await expectReaderSuccess({ src: './test/data/test-data.yaml' }, 'myproperty', 'old value') ); it('should read JS from object', async () => - await expectReaderSuccess({ src: { test: 'value' } }, readYaml, 'test', 'value') + await expectReaderSuccess({ src: { test: 'value' } }, 'test', 'value') ); it('should read YAML from stream', async () => await expectReaderSuccess({ origin: TYPE_YAML, src: fs.createReadStream('./test/data/test-data.yaml'), - }, readYaml, 'myproperty', 'old value') + }, 'myproperty', 'old value') ); it('should read invalid YAML from file path and fail by YAMLException', () => { - return expectReaderErrorByType({ src: './test/data/test-data-wrong-syntax.yaml' }, readYaml, - YAMLException); + return expectReaderErrorByType({ src: './test/data/test-data-wrong-syntax.yaml' }, YAMLException); }); it('should read invalid YAML from stream and fail by YAMLException', () => { return expectReaderErrorByType({ origin: TYPE_YAML, src: fs.createReadStream('./test/data/test-data-wrong-syntax.yaml'), - }, readYaml, YAMLException); + }, YAMLException); }); it('should fail YAML read by missing input options', () => { - return expectReaderError(null, readYaml, { name: 'ValidationError', isJoi: true }); + return expectReaderError(null, { name: 'ValidationError', isJoi: true }); }); it('should fail YAML read by missing options.src', () => { - return expectReaderError({}, readYaml, { name: 'ValidationError', isJoi: true }); + return expectReaderError({}, { name: 'ValidationError', isJoi: true }); }); it('should fail read YAML from configured directory source', async () => - await expectReaderError({ src: './test/data' }, readYaml, { name: 'ValidationError', isJoi: true }) + await expectReaderError({ src: './test/data' }, { name: 'ValidationError', isJoi: true }) ); it('should fail read YAML from file', async () => - await expectReaderError({ src: './test/data/non-existing.yml' }, readYaml, { + await expectReaderError({ src: './test/data/non-existing.yml' }, { name: 'ValidationError', isJoi: true }) diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js index 9d2d52f..317a776 100644 --- a/test/unit/test-writer.js +++ b/test/unit/test-writer.js @@ -96,9 +96,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { * @param {Object} [match={name:'Error'}] - The propertie(s) error should contain. * @private */ - const expectWriteError = (options, match = { name: 'Error' }) => { + const expectWriteError = (object, options, match = { name: 'Error' }) => { expect.assertions(1); - return expect(write(options)).rejects.toMatchObject(match); + return expect(write(object, options)).rejects.toMatchObject(match); }; /** @@ -108,16 +108,16 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { * @param {Error} [match=Error'] - The error type to match. * @private */ - const expectWriteErrorByType = (options, match = Error) => { + const expectWriteErrorByType = (object, options, match = Error) => { expect.assertions(1); - return expect(write(options)).rejects.toBeInstanceOf(match); + return expect(write(object, options)).rejects.toBeInstanceOf(match); }; describe('Testing writeJs(...)', () => { it('should write JS to file', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.js'; - const msg = await write({ src: JSON_CONTENT, dest: file }); + const msg = await write(JSON_CONTENT, { dest: file }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -125,7 +125,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JS to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.js'; - const msg = write({ src: JSON_CONTENT, dest: fs.createWriteStream(file) }); + const msg = await write(JSON_CONTENT, { dest: fs.createWriteStream(file) }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -134,12 +134,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { expect.assertions(4); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-exports-identifier.js'; const options = { - src: JSON_CONTENT, target: TYPE_JS, dest: fs.createWriteStream(file), exports: 'test', }; - const msg = write(options); + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(file); // eslint-disable-next-line import/no-unresolved, global-require @@ -148,33 +147,30 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { expect(json.test).toBe('value'); }); - it('should write JS to file and fail by invalid exports identifier (\'#3/-\')', () => { + it('should fail writing JS to file by invalid exports identifier (\'#3/-\')', () => { const options = { - src: JSON_CONTENT, dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js', exports: '#3/-', }; - return expectWriteError(options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'#3/-\')', () => { const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js'; const options = { - src: JSON_CONTENT, dest: fs.createWriteStream(file), exports: '#3/-', }; - return expectWriteError(options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'if\')', () => { const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js'; const options = { - src: JSON_CONTENT, dest: fs.createWriteStream(file), exports: 'if', }; - return expectWriteError(options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by provoked error', () => { @@ -186,7 +182,6 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { done(); }; return expectWriteErrorByType(JSON_CONTENT, { - src: JSON_CONTENT, target: TYPE_JS, dest: errorThrowingStream, }, Error); @@ -194,8 +189,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JS to JS object', async () => { expect.assertions(4); - const options = { src: JSON_CONTENT, dest: {} }; - const msg = await write(options); + const options = { dest: {} }; + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(options.dest, 'test')).toBeTruthy(); @@ -204,22 +199,20 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should reject to write JS to JS object with options.exports == \'\'', async () => { const options = { - src: JSON_CONTENT, dest: {}, exports: '', }; - return expectWriteError(options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); const exports = 'foo'; it('should write JS to JS object with options.exports == \'' + exports + '\'', async () => { expect.assertions(5); const options = { - src: JSON_CONTENT, dest: {}, exports, }; - const msg = await write(options); + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(options.dest, exports)).toBeTruthy(); @@ -230,20 +223,18 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { const invalidIdentifier = '#3/-'; it('should reject write JS with Error on invalid identifier for options.exports: ' + invalidIdentifier, () => { const options = { - src: JSON_CONTENT, dest: {}, exports: invalidIdentifier, }; - return expectWriteError(options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS with Error on missing destination', () => { - return expectWriteError({ src: JSON_CONTENT }, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, {}, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS to file by invalid file path', (done) => { const options = { - src: JSON_CONTENT, dest: WRITER_TEST_BASE_DIR + '/<>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + @@ -276,10 +267,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JSON to file', async () => { expect.assertions(2); const options = { - src: JSON_CONTENT, dest: WRITER_TEST_BASE_DIR + '/test-data-by-json-to-file.json' }; - const msg = await write(options); + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(options.dest); }); @@ -287,8 +277,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JSON to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-json-stream.json'; - const msg = await write({ - src: JSON_CONTENT, + const msg = await write(JSON_CONTENT, { target: TYPE_JSON, dest: fs.createWriteStream(file), }); @@ -299,19 +288,14 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JS to JS object', async () => { expect.assertions(4); const options = { - src: JSON_CONTENT, dest: {}, }; - const msg = await write(options); + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); - const result = JSON.parse(options.dest); - expect(Object.prototype.hasOwnProperty.call(result, 'test')).toBeDefined(); - expect(result.test).toBe('value'); - }); - - it('should reject with Error on missing destination', () => { - return expectWriteError({ src: JSON_CONTENT }, { name: 'ValidationError', isJoi: true }); + //const result = JSON.parse(options.dest); // TODO is this correct? + expect(Object.prototype.hasOwnProperty.call(options.dest, 'test')).toBeDefined(); + expect(options.dest.test).toBe('value'); }); }); @@ -319,8 +303,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write YAML to file', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.yaml'; - const msg = await write({ - src: JSON_CONTENT, + const msg = await write(JSON_CONTENT, { dest: file }); expect(msg).toBeDefined(); @@ -330,8 +313,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write YAML to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.yaml'; - const msg = await write({ - src: JSON_CONTENT, + const msg = await write(JSON_CONTENT, { target: TYPE_YAML, dest: fs.createWriteStream(file), }); @@ -342,11 +324,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write stringified YAML to JS object', async () => { expect.assertions(3); const options = { - src: JSON_CONTENT, target: TYPE_YAML, - dest: {} + dest: { XXXX: 'YYXXXX' }, }; - const msg = await write(options); + const msg = await write(JSON_CONTENT, options); + + console.log('options.dest 2: ' + JSON.stringify(options.dest)) expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); const key = Object.keys(JSON_CONTENT)[0]; @@ -357,20 +340,18 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { const invalidYamlJson = () => { }; return expectWriteError(invalidYamlJson, { - src: invalidYamlJson, dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file-invalid.yaml' }, { name: 'YAMLException' }); }); it('should reject with Error on missing destination', () => { - return expectWriteError({ src: JSON_CONTENT }, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JSON_CONTENT, {}, { name: 'ValidationError', isJoi: true }); }); }); describe('Testing force overwrite file', () => { it('should reject when options.dest is a directory', () => { - return expectWriteErrorByType({ - src: JSON_CONTENT, + return expectWriteErrorByType(JSON_CONTENT, { dest: './test/data', target: TYPE_YAML }, Error); @@ -381,26 +362,24 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { expect.assertions(13); const dest = WRITER_TEST_BASE_DIR + '/test-data-file-overwriting.yaml'; let options = { - src: JSON_CONTENT, indent: 4, dest, }; const asyncFunctions = [ async () => { - const msg = await write(options); + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(dest); return 'overwrite test #1 should initially write YAML to file \'' + dest + '\''; }, async () => { options = { - src: JSON_CONTENT, indent: 4, dest, force: true }; - const msg = await write(options); + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(dest); await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); @@ -408,12 +387,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }, async () => { options = { - src: JSON_CONTENT, indent: 4, dest, force: false, }; - const msg = await write(options); + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); return 'overwrite test #3 shouldn\'t overwrite existing YAML file \'' + dest + @@ -421,12 +399,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }, async () => { options = { - src: JSON_CONTENT, indent: 4, dest, force: true }; - const msg = await write(options); + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); @@ -434,11 +411,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }, async () => { options = { - src: JSON_CONTENT, indent: 4, dest, }; - const msg = await write(options); + const msg = await write(JSON_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); From 8fee655b79e51ad2089ef7ea7b9557ab0bc0b877 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 30 Jun 2017 00:59:50 +0200 Subject: [PATCH 11/58] Tests at 100%, further refactoring --- .jestrc.js | 9 +- API-PRIVATE.md | 883 ++++++------------ API-PUBLIC.md | 554 +++-------- readme/CHANGELOG.md => CHANGELOG.md | 25 +- README.md | 406 +++----- bin/create-readme.sh | 26 +- index.js | 15 +- jyt | 2 +- package.json | 2 +- readme/BADGES.md | 40 +- readme/DOCUMENTATION.md | 250 ++--- readme/QA.md | 22 + readme/TOC.md | 3 - src/{jyt.js => cli.js} | 0 src/debug-log.js | 6 + src/middleware.js | 70 -- src/reader.js | 177 ++-- src/transformer.js | 36 +- src/type-definitions.js | 50 +- src/validation/joi-extensions-file-helper.js | 3 - src/validation/options-schema-helper.js | 53 +- src/validation/options-schema.js | 25 +- src/writer.js | 122 +-- test/data/writable-test-dummy.txt | 0 test/data/writable-test-dummy.yaml | 0 test/logger.js | 2 +- test/unit/test-index.js | 65 +- test/unit/test-middleware.js | 117 --- test/unit/test-reader.js | 62 +- test/unit/test-transformer.js | 39 +- test/unit/test-writer.js | 99 +- .../validation/test-options-schema-helper.js | 92 +- test/unit/validation/test-options-schema.js | 615 ++++++------ 33 files changed, 1500 insertions(+), 2370 deletions(-) rename readme/CHANGELOG.md => CHANGELOG.md (78%) create mode 100644 readme/QA.md delete mode 100644 readme/TOC.md rename src/{jyt.js => cli.js} (100%) delete mode 100644 src/middleware.js create mode 100644 test/data/writable-test-dummy.txt create mode 100644 test/data/writable-test-dummy.yaml delete mode 100644 test/unit/test-middleware.js diff --git a/.jestrc.js b/.jestrc.js index 4f3cdbb..054736f 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -4,7 +4,7 @@ module.exports = { collectCoverageFrom: [ //'lib/**/*.js', 'src/**/*.js', - '!src/jyt.js', // TODO: maybe later! + '!src/cli.js', // TODO: maybe later! '!src/debug-log.js', // TODO: maybe later! 'index.js' ], @@ -25,15 +25,16 @@ module.exports = { //'**/test/unit/test-transformer.js', // '**/test/unit/test-index.js', //'**/test/unit/test-reader.js', - //'**/test/unit/test-writer.js', - // '**/test/unit/validation/test-options-schema.js', + // '**/test/unit/test-writer.js', + //'**/test/unit/validation/test-options-schema.js', '**/test/unit/**/*.js', +//'**/test/unit/validation/test-options-schema-helper.js', // '**/test/test-log-wrapper.js', // '**/test/unit/test-middleware.js', // '**/test/test-index.js', //'/*.js!**/test/functional/util/**', - // '!**/test/*.js', + //'!**/test/*.js', ], // testRegex: '\\/test\\/unit\\/.*|\/\\.js?$/', testEnvironment: 'node', diff --git a/API-PRIVATE.md b/API-PRIVATE.md index c1449d8..fca0540 100644 --- a/API-PRIVATE.md +++ b/API-PRIVATE.md @@ -1,6 +1,49 @@ + + +## TOC + +- [Modules](#modules) +- [Members](#members) +- [jy-transform:jyt ℗](#jy-transformjyt-%E2%84%97) +- [jy-transform:constants](#jy-transformconstants) +- [jy-transform:debug-log ℗](#jy-transformdebug-log-%E2%84%97) +- [jy-transform:middleware ℗](#jy-transformmiddleware-%E2%84%97) +- [jy-transform:reader](#jy-transformreader) +- [jy-transform:transformer](#jy-transformtransformer) +- [jy-transform:type-definitions](#jy-transformtype-definitions) +- [jy-transform:validation:joi-extensions-file-helper ℗](#jy-transformvalidationjoi-extensions-file-helper-%E2%84%97) +- [jy-transform:validation:joi-extensions-identifier-helper ℗](#jy-transformvalidationjoi-extensions-identifier-helper-%E2%84%97) +- [jy-transform:validation:joi-extension ℗](#jy-transformvalidationjoi-extension-%E2%84%97) +- [jy-transform:validation:options-schema-helper : Object ℗](#jy-transformvalidationoptions-schema-helper--codeobjectcode-%E2%84%97) +- [jy-transform:validation:options-schema : Object ℗](#jy-transformvalidationoptions-schema--codeobjectcode-%E2%84%97) +- [jy-transform:writer](#jy-transformwriter) +- [jy-transform:unit:helper-constants : Object ℗](#jy-transformunithelper-constants--codeobjectcode-%E2%84%97) +- [jy-transform:unit:logger : Object ℗](#jy-transformunitlogger--codeobjectcode-%E2%84%97) +- [jy-transform:test-unit:index ℗](#jy-transformtest-unitindex-%E2%84%97) +- [jy-transform:unit-test:test-middleware ℗](#jy-transformunit-testtest-middleware-%E2%84%97) +- [jy-transform:unit-test:test-reader ℗](#jy-transformunit-testtest-reader-%E2%84%97) +- [jy-transform:unit-test:test-transformer ℗](#jy-transformunit-testtest-transformer-%E2%84%97) +- [jy-transform:unit-test:test-writer ℗](#jy-transformunit-testtest-writer-%E2%84%97) +- [jy-transform:test-unit:test-joi-extension-file-helper ℗](#jy-transformtest-unittest-joi-extension-file-helper-%E2%84%97) +- [jy-transform:unit-test:test-joi-extensions-identifier-helper ℗](#jy-transformunit-testtest-joi-extensions-identifier-helper-%E2%84%97) +- [jy-transform:unit-test:test-options-schema-helper ℗](#jy-transformunit-testtest-options-schema-helper-%E2%84%97) +- [jy-transform:unit-test:test-options-schema ℗](#jy-transformunit-testtest-options-schema-%E2%84%97) +- [readJs ⇒ Promise.<Object> ℗](#readjs-%E2%87%92-codepromiseltobjectgtcode-%E2%84%97) +- [readYaml ⇒ Promise.<Object> ℗](#readyaml-%E2%87%92-codepromiseltobjectgtcode-%E2%84%97) +- [read ⇒ Promise.<string>](#read-%E2%87%92-codepromiseltstringgtcode) +- [writeYaml ⇒ Promise.<string> ℗](#writeyaml-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) +- [writeJson ⇒ Promise.<string> ℗](#writejson-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) +- [writeJs ⇒ Promise.<string> ℗](#writejs-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) +- [write ⇒ Promise.<string>](#write-%E2%87%92-codepromiseltstringgtcode) + + + ## Modules

+
jy-transform:jyt
+

The command line interface.

+
jy-transform:constants

Useful constants used for the module and its usage.

@@ -11,9 +54,6 @@
  • JYT_DEBUG: (only ERROR logging via console.error)
  • -
    jy-transform:jyt
    -

    The command line interface.

    -
    jy-transform:middleware

    The middleware ensuring functions module.

    @@ -23,7 +63,7 @@
    jy-transform:transformer

    This module provides the transform functionality for YAML, JS or JSON source to destination mapping.

    -
    jy-transform:type-definitions
    +
    jy-transform:type-definitions

    The type definitions for this module.

    jy-transform:validation:joi-extensions-file-helper
    @@ -43,7 +83,8 @@ values for origin and target depending on the options.src or

    The module options schema used in module:options-validator.

    jy-transform:writer
    -

    This module provides the write functionality for YAML, JS or JSON targets.

    +

    This module provides the write functionality to write JS objects from memory to a JSON/JS/YAML +destination (file, object or stream.Readable).

    jy-transform:unit:helper-constants : Object

    The test suite constants definitions.

    @@ -83,25 +124,96 @@ values for origin and target depending on the options.src or ## Members
    -
    readJsPromise.<Object>
    +
    readJsPromise.<Object>

    Reads the data from a given JS or JSON source.

    -
    readYamlPromise.<Object>
    +
    readYamlPromise.<Object>

    Loads a single YAML source containing document and returns a JS object. NOTE: this function does not understand multi-document sources, it throws exception on those.

    -
    writeYamlPromise.<string>
    +
    readPromise.<string>
    +

    TODO: doc me.

    +
    +
    writeYamlPromise.<string>

    Writes a JS object to a YAML destination.

    -
    writeJsonPromise.<string>
    +
    writeJsonPromise.<string>

    Writes a JS object to a JSON destination.

    -
    writeJsPromise
    +
    writeJsPromise.<string>

    Writes a JS object to a JS destination. The object is prefixed by module.exports[.${options.exports}] =.

    +
    writePromise.<string>
    +

    TODO: doc me.

    +
    + + +## jy-transform:jyt ℗ +The command line interface. + +**Access:** private + +* [jy-transform:jyt](#module_jy-transform_jyt) ℗ + * [~usage](#module_jy-transform_jyt..usage) : string ℗ + * [~packagePath](#module_jy-transform_jyt..packagePath) : string ℗ + * [~options](#module_jy-transform_jyt..options) : Object ℗ + * [~error(err)](#module_jy-transform_jyt..error) ℗ + * [~main(args, cliOptions)](#module_jy-transform_jyt..main) ℗ + + + +### jy-transform:jyt~usage : string ℗ +How to use the CLI. + +**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + + +### jy-transform:jyt~packagePath : string ℗ +The path to package.json. + +**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + + +### jy-transform:jyt~options : Object ℗ +The options description for parsing the command line input, must be an object with opts defined like: +``` +long_tag: [short_tag, description, value_type, default_value]; +``` + +**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + + +### jy-transform:jyt~error(err) ℗ +Prints the error to console and exit with 1. + +**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| err | string | Error | The error to print. | + + + +### jy-transform:jyt~main(args, cliOptions) ℗ +The main entry callback. When calling `cli.main()` this receives the `options` +given on CLI, then does the transformation with these options and finally, it +prints the result to the CLI. + +**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| args | Array | The first mandatory argument is the input file (`args[0]`), the second (optional) argument is the output file (`args[1]`). | +| cliOptions | module:type-definitions~Options | The options provided via CLI. | + ## jy-transform:constants @@ -115,7 +227,6 @@ Useful constants used for the module and its usage. * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string * [~TYPE_JSON](#module_jy-transform_constants..TYPE_JSON) : string * [~TYPE_JS](#module_jy-transform_constants..TYPE_JS) : string - * [~TYPES](#module_jy-transform_constants..TYPES) : Array.<string> * [~TYPE_MAP](#module_jy-transform_constants..TYPE_MAP) : Object * [~DEFAULT_INDENT](#module_jy-transform_constants..DEFAULT_INDENT) : number * [~MIN_INDENT](#module_jy-transform_constants..MIN_INDENT) : number @@ -128,16 +239,6 @@ Useful constants used for the module and its usage. * [~DEST_DESCRIPTION](#module_jy-transform_constants..DEST_DESCRIPTION) : string * [~DEFAULT_JS_IMPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_IMPORTS_IDENTIFIER) : string * [~DEFAULT_JS_EXPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_EXPORTS_IDENTIFIER) : string - * [~YAML_TO_JS](#module_jy-transform_constants..YAML_TO_JS) : string - * [~YAML_TO_JSON](#module_jy-transform_constants..YAML_TO_JSON) : string - * [~JS_TO_YAML](#module_jy-transform_constants..JS_TO_YAML) : string - * [~JSON_TO_YAML](#module_jy-transform_constants..JSON_TO_YAML) : string - * [~JSON_TO_JS](#module_jy-transform_constants..JSON_TO_JS) : string - * [~JS_TO_JSON](#module_jy-transform_constants..JS_TO_JSON) : string - * [~YAML_TO_YAML](#module_jy-transform_constants..YAML_TO_YAML) : string - * [~JSON_TO_JSON](#module_jy-transform_constants..JSON_TO_JSON) : string - * [~JS_TO_JS](#module_jy-transform_constants..JS_TO_JS) : string - * [~TRANSFORMATIONS](#module_jy-transform_constants..TRANSFORMATIONS) : Array.<string> @@ -189,13 +290,6 @@ The 'json' type constant. ### jy-transform:constants~TYPE_JS : string The 'js' type constant. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~TYPES : Array.<string> -The type constants assembled in an array: `[ 'yaml', 'json', 'js' ]`. - **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) **Access:** public @@ -284,76 +378,6 @@ module.exports.foo = {...}; // here 'foo' is the identifier for an object to rea ### jy-transform:constants~DEFAULT_JS_EXPORTS_IDENTIFIER : string The `dest` exports identifier value to write. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~YAML_TO_JS : string -The transformation direction YAML ⇒ JS. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~YAML_TO_JSON : string -The transformation direction YAML ⇒ JSON. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JS_TO_YAML : string -The transformation direction JS ⇒ YAML. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JSON_TO_YAML : string -The transformation direction JSON ⇒ YAML. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JSON_TO_JS : string -The transformation direction JSON ⇒ JS. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JS_TO_JSON : string -The transformation direction JS ⇒ JSON. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~YAML_TO_YAML : string -The transformation direction YAML ⇒ YAML. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JSON_TO_JSON : string -The transformation direction JSON ⇒ JSON. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JS_TO_JS : string -The transformation direction JS ⇒ JS. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~TRANSFORMATIONS : Array.<string> -The transformation directions. - **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) **Access:** public @@ -371,71 +395,6 @@ DEBUG function. **Kind**: inner constant of [jy-transform:debug-log](#module_jy-transform_debug-log) **Access:** protected - - -## jy-transform:jyt ℗ -The command line interface. - -**Access:** private - -* [jy-transform:jyt](#module_jy-transform_jyt) ℗ - * [~usage](#module_jy-transform_jyt..usage) : string ℗ - * [~packagePath](#module_jy-transform_jyt..packagePath) : string ℗ - * [~options](#module_jy-transform_jyt..options) : Object ℗ - * [~error(err)](#module_jy-transform_jyt..error) ℗ - * [~main(args, cliOptions)](#module_jy-transform_jyt..main) ℗ - - - -### jy-transform:jyt~usage : string ℗ -How to use the CLI. - -**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private - - -### jy-transform:jyt~packagePath : string ℗ -The path to package.json. - -**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private - - -### jy-transform:jyt~options : Object ℗ -The options description for parsing the command line input, must be an object with opts defined like: -``` -long_tag: [short_tag, description, value_type, default_value]; -``` - -**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private - - -### jy-transform:jyt~error(err) ℗ -Prints the error to console and exit with 1. - -**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private - -| Param | Type | Description | -| --- | --- | --- | -| err | string | Error | The error to print. | - - - -### jy-transform:jyt~main(args, cliOptions) ℗ -The main entry callback. When calling `cli.main()` this receives the `options` -given on CLI, then does the transformation with these options and finally, it -prints the result to the CLI. - -**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private - -| Param | Type | Description | -| --- | --- | --- | -| args | Array | The first mandatory argument is the input file (`args[0]`), the second (optional) argument is the output file (`args[1]`). | -| cliOptions | module:type-definitions~Options | The options provided via CLI. | - ## jy-transform:middleware ℗ @@ -522,7 +481,7 @@ This module provides the _read_ functionality for YAML, JS or JSON sources. * [jy-transform:reader](#module_jy-transform_reader) * [~fsPromisified](#module_jy-transform_reader..fsPromisified) ℗ - * [~createReadableFunction(readable, bufs)](#module_jy-transform_reader..createReadableFunction) ⇒ function ℗ + * [~createOnReadableEventFunction(readable, bufs)](#module_jy-transform_reader..createOnReadableEventFunction) ⇒ function ℗ * [~readFromStream(readable, resolve, reject, origin)](#module_jy-transform_reader..readFromStream) ℗ @@ -532,9 +491,9 @@ Promisified `fs` module. **Kind**: inner constant of [jy-transform:reader](#module_jy-transform_reader) **Access:** private - + -### jy-transform:reader~createReadableFunction(readable, bufs) ⇒ function ℗ +### jy-transform:reader~createOnReadableEventFunction(readable, bufs) ⇒ function ℗ Creates a function to read from the passed source in to the given buffer array. **Kind**: inner method of [jy-transform:reader](#module_jy-transform_reader) @@ -567,76 +526,14 @@ Reads from a passed stream and resolves by callback. This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. **Access:** public - -* [jy-transform:transformer](#module_jy-transform_transformer) - * [~createTransformation](#module_jy-transform_transformer..createTransformation) ⇒ Promise.<string> - * [~itmo](#module_jy-transform_transformer..itmo) ℗ - * [~transform](#module_jy-transform_transformer..transform) ⇒ Promise.<String> - * [~transformations](#module_jy-transform_transformer..transformations) : object ℗ - * [~yamlToJson(options, [middleware])](#module_jy-transform_transformer..yamlToJson) ⇒ Promise ℗ - * [~jsToYaml(options, [middleware])](#module_jy-transform_transformer..jsToYaml) ⇒ Promise ℗ - * [~jsonToJs(options, [middleware])](#module_jy-transform_transformer..jsonToJs) ⇒ Promise ℗ - * [~jsToJson(options, [middleware])](#module_jy-transform_transformer..jsToJson) ⇒ Promise ℗ - * [~yamlToYaml(options, [middleware])](#module_jy-transform_transformer..yamlToYaml) ⇒ Promise ℗ - * [~jsonToJson(options, [middleware])](#module_jy-transform_transformer..jsonToJson) ⇒ Promise ℗ - * [~jsToJs(options, [middleware])](#module_jy-transform_transformer..jsToJs) ⇒ Promise.<String> ℗ - - - -### jy-transform:transformer~createTransformation ⇒ Promise.<string> -This method creates the transformation described by the given options resolving a name to get the proper function. - -**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise.<string> - The transformation name. -**Access:** public -**See**: [transformations](transformations) - -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.validateTransformation(options) - .spread(function (validatedOptions, transformation) { - ... - )): -``` - - -### jy-transform:transformer~itmo ℗ -Internal delegate function to execute transformation logic (ITMO): -- Input (read) -- Transform + Middleware -- Output (write) - -**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) -**Access:** private - -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | -| read | function | The reader function. | -| [middleware] | function | The middleware to apply. | -| write | function | The writer functions. | - -**Example** -```js -var options = {...}; -var middleware = async (obj) => { - ... -}; -const result = await itmo(options, readYaml, middleware, writeJson); -``` ### jy-transform:transformer~transform ⇒ Promise.<String> The entry method for all transformation accepting a configuration object and -an (optional) middleware function. +an (optional) middleware function. It executes the transformation logic: +1. Input (read) +2. Transform [ + Middleware] +3. Output (write). **Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) **Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). @@ -649,286 +546,87 @@ an (optional) middleware function. | Param | Type | Description | | --- | --- | --- | -| options | Options | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(json) ```

    **NOTE:** the Promise has to return the processed JSON! | +| options | module:type-definitions~TransformerOptions | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(object) ```

    **NOTE:** the Promise has to return the processed JSON. | **Example** ```js import { transform } from 'jy-transform'; const options = {...}; -const middleware = async (json) { - json.myproperty = 'new value'; - return json; +const middleware = async (object) { + object.myproperty = 'new value'; + return object; }; -transform(options, middleware) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - - -### jy-transform:transformer~transformations : object ℗ -A transformation name to internal function mapping. - -**Kind**: inner namespace of [jy-transform:transformer](#module_jy-transform_transformer) -**Access:** private -**Properties** - -| Name | Type | Description | -| --- | --- | --- | -| yaml2js | function | The transformation function for YAML ⇒ JS. | -| yaml2json | function | The transformation function for YAML ⇒ JSON. | -| yaml2yaml | function | The transformation function for YAML ⇒ YAML. | -| json2yaml | function | The transformation function for JSON ⇒ YAML. | -| json2js | function | The transformation function for JSON ⇒ JS. | -| json2json | function | The transformation function for JSON ⇒ JSON. | -| js2yaml | function | The transformation function for JS ⇒ YAML. | -| js2json | function | The transformation function for JS ⇒ JSON. | -| js2js | function | The transformation function for JS ⇒ JS. | - - - -### jy-transform:transformer~yamlToJson(options, [middleware]) ⇒ Promise ℗ -Convert YAML to JSON. - -**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: - -- TypeError - Will throw this error when the passed `middleware` - is not type of `Function`. -- Error - Will throw plain error when writing to JSON destination failed due to any reason. - -**Access:** private -**See**: itmo - -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | - -**Example** -```js -var logger = ...; -var options = {...}; -var middleware = function (obj) { - ... -}; -var Transformer = require('jy-transform'); -var transformer = new Transformer(logger); -transformer.yamlToJson(options, middleware); -``` - - -### jy-transform:transformer~jsToYaml(options, [middleware]) ⇒ Promise ℗ -Convert JS to YAML. - -**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: - -- TypeError - Will throw this error when the passed `middleware` - is not type of `Function`. -- Error - Will throw plain error when writing to YAML destination failed due to any reason. - -**Access:** private -**See**: itmo - -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | - -**Example** -```js -var logger = ...; -var options = {...}; -var middleware = function (obj) { - ... -}; -var Transformer = require('jy-transform'); -var transformer = new Transformer(logger); -transformer.jsToYaml(options, middleware); -``` - - -### jy-transform:transformer~jsonToJs(options, [middleware]) ⇒ Promise ℗ -Convert JSON to JS. - -**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: - -- TypeError - Will throw this error when the passed `middleware` - is not type of `Function`. -- Error - Will throw plain error when writing to JS destination failed due to any reason. - -**Access:** private -**See**: itmo - -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | - -**Example** -```js -var logger = ...; -var options = {...}; -var middleware = function (obj) { - ... -}; -var Transformer = require('jy-transform'); -var transformer = new Transformer(logger); -transformer.jsonToJs(options, middleware); -``` - - -### jy-transform:transformer~jsToJson(options, [middleware]) ⇒ Promise ℗ -Convert JS to JSON. - -**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: +// ---- Promise style: -- TypeError - Will throw this error when the passed `middleware` - is not type of `Function`. -- Error - Will throw plain error when writing to JSON destination failed due to any reason. - -**Access:** private -**See**: itmo - -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | - -**Example** -```js -var logger = ...; -var options = {...}; -var middleware = function (obj) { - ... -}; -var Transformer = require('jy-transform'); -var transformer = new Transformer(logger); -transformer.jsToJson(options, middleware); -``` - - -### jy-transform:transformer~yamlToYaml(options, [middleware]) ⇒ Promise ℗ -Convert YAML to YAML. - -**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: - -- TypeError - Will throw this error when the passed `middleware` - is not type of `Function`. -- Error - Will throw plain error when writing to YAML destination failed due to any reason. - -**Access:** private -**See**: itmo - -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | - -**Example** -```js -var logger = ...; -var options = {...}; -var middleware = function (obj) { - ... +transform(options, middleware) + .then(console.log) + .catch(console.error); + +// ---- async/await style: +try { + const msg = await transform(options, middleware); + console.log(msg); +} catch (err) { + console.error(err.stack); }; -var Transformer = require('jy-transform'); -var transformer = new Transformer(logger); -transformer.yamlToYaml(options, middleware); ``` - - -### jy-transform:transformer~jsonToJson(options, [middleware]) ⇒ Promise ℗ -Convert JSON to JSON. - -**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise - - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: + -- TypeError - Will throw this error when the passed `middleware` - is not type of `Function`. -- Error - Will throw plain error when writing to JSON destination failed due to any reason. +## jy-transform:type-definitions +The type definitions for this module. -**Access:** private -**See**: itmo +**Access:** public -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | +* [jy-transform:type-definitions](#module_jy-transform_type-definitions) + * [~ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) : object + * [~WriterOptions](#module_jy-transform_type-definitions..WriterOptions) : object + * [~TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) : object + * [~TEESTTransformerOptions](#module_jy-transform_type-definitions..TEESTTransformerOptions) : object + * [~TEESTT2ransformerOptions](#module_jy-transform_type-definitions..TEESTT2ransformerOptions) : WriterOptions | ReaderOptions + * [~joi](#external_joi) ℗ + * [.ValidationError](#external_joi.ValidationError) ℗ + * [.Schema](#external_joi.Schema) ℗ + * [.Extension](#external_joi.Extension) ℗ + * [.validate](#external_joi.validate) : function ℗ -**Example** -```js -var logger = ...; -var options = {...}; -var middleware = function (obj) { - ... -}; -var Transformer = require('jy-transform'); -var transformer = new Transformer(logger); -transformer.jsonToJson(options, middleware); -``` - + -### jy-transform:transformer~jsToJs(options, [middleware]) ⇒ Promise.<String> ℗ -Convert JS to JS. +### jy-transform:type-definitions~ReaderOptions : object +The configuration properties provided to the reader function. -**Kind**: inner method of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**Access:** public +**Properties** -- TypeError Will throw this error when the passed `middleware` is not type of `Function`. -- Error Will throw plain error when writing to JS destination failed due to any reason. +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| src | string | Stream.Readable | object | | The source (if `string` type is treated as a file path). | +| origin | string | "yaml" | The origin type. | +| imports | string | | The exports name for reading from JS source files or objects only. | -**Access:** private -**See**: itmo + -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JS object for manipulation. The function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JS object! | +### jy-transform:type-definitions~WriterOptions : object +The writer configuration properties provided to the writer function. -**Example** -```js -var options = {...}; -var middleware = async (json) { - ... -}; -jsToJs(options, middleware); -``` - - -## jy-transform:type-definitions ℗ -The type definitions for this module. - -**Access:** private +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**Access:** public +**Properties** -* [jy-transform:type-definitions](#module_jy-transform_type-definitions) ℗ - * [~Options](#module_jy-transform_type-definitions..Options) : object - * [~joi](#external_joi) - * [.ValidationError](#external_joi.ValidationError) - * [.Schema](#external_joi.Schema) - * [.Extension](#external_joi.Extension) - * [.validate](#external_joi.validate) : function +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | +| target | string | "js" | The target type. | +| indent | number | 2 | The indention in files. | +| exports | string | | The exports name for usage in JS destination files only. | +| force | string | false | Force overwriting of existing output files on write phase. | - + -### jy-transform:type-definitions~Options : object -The configuration properties provided to the framework functions. +### jy-transform:type-definitions~TransformerOptions : object +The configuration properties provided to the transformer function. **Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) **Access:** public @@ -936,57 +634,76 @@ The configuration properties provided to the framework functions. | Name | Type | Default | Description | | --- | --- | --- | --- | -| origin | string | "yaml" | The origin type. | -| target | string | "js" | The target type. | | src | string | Stream.Readable | object | | The source (if `string` type is treated as a file path). | -| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | -| indent | number | 2 | The indention in files. | +| origin | string | "yaml" | The origin type. | | imports | string | | The exports name for reading from JS source files or objects only. | +| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | +| target | string | "js" | The target type. | | exports | string | | The exports name for usage in JS destination files only. | +| indent | number | 2 | The indention in files. | | force | string | false | Force overwriting of existing output files on write phase. | + + +### jy-transform:type-definitions~TEESTTransformerOptions : object +The configuration properties provided to the transformer function. + +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**Access:** public + + +### jy-transform:type-definitions~TEESTT2ransformerOptions : WriterOptions | ReaderOptions +The configuration properties provided to the transformer function. + +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**Access:** public -### jy-transform:type-definitions~joi +### jy-transform:type-definitions~joi ℗ Hapi.js Joi. **Kind**: inner external of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**Access:** private **See**: [Hapi Joi](https://github.com/hapijs/joi) -* [~joi](#external_joi) - * [.ValidationError](#external_joi.ValidationError) - * [.Schema](#external_joi.Schema) - * [.Extension](#external_joi.Extension) - * [.validate](#external_joi.validate) : function +* [~joi](#external_joi) ℗ + * [.ValidationError](#external_joi.ValidationError) ℗ + * [.Schema](#external_joi.Schema) ℗ + * [.Extension](#external_joi.Extension) ℗ + * [.validate](#external_joi.validate) : function -#### joi.ValidationError +#### joi.ValidationError ℗ Joi validation error. **Kind**: static typedef of [joi](#external_joi) +**Access:** private **See**: [Joi errors](hhttps://github.com/hapijs/joi/blob/v10.2.0/API.md#errors) -#### joi.Schema +#### joi.Schema ℗ The validation schema. Can be a [joi](#external_joi) type object or a plain object where every key is assigned a [joi](#external_joi) type object. **Kind**: static typedef of [joi](#external_joi) +**Access:** private **See**: [Joi API](https://github.com/hapijs/joi/blob/v10.2.2/API.md#joi) -#### joi.Extension +#### joi.Extension ℗ Hapi.js Joi schema extension. **Kind**: static typedef of [joi](#external_joi) +**Access:** private **See**: [Hapi Joi Extension](https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension) -#### joi.validate : function +#### joi.validate : function ℗ Joi `validate` method. **Kind**: static typedef of [joi](#external_joi) +**Access:** private **See**: [Joi.validate](https://github.com/hapijs/joi/blob/master/API.md#validatevalue-schema-options-callback) @@ -1071,41 +788,13 @@ values for origin and target depending on the `options.src` or `options.dest` va **See**: [module:validation:options-schema](module:validation:options-schema) * [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) : Object ℗ - * [~inferOriginDefaultFromStreamReadableFilePath](#module_jy-transform_validation_options-schema-helper..inferOriginDefaultFromStreamReadableFilePath) ⇒ string - * [~inferOriginDefaultFromFilePath](#module_jy-transform_validation_options-schema-helper..inferOriginDefaultFromFilePath) ⇒ string - * [~inferTargetDefaultFromStreamWritableFilePath](#module_jy-transform_validation_options-schema-helper..inferTargetDefaultFromStreamWritableFilePath) ⇒ string - * [~inferTargetDefaultFromFilePath](#module_jy-transform_validation_options-schema-helper..inferTargetDefaultFromFilePath) ⇒ string - * [~getTypeFromFilePath(pathStr, origin, defaultValue)](#module_jy-transform_validation_options-schema-helper..getTypeFromFilePath) ⇒ string ℗ + * [~inferOriginDefault](#module_jy-transform_validation_options-schema-helper..inferOriginDefault) ⇒ string + * [~inferTargetDefault](#module_jy-transform_validation_options-schema-helper..inferTargetDefault) ⇒ string + * [~getTypeFromFilePath(pathStr, defaultValue)](#module_jy-transform_validation_options-schema-helper..getTypeFromFilePath) ⇒ string ℗ - + -### jy-transform:validation:options-schema-helper~inferOriginDefaultFromStreamReadableFilePath ⇒ string -TODO describe me. - -**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) -**Returns**: string - The origin type. -**Access:** protected - -| Param | Type | Description | -| --- | --- | --- | -| context | Object | TODO describe me. | - - - -### jy-transform:validation:options-schema-helper~inferOriginDefaultFromFilePath ⇒ string -TODO describe me. - -**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) -**Returns**: string - The origin type. -**Access:** protected - -| Param | Type | Description | -| --- | --- | --- | -| context | Object | TODO describe me. | - - - -### jy-transform:validation:options-schema-helper~inferTargetDefaultFromStreamWritableFilePath ⇒ string +### jy-transform:validation:options-schema-helper~inferOriginDefault ⇒ string TODO describe me. **Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) @@ -1116,9 +805,9 @@ TODO describe me. | --- | --- | --- | | context | Object | TODO describe me. | - + -### jy-transform:validation:options-schema-helper~inferTargetDefaultFromFilePath ⇒ string +### jy-transform:validation:options-schema-helper~inferTargetDefault ⇒ string TODO describe me. **Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) @@ -1131,7 +820,7 @@ TODO describe me. -### jy-transform:validation:options-schema-helper~getTypeFromFilePath(pathStr, origin, defaultValue) ⇒ string ℗ +### jy-transform:validation:options-schema-helper~getTypeFromFilePath(pathStr, defaultValue) ⇒ string ℗ Infer from path extension to a type using default value as fallback. **Kind**: inner method of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) @@ -1141,7 +830,6 @@ Infer from path extension to a type using default value as fallback. | Param | Type | Description | | --- | --- | --- | | pathStr | string | The file path with or without extension. | -| origin | boolean | If the type is origin (true) or target (false). | | defaultValue | string | The default value to use if type cannot be inferred from path. | @@ -1155,7 +843,6 @@ The module options schema used in [module:options-validator](module:options-vali * [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) : Object ℗ * [~readerOptionsSchema](#module_jy-transform_validation_options-schema..readerOptionsSchema) : JoiSchema ℗ * [~writerOptionsSchema](#module_jy-transform_validation_options-schema..writerOptionsSchema) : JoiSchema ℗ - * [~transformerOptionsSchema](#module_jy-transform_validation_options-schema..transformerOptionsSchema) : JoiSchema @@ -1169,19 +856,13 @@ The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the ### jy-transform:validation:options-schema~writerOptionsSchema : JoiSchema ℗ The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [Writer](Writer) options. -**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) -**Access:** private - - -### jy-transform:validation:options-schema~transformerOptionsSchema : JoiSchema ℗ -The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [Transformer](Transformer) options. - **Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) **Access:** private ## jy-transform:writer -This module provides the _write_ functionality for YAML, JS or JSON targets. +This module provides the _write_ functionality to write JS objects from memory to a JSON/JS/YAML +destination (file, object or [stream.Readable](stream.Readable)). **Access:** public @@ -1314,9 +995,9 @@ Writes a string serialized data object to a stream. **Access:** private **See** -- [Constants#TYPE_YAML](Constants#TYPE_YAML) -- [Constants#TYPE_JSON](Constants#TYPE_JSON) -- [Constants#TYPE_JS](Constants#TYPE_JS) +- [TYPE_YAML](TYPE_YAML) +- [TYPE_JSON](TYPE_JSON) +- [TYPE_JS](TYPE_JS) | Param | Type | Description | @@ -1487,14 +1168,45 @@ This unit test suite checks the validity and correctness of options schema helpe This unit test suite checks the validity and correctness of options schema. **Access:** private + +* [jy-transform:unit-test:test-options-schema](#module_jy-transform_unit-test_test-options-schema) ℗ + * [~expectOptionsValidationError(invalidOptions, schema)](#module_jy-transform_unit-test_test-options-schema..expectOptionsValidationError) ℗ + * [~expectOptionsValidationSuccess(validOptions, schema)](#module_jy-transform_unit-test_test-options-schema..expectOptionsValidationSuccess) ℗ + + + +### jy-transform:unit-test:test-options-schema~expectOptionsValidationError(invalidOptions, schema) ℗ +Expect a `ValidationError` for a given options function. + +**Kind**: inner method of [jy-transform:unit-test:test-options-schema](#module_jy-transform_unit-test_test-options-schema) +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| invalidOptions | Options | The options which potentially produce the error. | +| schema | Schema | The validation schema. | + + + +### jy-transform:unit-test:test-options-schema~expectOptionsValidationSuccess(validOptions, schema) ℗ +Expect a validation success for a given options. + +**Kind**: inner method of [jy-transform:unit-test:test-options-schema](#module_jy-transform_unit-test_test-options-schema) +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| validOptions | Options | The options which should be correct. | +| schema | Schema | The validation schema. | + -## readJs ⇒ Promise.<Object> +## readJs ⇒ Promise.<Object> ℗ Reads the data from a given JS or JSON source. **Kind**: global variable **Returns**: Promise.<Object> - Contains the read JS object. -**Access:** public +**Access:** private | Param | Type | Description | | --- | --- | --- | @@ -1554,14 +1266,14 @@ reader.readJs(options) ``` -## readYaml ⇒ Promise.<Object> +## readYaml ⇒ Promise.<Object> ℗ Loads a single YAML source containing document and returns a JS object. *NOTE:* this function does not understand multi-document sources, it throws exception on those. **Kind**: global variable **Returns**: Promise.<Object> - Contains the read JS object. -**Access:** public +**Access:** private | Param | Type | Description | | --- | --- | --- | @@ -1602,9 +1314,22 @@ reader.readJs(options) logger.error(err.stack); }); ``` + + +## read ⇒ Promise.<string> +TODO: doc me. + +**Kind**: global variable +**Returns**: Promise.<string> - Resolves with read success message. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | module:type-definitions~ReaderOptions | The read options. | + -## writeYaml ⇒ Promise.<string> +## writeYaml ⇒ Promise.<string> ℗ Writes a JS object to a YAML destination. **Kind**: global variable @@ -1613,12 +1338,12 @@ Writes a JS object to a YAML destination. - Error If YAML destination could not be written due to any reason. -**Access:** public +**Access:** private **See** -- [Constants#MIN_INDENT](Constants#MIN_INDENT) -- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) -- [Constants#MAX_INDENT](Constants#MAX_INDENT) +- [MIN_INDENT](MIN_INDENT) +- [DEFAULT_INDENT](DEFAULT_INDENT) +- [MAX_INDENT](MAX_INDENT) | Param | Type | Description | @@ -1666,17 +1391,17 @@ writer.writeYaml(obj, options) ``` -## writeJson ⇒ Promise.<string> +## writeJson ⇒ Promise.<string> ℗ Writes a JS object to a JSON destination. **Kind**: global variable **Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). -**Access:** public +**Access:** private **See** -- [Constants#MIN_INDENT](Constants#MIN_INDENT) -- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) -- [Constants#MAX_INDENT](Constants#MAX_INDENT) +- [MIN_INDENT](MIN_INDENT) +- [DEFAULT_INDENT](DEFAULT_INDENT) +- [MAX_INDENT](MAX_INDENT) | Param | Type | Description | @@ -1739,17 +1464,17 @@ writer.writeJson(obj, options) ``` -## writeJs ⇒ Promise +## writeJs ⇒ Promise.<string> ℗ Writes a JS object to a JS destination. The object is prefixed by `module.exports[.${options.exports}] = `. **Kind**: global variable -**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). -**Access:** public +**Returns**: Promise.<string> - - Containing the write success message to handle by caller (e.g. for logging). +**Access:** private **See** -- [Constants#MIN_INDENT](Constants#MIN_INDENT) -- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) -- [Constants#MAX_INDENT](Constants#MAX_INDENT) +- [MIN_INDENT](MIN_INDENT) +- [DEFAULT_INDENT](DEFAULT_INDENT) +- [MAX_INDENT](MAX_INDENT) | Param | Type | Description | @@ -1811,3 +1536,17 @@ writer.writeJs(obj, options) logger.error(err.stack); }); ``` + + +## write ⇒ Promise.<string> +TODO: doc me. + +**Kind**: global variable +**Returns**: Promise.<string> - Resolves with write success message. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | Object | The source object to write. | +| options | module:type-definitions~WriterOptions | The write options. | + diff --git a/API-PUBLIC.md b/API-PUBLIC.md index c343992..b1dbf10 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -1,3 +1,19 @@ + + +## TOC + +- [Modules](#modules) +- [Members](#members) +- [jy-transform:constants](#jy-transformconstants) +- [jy-transform:reader](#jy-transformreader) +- [jy-transform:transformer](#jy-transformtransformer) +- [jy-transform:type-definitions](#jy-transformtype-definitions) +- [jy-transform:writer](#jy-transformwriter) +- [read ⇒ Promise.<string>](#read-%E2%87%92-codepromiseltstringgtcode) +- [write ⇒ Promise.<string>](#write-%E2%87%92-codepromiseltstringgtcode) + + + ## Modules

    @@ -10,30 +26,23 @@
    jy-transform:transformer

    This module provides the transform functionality for YAML, JS or JSON source to destination mapping.

    +
    jy-transform:type-definitions
    +

    The type definitions for this module.

    +
    jy-transform:writer
    -

    This module provides the write functionality for YAML, JS or JSON targets.

    +

    This module provides the write functionality to write JS objects from memory to a JSON/JS/YAML +destination (file, object or stream.Readable).

    ## Members
    -
    readJsPromise.<Object>
    -

    Reads the data from a given JS or JSON source.

    -
    -
    readYamlPromise.<Object>
    -

    Loads a single YAML source containing document and returns a JS object. -NOTE: this function does not understand multi-document sources, it throws -exception on those.

    -
    -
    writeYamlPromise.<string>
    -

    Writes a JS object to a YAML destination.

    -
    -
    writeJsonPromise.<string>
    -

    Writes a JS object to a JSON destination.

    +
    readPromise.<string>
    +

    TODO: doc me.

    -
    writeJsPromise
    -

    Writes a JS object to a JS destination. The object is prefixed by module.exports[.${options.exports}] =.

    +
    writePromise.<string>
    +

    TODO: doc me.

    @@ -50,7 +59,6 @@ Useful constants used for the module and its usage. * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string * [~TYPE_JSON](#module_jy-transform_constants..TYPE_JSON) : string * [~TYPE_JS](#module_jy-transform_constants..TYPE_JS) : string - * [~TYPES](#module_jy-transform_constants..TYPES) : Array.<string> * [~TYPE_MAP](#module_jy-transform_constants..TYPE_MAP) : Object * [~DEFAULT_INDENT](#module_jy-transform_constants..DEFAULT_INDENT) : number * [~MIN_INDENT](#module_jy-transform_constants..MIN_INDENT) : number @@ -63,16 +71,6 @@ Useful constants used for the module and its usage. * [~DEST_DESCRIPTION](#module_jy-transform_constants..DEST_DESCRIPTION) : string * [~DEFAULT_JS_IMPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_IMPORTS_IDENTIFIER) : string * [~DEFAULT_JS_EXPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_EXPORTS_IDENTIFIER) : string - * [~YAML_TO_JS](#module_jy-transform_constants..YAML_TO_JS) : string - * [~YAML_TO_JSON](#module_jy-transform_constants..YAML_TO_JSON) : string - * [~JS_TO_YAML](#module_jy-transform_constants..JS_TO_YAML) : string - * [~JSON_TO_YAML](#module_jy-transform_constants..JSON_TO_YAML) : string - * [~JSON_TO_JS](#module_jy-transform_constants..JSON_TO_JS) : string - * [~JS_TO_JSON](#module_jy-transform_constants..JS_TO_JSON) : string - * [~YAML_TO_YAML](#module_jy-transform_constants..YAML_TO_YAML) : string - * [~JSON_TO_JSON](#module_jy-transform_constants..JSON_TO_JSON) : string - * [~JS_TO_JS](#module_jy-transform_constants..JS_TO_JS) : string - * [~TRANSFORMATIONS](#module_jy-transform_constants..TRANSFORMATIONS) : Array.<string> @@ -124,13 +122,6 @@ The 'json' type constant. ### jy-transform:constants~TYPE_JS : string The 'js' type constant. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~TYPES : Array.<string> -The type constants assembled in an array: `[ 'yaml', 'json', 'js' ]`. - **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) **Access:** public @@ -219,76 +210,6 @@ module.exports.foo = {...}; // here 'foo' is the identifier for an object to rea ### jy-transform:constants~DEFAULT_JS_EXPORTS_IDENTIFIER : string The `dest` exports identifier value to write. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~YAML_TO_JS : string -The transformation direction YAML ⇒ JS. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~YAML_TO_JSON : string -The transformation direction YAML ⇒ JSON. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JS_TO_YAML : string -The transformation direction JS ⇒ YAML. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JSON_TO_YAML : string -The transformation direction JSON ⇒ YAML. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JSON_TO_JS : string -The transformation direction JSON ⇒ JS. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JS_TO_JSON : string -The transformation direction JS ⇒ JSON. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~YAML_TO_YAML : string -The transformation direction YAML ⇒ YAML. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JSON_TO_JSON : string -The transformation direction JSON ⇒ JSON. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~JS_TO_JS : string -The transformation direction JS ⇒ JS. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~TRANSFORMATIONS : Array.<string> -The transformation directions. - **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) **Access:** public @@ -303,41 +224,14 @@ This module provides the _read_ functionality for YAML, JS or JSON sources. This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. **Access:** public - -* [jy-transform:transformer](#module_jy-transform_transformer) - * [~createTransformation](#module_jy-transform_transformer..createTransformation) ⇒ Promise.<string> - * [~transform](#module_jy-transform_transformer..transform) ⇒ Promise.<String> - - - -### jy-transform:transformer~createTransformation ⇒ Promise.<string> -This method creates the transformation described by the given options resolving a name to get the proper function. - -**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise.<string> - The transformation name. -**Access:** public -**See**: [transformations](transformations) - -| Param | Type | Description | -| --- | --- | --- | -| options | Options | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.validateTransformation(options) - .spread(function (validatedOptions, transformation) { - ... - )): -``` ### jy-transform:transformer~transform ⇒ Promise.<String> The entry method for all transformation accepting a configuration object and -an (optional) middleware function. +an (optional) middleware function. It executes the transformation logic: +1. Input (read) +2. Transform [ + Middleware] +3. Output (write). **Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) **Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). @@ -350,353 +244,143 @@ an (optional) middleware function. | Param | Type | Description | | --- | --- | --- | -| options | Options | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(json) ```

    **NOTE:** the Promise has to return the processed JSON! | +| options | module:type-definitions~TransformerOptions | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(object) ```

    **NOTE:** the Promise has to return the processed JSON. | **Example** ```js import { transform } from 'jy-transform'; const options = {...}; -const middleware = async (json) { - json.myproperty = 'new value'; - return json; +const middleware = async (object) { + object.myproperty = 'new value'; + return object; }; +// ---- Promise style: + transform(options, middleware) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); + .then(console.log) + .catch(console.error); + +// ---- async/await style: +try { + const msg = await transform(options, middleware); + console.log(msg); +} catch (err) { + console.error(err.stack); +}; ``` - + -## jy-transform:writer -This module provides the _write_ functionality for YAML, JS or JSON targets. +## jy-transform:type-definitions +The type definitions for this module. **Access:** public - -## readJs ⇒ Promise.<Object> -Reads the data from a given JS or JSON source. - -**Kind**: global variable -**Returns**: Promise.<Object> - Contains the read JS object. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | Options | Contains the JS/JSON source reference to read from. | - -**Example** -```js -var Reader = require('jy-transform').Reader; -var logger = ...; -var reader = new Reader(logger); - -// --- from file path - -var options = { - src: 'foo.js' -}; - -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// --- from Readable - -options = { - src: fs.createReadStream('foo.js') -}; - -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); +* [jy-transform:type-definitions](#module_jy-transform_type-definitions) + * [~ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) : object + * [~WriterOptions](#module_jy-transform_type-definitions..WriterOptions) : object + * [~TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) : object + * [~TEESTTransformerOptions](#module_jy-transform_type-definitions..TEESTTransformerOptions) : object + * [~TEESTT2ransformerOptions](#module_jy-transform_type-definitions..TEESTT2ransformerOptions) : WriterOptions | ReaderOptions + -// --- from object +### jy-transform:type-definitions~ReaderOptions : object +The configuration properties provided to the reader function. -options = { - src: { - foo: 'bar' - } -}; - -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - - -## readYaml ⇒ Promise.<Object> -Loads a single YAML source containing document and returns a JS object. -*NOTE:* this function does not understand multi-document sources, it throws -exception on those. - -**Kind**: global variable -**Returns**: Promise.<Object> - Contains the read JS object. +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) **Access:** public +**Properties** -| Param | Type | Description | -| --- | --- | --- | -| options | Options | Contains the YAML source reference to read from. | - -**Example** -```js -var Reader = require('jy-transform').Reader; -var logger = ...; -var reader = new Reader(logger); +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| src | string | Stream.Readable | object | | The source (if `string` type is treated as a file path). | +| origin | string | "yaml" | The origin type. | +| imports | string | | The exports name for reading from JS source files or objects only. | -// --- from file path + -options = { - src: 'foo.yml' -}; +### jy-transform:type-definitions~WriterOptions : object +The writer configuration properties provided to the writer function. -reader.readYaml(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**Access:** public +**Properties** +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | +| target | string | "js" | The target type. | +| indent | number | 2 | The indention in files. | +| exports | string | | The exports name for usage in JS destination files only. | +| force | string | false | Force overwriting of existing output files on write phase. | -// --- from Readable + -options = { - src: fs.createReadStream('foo.yml') -}; +### jy-transform:type-definitions~TransformerOptions : object +The configuration properties provided to the transformer function. -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**Access:** public +**Properties** -## writeYaml ⇒ Promise.<string> -Writes a JS object to a YAML destination. +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| src | string | Stream.Readable | object | | The source (if `string` type is treated as a file path). | +| origin | string | "yaml" | The origin type. | +| imports | string | | The exports name for reading from JS source files or objects only. | +| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | +| target | string | "js" | The target type. | +| exports | string | | The exports name for usage in JS destination files only. | +| indent | number | 2 | The indention in files. | +| force | string | false | Force overwriting of existing output files on write phase. | -**Kind**: global variable -**Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). -**Throws**: + -- Error If YAML destination could not be written due to any reason. +### jy-transform:type-definitions~TEESTTransformerOptions : object +The configuration properties provided to the transformer function. +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) **Access:** public -**See** + -- [Constants#MIN_INDENT](Constants#MIN_INDENT) -- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) -- [Constants#MAX_INDENT](Constants#MAX_INDENT) +### jy-transform:type-definitions~TEESTT2ransformerOptions : WriterOptions | ReaderOptions +The configuration properties provided to the transformer function. +**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) +**Access:** public + -| Param | Type | Description | -| --- | --- | --- | -| object | Object | The JS object to write into YAML destination. | -| options | Options | The write destination and indention. | +## jy-transform:writer +This module provides the _write_ functionality to write JS objects from memory to a JSON/JS/YAML +destination (file, object or [stream.Readable](stream.Readable)). -**Example** -```js -var Writer = require('jy-transform').Writer; -var logger = ...; -var writer = new Writer(logger); - -// ---- write obj to file - -var obj = {...}, -var options = { - dest: 'result.yml', - indent: 2 -} - -writer.writeYaml(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to Writable - -options = { - dest: fs.createWriteStream('result.yml'), - indent: 4 -} - -writer.writeYaml(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - +**Access:** public + -## writeJson ⇒ Promise.<string> -Writes a JS object to a JSON destination. +## read ⇒ Promise.<string> +TODO: doc me. **Kind**: global variable -**Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). +**Returns**: Promise.<string> - Resolves with read success message. **Access:** public -**See** - -- [Constants#MIN_INDENT](Constants#MIN_INDENT) -- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) -- [Constants#MAX_INDENT](Constants#MAX_INDENT) - | Param | Type | Description | | --- | --- | --- | -| object | Object | The JS object to write into JSON destination. | -| options | Options | The write destination and indention. | +| options | module:type-definitions~ReaderOptions | The read options. | -**Example** -```js -var Writer = require('jy-transform').Writer; -var logger = ...; -var writer = new Writer(logger); - -// ---- write obj to file - -var obj = {...}; -var options = { - dest: 'result.json', - indent: 2 -} - -writer.writeJson(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to Writable - -options = { - dest: fs.createWriteStream('result.json'), - indent: 4 -} - -writer.writeJson(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - -// ---- write obj to object - -options = { - dest: {}, - indent: 4 -} - -writer.writeJson(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - + -## writeJs ⇒ Promise -Writes a JS object to a JS destination. The object is prefixed by `module.exports[.${options.exports}] = `. +## write ⇒ Promise.<string> +TODO: doc me. **Kind**: global variable -**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). +**Returns**: Promise.<string> - Resolves with write success message. **Access:** public -**See** - -- [Constants#MIN_INDENT](Constants#MIN_INDENT) -- [Constants#DEFAULT_INDENT](Constants#DEFAULT_INDENT) -- [Constants#MAX_INDENT](Constants#MAX_INDENT) - | Param | Type | Description | | --- | --- | --- | -| object | Object | The JSON to write into JS destination. | -| options | Options | The write destination and indention. | +| options | Object | The source object to write. | +| options | module:type-definitions~WriterOptions | The write options. | -**Example** -```js -var Writer = require('jy-transform').Writer; -var logger = ...; -var writer = new Writer(logger); - -// ---- write obj to file - -var obj = {...}; -var options = { - dest: 'result.js', - indent: 2 -} - -writer.writeJs(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to Writable - -options = { - dest: fs.createWriteStream('result.json'), - indent: 4 -} - -writer.writeJs(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to object - -options = { - dest: {}, - indent: 2 -} - -writer.writeJs(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` diff --git a/readme/CHANGELOG.md b/CHANGELOG.md similarity index 78% rename from readme/CHANGELOG.md rename to CHANGELOG.md index eb9a564..f46204a 100644 --- a/readme/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,31 +1,34 @@ +## Changelog + #### v3.0.0 - **CLI & API Changes (Backwards Incompatible!):** - Removed support for Node.js < v4.0 - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community -- **API Changes only (Backwards Incompatible!):** - - Prototype removal from `Transformer`, `Reader` and `Writer`, turning it to simple exports of functions - - Easier usage by using named imports only for all classes (i.e. also for `Transformer`) - - The formerly exported `middleware` is not public anymore - - Eased interfaces: - - The formerly exported `Reader.readJs(...)|readYaml(...)` functions are not public anymore and replaced by a more simple to use `read(options)` function - - The formerly exported `Writer.writeJs(...)|writeJson(...)|readYaml(...)` functions are not public anymore and replaced by a more simple to use `write(options)` function - - The `options.imports/exports` are not allowed to be empty strings anymore (just leave it out) +- **API Changes Only (Backwards Incompatible!):** - The exported constants `YAML`, `JS` and `JSON` (usable for `options.origin/target`) are renamed respectively to `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` - - `options.dest` is required for `Transfomer` and `Writer` on API usage now + - Eased interface: + - Prototype approach removed from `Transformer`, `Reader` and `Writer`, turning it to internal modules with exports of _named_ functions to provide a simplified interface + - The formerly exported `Reader.readJs(...)/readYaml(...)` functions are not public anymore and replaced by a general `read(options)` function + - The formerly exported `Writer.writeJs(...)/writeJson(...)/writeYaml(...)` functions are not public anymore and replaced by a general `write(object, options)` function + - The formerly exported `middleware` is not publicly available anymore + - Reduced and named export of constants: `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` only + - The `options.imports/exports` are not allowed to be empty strings anymore (semantically senseless, just leave it out) + - The configuration property `options.dest` is required for `Transformer` and `Writer` when using the API - Removal of `LogWrapper` (no more logger injection possible/needed) - Internal Changes & Improvements: + - Documentation restructured - Removal of _development_ branch - Usage of [babel](https://babeljs.io/) and therefore most modern language features - - Code base could be shrinked and readabilty improved + - Code base could be shrinked and readabilty was improved - Usage of _native promises_ instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html) - Tests re-written in [Jest](https://facebook.github.io/jest) (could get rid of [assert](https://github.com/defunctzombie/commonjs-assert), [mocha](https://mochajs.org/) and [istanbul](https://github.com/gotwarlost/istanbul)) - Add travis build for Node.js v8.x - Remove travis build for Node.js < v4.x - - Removal of `OptionsHandler` and `Validator` (replaced validation stuff by [joi](https://github.com/hapijs/joi/tree/v10.5.0)) + - Removal of `OptionsHandler` and `Validator` (replaced the validation by using [joi](https://github.com/hapijs/joi/tree/v10.5.0) now) #### v2.0.1 diff --git a/README.md b/README.md index f9dc471..34a61b9 100644 --- a/README.md +++ b/README.md @@ -12,95 +12,12 @@ npm install jy-transform --global -# Stats - -## General - -| [License](https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md) | [Issues](https://github.com/deadratfink/jy-transform/issues) | [Releases](https://github.com/deadratfink/jy-transform/releases) | [Tags](https://github.com/deadratfink/jy-transform/tags) | [Travis CI](https://travis-ci.org) | [Waffle](https://waffle.io/deadratfink/jy-transform) | [Code Climate](https://codeclimate.com/github/deadratfink/jy-transform) | -| --- | --- | --- | --- | --- | --- | --- | -| [![License][gh-license-image]][gh-license-url] | [![Issue Stats][gh-issues-image]][gh-issues-url] | [![Releases][gh-releases-image]][gh-releases-url] | [![Tags][gh-tags-image]][gh-tags-url] | [![Build Status][ci-image]][ci-url] | [![Waffle][waffle-image]][waffle-url] | [![Code Climate][cocl-image]][cocl-url] | - -## Branches - -| Branch | [Codecov](https://codecov.io) | [Coveralls](https://coveralls.io) | [Inch CI](http://inch-ci.org) | [David](https://david-dm.org) DM | [David](https://david-dm.org) DM (dev) | -| --- | --- | --- | --- | --- | --- | -| master | [![codecov.io][cc-image-master]][cc-url-master] | [![coveralls.io][ca-image-master]][ca-url-master] | [![inch-ci.org][inch-image-master]][inch-url-master] | [![Dependency Status][dep-image-master]][dep-url-master] | [![devDependency Status][devdep-image-master]][devdep-url-master] | -| development | [![codecov.io][cc-image-development]][cc-url-development] | [![coveralls.io][ca-image-development]][ca-url-development] | [![inch-ci.org][inch-image-development]][inch-url-development] | [![Dependency Status][dep-image-development]][dep-url-development] | [![devDependency Status][devdep-image-development]][devdep-url-development] | - -### Coverage - -| master | development | -| --- | --- | -| ![codecov.io](https://codecov.io/gh/deadratfink/jy-transform/branch/master/graphs/tree.svg) | ![codecov.io](https://codecov.io/gh/deadratfink/jy-transform/branch/development/graphs/tree.svg) | - - - - - - -## NPM - -[![NPM](https://nodei.co/npm/jy-transform.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/jy-transform/) -[![NPM](https://nodei.co/npm-dl/jy-transform.png?height=3&months=9)](https://nodei.co/npm-dl/jy-transform/) - -[gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat-square -[gh-license-url]: https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md - -[gh-issues-image]: https://img.shields.io/github/issues/deadratfink/jy-transform.svg?style=flat-square -[gh-issues-url]: https://github.com/deadratfink/jy-transform/issues - -[gh-releases-image]: https://img.shields.io/github/release/deadratfink/jy-transform.svg?style=flat-square -[gh-releases-url]: https://github.com/deadratfink/jy-transform/releases - -[gh-tags-image]: https://img.shields.io/github/tag/deadratfink/jy-transform.svg?style=flat-square -[gh-tags-url]: https://github.com/deadratfink/jy-transform/tags - - -[ci-image]: https://img.shields.io/travis/deadratfink/jy-transform.svg?style=flat-square -[ci-url]: https://travis-ci.org/deadratfink/jy-transform/branches - -[is-pull-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/pr?style=flat-square -[is-issue-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/issue?style=flat-square -[is-url]: http://issuestats.com/github/deadratfink/jy-transform - -[waffle-image]: https://badge.waffle.io/deadratfink/jy-transform.png?label=ready&title=Ready&style=flat-square -[waffle-url]: https://waffle.io/deadratfink/jy-transform - -[cocl-image]: https://img.shields.io/codeclimate/github/deadratfink/jy-transform.svg?style=flat-square -[cocl-url]: https://codeclimate.com/github/deadratfink/jy-transform - - -[cc-image-master]: https://img.shields.io/codecov/c/github/deadratfink/jy-transform/master.svg?style=flat-square -[cc-url-master]: https://codecov.io/github/deadratfink/jy-transform?branch=master -[cc-image-development]: https://img.shields.io/codecov/c/github/deadratfink/jy-transform/development.svg?style=flat-square -[cc-url-development]: https://codecov.io/github/deadratfink/jy-transform?branch=development - -[ca-image-master]: https://img.shields.io/coveralls/deadratfink/jy-transform/master.svg?style=flat-square -[ca-url-master]: https://coveralls.io/github/deadratfink/jy-transform?branch=master -[ca-image-development]: https://img.shields.io/coveralls/deadratfink/jy-transform/development.svg?style=flat-square -[ca-url-development]: https://coveralls.io/github/deadratfink/jy-transform?branch=development - - -[inch-image-master]: https://inch-ci.org/github/deadratfink/jy-transform.svg?branch=master&style=flat-square -[inch-url-master]: https://inch-ci.org/github/deadratfink/jy-transform?branch=master -[inch-image-development]: https://inch-ci.org/github/deadratfink/jy-transform.svg?branch=development&style=flat-square -[inch-url-development]: https://inch-ci.org/github/deadratfink/jy-transform?branch=development - -[dep-image-master]: https://img.shields.io/david/deadratfink/jy-transform/master.svg?style=flat-square -[dep-url-master]: https://david-dm.org/deadratfink/jy-transform/master -[dep-image-development]: https://img.shields.io/david/deadratfink/jy-transform/development.svg?style=flat-square -[dep-url-development]: https://david-dm.org/deadratfink/jy-transform/development - -[devdep-image-master]: https://img.shields.io/david/dev/deadratfink/jy-transform/master.svg?style=flat-square -[devdep-url-master]: https://david-dm.org/deadratfink/jy-transform/master#info=devDependencies -[devdep-image-development]: https://img.shields.io/david/dev/deadratfink/jy-transform/development.svg?style=flat-square -[devdep-url-development]: https://david-dm.org/deadratfink/jy-transform/development#info=devDependencies - ## TOC - - [Motivation](#motivation) +- [API in a Minute](#api-in-a-minute) +- [Why This Module?](#why-this-module) - [Usage](#usage) - [Usage Types](#usage-types) - [Use Cases](#use-cases) @@ -108,28 +25,72 @@ npm install jy-transform --global - [CLI Usage](#cli-usage) - [Origin and Target Type Inference](#origin-and-target-type-inference) - [API Usage](#api-usage) - - [Using Custom Logger](#using-custom-logger) - [API Reference](#api-reference) - [Contributing](#contributing) - - [Further information](#further-information) - - [License](#license) +- [Further information](#further-information) -## Motivation +## API in a Minute + +```javascript +import { transform, read, write } from 'jy-transform'; + +// --- transform + +const transformOptions = { + src: 'foo/bar.yaml', + target: 'foo/bar.json', + indent: 4, +}; + +const transformFunc = async (object) => { + object.foo = 'new value'; + return object; +}; + +try { + const msg = await transform(transformOptions, transformFunc); + console.log(msg); +} catch (err) { + console.error(err.stack); +} + +// --- read + +let object; + +try { + object = await read({ src: 'foo/bar.yaml' }); + console.log(JSON.stringify(object)); +} catch (err) { + console.error(err.stack); +} + +// --- write -Why this module? After struggling with some huge YAML file and accidentally -occurring wrong indentions which results in an annoying failure investigation, +try { + const msg = await write(object, { dest: 'foo/bar.yaml' }); + console.log(msg); +} catch (err) { + console.error(err.stack); +} +``` + +## Why This Module? + +After struggling with some huge YAML file and accidentally +occurring wrong indentions which results in an annoying investigation hell, I decided to get rid of the YAML file and therefore, create a module which should be aimed as the swiss army knife for transforming YAML, JS and JSON types into each other format. -# Usage +## Usage The module can be used on CLI or as API (the latter is fully [Promise](http://bluebirdjs.com/docs/api-reference.html) based). -## Usage Types +### Usage Types Since the module can be used in two different ways, use installation as follows: @@ -138,7 +99,7 @@ Since the module can be used in two different ways, use installation as follows: Both usage types are described in more detail in the following sections. -## Use Cases +### Use Cases So, what are the typical use cases for this module? In terms of _transformation_ these consists of different phases: @@ -148,7 +109,7 @@ these consists of different phases: - Apply dedicated actions on the intermediate JSON objects (`Transformer` + `Middleware`) - Writing files (`Writer`) -### Reading +#### Reading Reading from: @@ -164,7 +125,7 @@ Additionally, on API level to a: - Reads as UTF-8 - JS `object` (actually, this means the reading phase is skipped, because object is in-memory already) -### Transformation +#### Transformation The transformation can take place into several directions: @@ -184,12 +145,12 @@ while: - [JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript) = _*.js_ (JS object) - [JSON](http://json.org) = _*.json_ (JS object serialized as JSON) -### Middleware +#### Middleware Apply actions on the intermediate JS object via injected [Promise](http://bluebirdjs.com/docs/api-reference.html) functions. This is an optional part for [transformation](#transformation) phase. -### Writing +#### Writing Writing to: @@ -205,17 +166,17 @@ Additionally, on API level to a: - Writes UTF-8 - JS `object` -## Limitations +### Limitations - Since this module is build to transform from and to different type formats, any `Function`s residing in JS type objects are _not_ supported, e.g. transforming ```javascript module.exports = { - fooKey: 'foo', - fooFunction: function foo() { - //... - } + fooKey: 'foo', + fooFunction: function foo() { + //... + } } ``` @@ -223,7 +184,7 @@ Additionally, on API level to a: ```json { - "fooKey": "foo" + "fooKey": "foo" } ``` @@ -242,15 +203,15 @@ Additionally, on API level to a: [#1](https://github.com/deadratfink/jy-transform/issues/1) and [#2](https://github.com/deadratfink/jy-transform/issues/2). -## CLI Usage +### CLI Usage -The CLI provides the `jyt.js` command (actually, this might require the use of options). +The CLI provides the `jyt` command which requires the use of some options. After the global installation you can access the `Transformer`'s command options with the usual `--help` option which prints an overview about all available CLI properties: ``` -$ jyt.js --help +$ jyt --help Usage: jyt.js INPUT-FILE [OUTPUT-FILE] [OPTIONS] @@ -271,7 +232,7 @@ Options: -h, --help Display help and usage details ``` -### CLI Args +#### CLI Args The ARGS are more formally defined in the following table: @@ -282,7 +243,7 @@ The ARGS are more formally defined in the following table: **NOTE:** the input file has to be specified and should be _first_ argument (in fact, it can be anywhere but it must be before an out file argument)! -### CLI Options +#### CLI Options The OPTIONS are more formally defined in the following table: @@ -302,7 +263,7 @@ The OPTIONS are more formally defined in the following table: **NOTE:** an invalid indention setting (1 > `-i`, `--indent` > 8) does not raise an error but a default of 4 SPACEs is applied instead. -### Examples +#### Examples Now we know which properties can be applied on CLI, so let's assume we have a YAML content located in _foo.yaml_ holding this data: @@ -310,7 +271,7 @@ have a YAML content located in _foo.yaml_ holding this data: ```yaml foo: bar ``` -#### Example: YAML ⇒ JSON +##### Example: YAML ⇒ JSON Then we can transform it to a JSON content as _foo.json_ file: @@ -323,7 +284,7 @@ Then we can transform it to a JSON content as _foo.json_ file: simply by using this command: ``` -$ jyt.js foo.yaml -t json -i 4 +$ jyt foo.yaml -t json -i 4 ``` In this example we have overwritten the standard target type (which is `js`) @@ -336,16 +297,16 @@ default `js` would have been applied! If the source would have been a `js` type like ``` -$ jyt.js foo.js -t json -i 4 +$ jyt foo.js -t json -i 4 ``` then the `js` value for `origin` is automatically inferred from file extension. Accordingly, this is also true for the `target` option. -#### Example: JSON ⇒ JS +##### Example: JSON ⇒ JS The command ``` -$ jyt.js foo.json -i 4 +$ jyt foo.json -i 4 ``` results in _foo.js_: ```javascript @@ -354,34 +315,34 @@ module.exports = { } ``` -#### Example: JS ⇒ YAML +##### Example: JS ⇒ YAML The command ``` -$ jyt.js foo.js -t yaml +$ jyt foo.js -t yaml ``` results in _foo.yaml_: ```yaml foo: bar ``` -#### Example: Transformation with Different Destination +##### Example: Transformation with Different Destination Simply specify the _output_ file with a different file name: ``` -$ jyt.js foo.json results/foobar.yaml +$ jyt foo.json results/foobar.yaml ``` -#### Example: Transformation with Unsupported Source File Extension +##### Example: Transformation with Unsupported Source File Extension As said, normally we infer from file extension to the type, but assume the source file has a file name which does not imply the type (here a JSON type in a TEXT file), then you can simply provide the `-o` option with the correct `origin` type (of course, the `-t` option works analogous): ``` -$ jyt.js foo.txt foobar.yaml -o json +$ jyt foo.txt foobar.yaml -o json ``` -#### Example: Read from File with Exports Identifier +##### Example: Read from File with Exports Identifier It could be that a JS source `exports` several objects and you want to read from exactly the one you specify, then provide the `-m` (`--imports`) option. @@ -399,7 +360,7 @@ module.exports.bar = { ``` but you want to convert only `bar` object, then call: ``` -$ jyt.js foo.js bar.yaml -m bar +$ jyt foo.js bar.yaml -m bar ``` to get the YAML result: ```yaml @@ -409,12 +370,12 @@ bar: foo **NOTE:** the same applies on API level when using JS objects as `dest`: ```javascript -var fooBar = { +const fooBar = { foo: 'bar', bar: 'foo' }; -var options = { +const options = { src: fooBar, dest: {}, exports: 'bar' @@ -433,7 +394,7 @@ bar: { ``` Of course, as sub-node of `options.dest`. -#### Example: Write Exports Identifier for JS File +##### Example: Write Exports Identifier for JS File Assume you want to generate a JS file with an exports string which gets an identifier. We reuse the YAML file from above: @@ -442,7 +403,7 @@ foo: bar ``` using this command: ``` -$ jyt.js foo.yaml foobar.js -x foobar +$ jyt foo.yaml foobar.js -x foobar ``` This generates the following output in JS file using `foobar` as identifier: ```javascript @@ -455,7 +416,7 @@ module.exports.foobar = { (see also [Valid JavaScript variable names in ECMAScript 6](https://mathiasbynens.be/notes/javascript-identifiers-es6) and [Generating a regular expression to match valid JavaScript identifiers](https://mathiasbynens.be/demo/javascript-identifier-regex)). -#### Example: Force Overwriting +##### Example: Force Overwriting **IMPORTANT NOTE:** when using this feature then any subsequent execution which uses the same target/file name, @@ -467,14 +428,14 @@ overwriting your input source or already generated targets. But let's say we want to overwrite the original source now because you want to change the indention from 2 to 4 SPACEs, then we can do this as follows: ``` -$ jyt.js foo.js -f +$ jyt foo.js -f ``` Of course, leaving out the `-f` switch creates a new file relatively to the `origin`, named as _foo(1).js_ (note the consecutive number). Naturally, another run of the command would result in a file called _foo(2).js_ and so forth. -## Origin and Target Type Inference +### Origin and Target Type Inference The examples above have shown that we have an automatic type inference from file extensions. This is supported as shown by the following table (from-to): @@ -489,7 +450,7 @@ extensions. This is supported as shown by the following table (from-to): **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! -## API Usage +### API Usage Since the usage on CLI is a 2-step process: @@ -509,7 +470,7 @@ For more details about this and all the functions provided by this module please The `origin` and `target` type inference is also standard for the API level. -### API Properties +#### API Properties The `Transformer` exposes the following function which takes besides an (optional) `middleware` function the necessary `options` for the transformation: @@ -518,15 +479,22 @@ The `Transformer` exposes the following function which takes besides an (optiona async function transform(options, middleware) ``` -#### Transformer Options +#### Options The `options` object has to follow this key-values table: +##### Reader Options + | Option | Type | Description | Default | Required | | --- | --- | --- | --- | --- | | `origin` | _String_ | The origin type. | If not given, the type is tried to be inferred from the extension of source path, else it is _yaml_. | no | | `target` | _String_ | The target type. | If not given, the type is tried to be inferred from the extension of destination path, else it is _js_. | no | | `src` | [ _String_ | _Stream.Readable_ | _Object_ ] | The source information object: `String` is used as file path, `Stream.Readable` provides a stringified source and `object` is used as direct JS source. | - | yes | + +##### Writer Options + +| Option | Type | Description | Default | Required | +| --- | --- | --- | --- | --- | | `dest` | [ _String_ | _Stream.Writable_ | _Object_ ] | The destination information object: `String` is used as file path, `Stream.Writable` writes a stringified source and `object` is used for direct JS object assignment of the (stringified or JS object) source. If a string is set as file path then the output and if input and output file path are the same, then the file overwriting is handled depending on the `force` value! | - | yes | | `indent` | _Number_ | The indention in files. | 2 | no | | `force` | _Boolean_ | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | @@ -535,10 +503,14 @@ The `options` object has to follow this key-values table: **NOTE:** an invalid indention setting (1 > indent > 8) does not raise an error but a default of 2 SPACEs is applied instead. -#### Example +##### Transformer Options + +These are a combination of the [Reader](#reader-options) and [Writer](#writer-options) Options. + +##### Example ```javascript -var options = { +const options = { origin: 'json', target: 'yaml', src: 'foo.json', @@ -547,7 +519,7 @@ var options = { } ``` -### Using Middleware +#### Using Middleware The `middleware` is optional but if provided it must be of type `Function` and a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). @@ -560,7 +532,7 @@ function as follows: ```javascript const identity = async (data) => { - return data; + return data; } ``` @@ -580,18 +552,16 @@ Applying this [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/ as middleware ```javascript +import { transform } from 'jy-transform'; + const middleware = async (data) => { data.foo = 'new bar'; return data; }; transform(options, middleware) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); + .then(console.log) + .catch(console.error); ``` will result in such JSON file: @@ -626,8 +596,8 @@ const key2 = async (data) => { }; const key3 = async (data) => { - objectPath.set(data, 'key3', 'value3'); - return data; + objectPath.set(data, 'key3', 'value3'); + return data; }; ``` @@ -636,20 +606,16 @@ Promise framework, e.g. using the [`Promise.all([...])`](https://developer.mozil function. This one can collect all three functions above and ensure their proper subsequent execution: ```javascript +import { transform } from 'jy-transform'; + const middleware = (data) => { return Promise.all([key1(data), key2(data), key3(data)]) .then(result => result[result.length - 1]); }; -var logger = new Logger(); -var transformer = new Transformer(logger); -var options = { - src: {} -}; - -return transform(options, middleware) - .then(msg => logger.info(msg)) - .catch(err => logger.error(err.stack)); +return transform({ src: {} }, middleware) + .then(console.log) + .catch(console.error); ``` Then the result in the `middleware` function can be retrieved from the returned @@ -676,47 +642,18 @@ you can do almost everything with the JS object, like Whatever you do during transformation, just keep it valid ;-) -## Using Custom Logger - -It is usual that you use an own `logger` in your application. This module supports you by -letting you inject your logger as constructor argument: the `Reader`, `Transformer` and -`Writer` constructor will accept an (optional) logger object. - -If you do not provide one, then the default logger is `console`. - -```javascript -var logger = ...; - -var reader = new Reader(logger); -var transformer = new Transformer(logger); -var writer = new Writer(logger); -``` - -At least, the passed `logger` object _has_ to support the following functions: - -```javascript -function info(msg) -function debug(msg) -function trace(msg) -function error(err|msg) -``` -Anyway, there are some fallbacks if a level is not supported: - -- DEBUG ⇒ INFO -- TRACE ⇒ DEBUG - -# API Reference +## API Reference For more details on how to use the API, please refer to the [API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v3) wiki which describes the full API and provides more examples. -# Contributing +## Contributing Pull requests and stars are always welcome. Anybody is invited to take part into this project. For bugs and feature requests, please create an [issue](https://github.com/deadratfink/jy-transform/issues). -See the wiki [Contributing](https://github.com/deadratfink/jy-transform/wiki/Changelog) +See the wiki [Contributing](https://github.com/deadratfink/jy-transform/wiki/Contributing) section for more details about conventions. @@ -731,100 +668,3 @@ section for more details about conventions. - [Makefile Reference](./MAKE.md) -#### v3.0.0 - -- **CLI & API Changes (Backwards Incompatible!):** - - Removed support for Node.js < v4.0 - - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community - -- **API Changes only (Backwards Incompatible!):** - - Prototype removal from `Transformer`, `Reader` and `Writer`, turning it to simple exports of functions - - Easier usage by using named imports only for all classes (i.e. also for `Transformer`) - - The formerly exported `middleware` is not public anymore - - Eased interfaces: - - The formerly exported `Reader.readJs(...)|readYaml(...)` functions are not public anymore and replaced by a more simple to use `read(options)` function - - The formerly exported `Writer.writeJs(...)|writeJson(...)|readYaml(...)` functions are not public anymore and replaced by a more simple to use `write(options)` function - - The `options.imports/exports` are not allowed to be empty strings anymore (just leave it out) - - The exported constants `YAML`, `JS` and `JSON` (usable for `options.origin/target`) are renamed respectively to `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` - - `options.dest` is required for `Transfomer` and `Writer` on API usage now - - Removal of `LogWrapper` (no more logger injection possible/needed) - -- Internal Changes & Improvements: - - Removal of _development_ branch - - Usage of [babel](https://babeljs.io/) and therefore most modern language features - - Code base could be shrinked and readabilty improved - - Usage of _native promises_ instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html) - - Tests re-written in [Jest](https://facebook.github.io/jest) (could get rid of [assert](https://github.com/defunctzombie/commonjs-assert), - [mocha](https://mochajs.org/) and [istanbul](https://github.com/gotwarlost/istanbul)) - - Add travis build for Node.js v8.x - - Remove travis build for Node.js < v4.x - - Removal of `OptionsHandler` and `Validator` (replaced validation stuff by [joi](https://github.com/hapijs/joi/tree/v10.5.0)) - -#### v2.0.1 - -- [[#39](https://github.com/deadratfink/jy-transform/issues/39)] Maintenance release - - Update dependencies to latest - - Add travis build for Node.js v7.x and v6.x - - Docs improved/corrected - - Add target pretest in `scripts` section to `rm` _./test/tmp_ folder - -#### v2.0.0 - -- [[#33](https://github.com/deadratfink/jy-transform/issues/33)] Enhance `LogWrapper` with `TRACE` level (API) -- [[#32](https://github.com/deadratfink/jy-transform/issues/32)] Introduce input and output on CLI as ARGS instead of OPTIONS (non-backwards compatible change for CLI usage, _no_ impact on API level!) - - e.g. on CLI type in `$ jyt.js foo.js bar.yaml` instead of `$ jyt.js -s foo.js -d bar.yaml` -- [[#31](https://github.com/deadratfink/jy-transform/issues/31)] Bugfix: given `Object` source results in 'yaml' for origin (API) -- [Cleanup] Update dependencies - -#### v1.0.2 - -- [[#30](https://github.com/deadratfink/jy-transform/issues/30)] Fix README and externalize API reference to wiki -- [[#29](https://github.com/deadratfink/jy-transform/issues/29)] Fix Promise warning on write process - -#### v1.0.1 - -Initial public release. This covers the basic implementation and tests. The following features and fixes and part of this release: - -- [[#27](https://github.com/deadratfink/jy-transform/issues/27)] Export variable for JS input -- [[#22](https://github.com/deadratfink/jy-transform/issues/22)] Integrate Coveralls -- [[#21](https://github.com/deadratfink/jy-transform/issues/21)] Check and fix CodeClimate issues -- [[#20](https://github.com/deadratfink/jy-transform/issues/20)] Cleanup test dir -- [[#19](https://github.com/deadratfink/jy-transform/issues/19)] File overwrite switch (`-f`, `-force`) -- [[#18](https://github.com/deadratfink/jy-transform/issues/18)] Read and Write from other sources than file path -- [[#16](https://github.com/deadratfink/jy-transform/issues/16)] ERROR: Error: Invalid target option found while creating destination file extension -- [[#15](https://github.com/deadratfink/jy-transform/issues/15)] Measure test code coverage and add a badge -- [[#12](https://github.com/deadratfink/jy-transform/issues/12)] Create middleware collection file to use by clients and internally -- [[#11](https://github.com/deadratfink/jy-transform/issues/11)] Check all Promises for optimization possibilities -- [[#10](https://github.com/deadratfink/jy-transform/issues/10)] Integrate project with Travis -- [[#9](https://github.com/deadratfink/jy-transform/issues/9)] Resolve origin and target from file extension whenever possible -- [[#8](https://github.com/deadratfink/jy-transform/issues/8)] Enable JS reading with `require(...)` -- [[#7](https://github.com/deadratfink/jy-transform/issues/7)] YAML indent is not set to `Constants.MIN_YAML_INDENT` when indent is set to 0 -- [[#6](https://github.com/deadratfink/jy-transform/issues/6)] Finish full JSDoc for all methods -- [[#5](https://github.com/deadratfink/jy-transform/issues/5)] Write unit tests -- [[#4](https://github.com/deadratfink/jy-transform/issues/4)] Export variable for JS output -- [[#3](https://github.com/deadratfink/jy-transform/issues/3)] Promise array as middleware solved with `Promise.all([...])` - - -## License - -The MIT License (MIT) - -Copyright (c) 2016 [Jens Krefeldt](https://github.com/deadratfink) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/bin/create-readme.sh b/bin/create-readme.sh index 4db74d5..c01422b 100755 --- a/bin/create-readme.sh +++ b/bin/create-readme.sh @@ -4,7 +4,6 @@ api="true" package="true" changelog="true" license="true" -env="false" makefile="true" while [[ $# -gt 1 ]]; do @@ -48,6 +47,7 @@ if [ "$package" == "true" ]; then touch PACKAGE.md node node_modules/.bin/package-json-to-readme --no-footer package.json > PACKAGE.md cat readme/LOGO.md >> README.md + cat readme/BADGES.md >> README.md printf "\n\n" >> README.md head -12 PACKAGE.md > README.md printf "\n\n" >> README.md @@ -60,8 +60,6 @@ fi # README.md ############################################################################### -cat readme/BADGES.md >> README.md - printf "\n\n\n" >> README.md cat readme/DOCUMENTATION.md >> README.md printf "\n\n" >> README.md @@ -83,7 +81,9 @@ fi if [ "$api" == "true" ]; then printf "Create documentation (API-PUBLIC.md)\n" touch API-PUBLIC.md + printf "\n\n\n" >> API-PUBLIC.md node node_modules/.bin/jsdoc2md --no-cache --configure .jsdoc.json . > API-PUBLIC.md + node node_modules/.bin/doctoc API-PUBLIC.md --github --title "## TOC" --maxlevel 2 printf -- "- [Public Api Reference](./API-PUBLIC.md)" >> README.md printf "\n\n" >> README.md @@ -96,7 +96,9 @@ fi if [ "$api" == "true" ]; then printf "Create documentation (API-PRIVATE.md)\n" touch API-PRIVATE.md + printf "\n\n\n" >> API-PRIVATE.md node node_modules/.bin/jsdoc2md --no-cache --private --configure .jsdoc.json . > API-PRIVATE.md + node node_modules/.bin/doctoc API-PRIVATE.md --github --title "## TOC" --maxlevel 2 printf -- "- [Private Api Reference](./API-PRIVATE.md)" >> README.md printf "\n\n" >> README.md @@ -130,26 +132,12 @@ if [[ -f "$MAKE_FILE" ]] && [[ "$makefile" == "true" ]]; then printf "\n\n" >> README.md fi -############################################################################### -# ENV.md -############################################################################### - -if [ "$env" == "true" ]; then - printf "Create documentation (ENV.md)\n" - - touch ENV.md - babel-node node_modules/.bin/cfg-combined markdown > ENV.md - - printf -- "- [Environment Reference](./ENV.md)" >> README.md - printf "\n\n" >> README.md -fi - ############################################################################### # Finalize README.md ############################################################################### if [ "$changelog" == "true" ]; then - cat readme/CHANGELOG.md >> README.md + cat CHANGELOG.md >> README.md printf "\n\n" >> README.md fi @@ -162,4 +150,4 @@ fi # Create the TOC in README.md ############################################################################### -node node_modules/.bin/doctoc README.md --github --title "## TOC" --maxlevel 2 +node node_modules/.bin/doctoc README.md --github --title "## TOC" --maxlevel 3 diff --git a/index.js b/index.js index b318f5e..aad8f64 100755 --- a/index.js +++ b/index.js @@ -1,7 +1,14 @@ +const Transformer = require('./src/transformer.js').default; +const Reader = require('./src/reader.js').default; +const Writer = require('./src/writer.js').default; +const Constants = require('./src/constants.js'); + module.exports = { - Transformer: require('./src/transformer.js').default, - Reader: require('./src/reader.js').default, - Writer: require('./src/writer.js').default, - Constants: require('./src/constants.js'), + transform: Transformer.transform, + read: Reader.read, + write: Writer.write, + TYPE_YAML: Constants.TYPE_YAML, + TYPE_JS: Constants.TYPE_JS, + TYPE_JSON: Constants.TYPE_JSON, }; diff --git a/jyt b/jyt index be0f0db..0fa0360 100755 --- a/jyt +++ b/jyt @@ -1,4 +1,4 @@ #!./node_modules/.bin/babel-node // eslint-disable-next-line no-unused-vars -import jyt from './src/jyt'; +import jyt from './src/cli'; diff --git a/package.json b/package.json index ad20b1c..1a2ebf3 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "scripts": { "build": "babel src -d lib", "doc": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", - "readme": "bin/create-readme.sh -a true -c true -m true", + "readme": "bin/create-readme.sh -a true -c false -m true -l false", "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", "pretest": "mkdir -pv test/tmp", "test": "JYT_DEBUG=true JYT_ERROR=true jest --expand --no-cache --coverage --runInBand --config=./.jestrc.js", diff --git a/readme/BADGES.md b/readme/BADGES.md index 7262b7b..6f08584 100644 --- a/readme/BADGES.md +++ b/readme/BADGES.md @@ -1,32 +1,8 @@ -# Stats - -## General - -| [License](https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md) | [Issues](https://github.com/deadratfink/jy-transform/issues) | [Releases](https://github.com/deadratfink/jy-transform/releases) | [Tags](https://github.com/deadratfink/jy-transform/tags) | [Travis CI](https://travis-ci.org) | [Waffle](https://waffle.io/deadratfink/jy-transform) | [Code Climate](https://codeclimate.com/github/deadratfink/jy-transform) | -| --- | --- | --- | --- | --- | --- | --- | -| [![License][gh-license-image]][gh-license-url] | [![Issue Stats][gh-issues-image]][gh-issues-url] | [![Releases][gh-releases-image]][gh-releases-url] | [![Tags][gh-tags-image]][gh-tags-url] | [![Build Status][ci-image]][ci-url] | [![Waffle][waffle-image]][waffle-url] | [![Code Climate][cocl-image]][cocl-url] | - -## Branches - -| Branch | [Codecov](https://codecov.io) | [Coveralls](https://coveralls.io) | [Inch CI](http://inch-ci.org) | [David](https://david-dm.org) DM | [David](https://david-dm.org) DM (dev) | -| --- | --- | --- | --- | --- | --- | -| master | [![codecov.io][cc-image-master]][cc-url-master] | [![coveralls.io][ca-image-master]][ca-url-master] | [![inch-ci.org][inch-image-master]][inch-url-master] | [![Dependency Status][dep-image-master]][dep-url-master] | [![devDependency Status][devdep-image-master]][devdep-url-master] | -| development | [![codecov.io][cc-image-development]][cc-url-development] | [![coveralls.io][ca-image-development]][ca-url-development] | [![inch-ci.org][inch-image-development]][inch-url-development] | [![Dependency Status][dep-image-development]][dep-url-development] | [![devDependency Status][devdep-image-development]][devdep-url-development] | - -### Coverage - -| master | development | -| --- | --- | -| ![codecov.io](https://codecov.io/gh/deadratfink/jy-transform/branch/master/graphs/tree.svg) | ![codecov.io](https://codecov.io/gh/deadratfink/jy-transform/branch/development/graphs/tree.svg) | - - - - - - -## NPM +[![License][gh-license-image]][gh-license-url] [![Issue Stats][gh-issues-image]][gh-issues-url] [![Releases][gh-releases-image]][gh-releases-url] [![Tags][gh-tags-image]][gh-tags-url] [![Build Status][ci-image]][ci-url] [![Waffle][waffle-image]][waffle-url] [![Code Climate][cocl-image]][cocl-url] +[![codecov.io][cc-image-master]][cc-url-master] [![coveralls.io][ca-image-master]][ca-url-master] [![inch-ci.org][inch-image-master]][inch-url-master] [![Dependency Status][dep-image-master]][dep-url-master] [![devDependency Status][devdep-image-master]][devdep-url-master] [![NPM](https://nodei.co/npm/jy-transform.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/jy-transform/) + [![NPM](https://nodei.co/npm-dl/jy-transform.png?height=3&months=9)](https://nodei.co/npm-dl/jy-transform/) [gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat-square @@ -58,27 +34,17 @@ [cc-image-master]: https://img.shields.io/codecov/c/github/deadratfink/jy-transform/master.svg?style=flat-square [cc-url-master]: https://codecov.io/github/deadratfink/jy-transform?branch=master -[cc-image-development]: https://img.shields.io/codecov/c/github/deadratfink/jy-transform/development.svg?style=flat-square -[cc-url-development]: https://codecov.io/github/deadratfink/jy-transform?branch=development [ca-image-master]: https://img.shields.io/coveralls/deadratfink/jy-transform/master.svg?style=flat-square [ca-url-master]: https://coveralls.io/github/deadratfink/jy-transform?branch=master -[ca-image-development]: https://img.shields.io/coveralls/deadratfink/jy-transform/development.svg?style=flat-square -[ca-url-development]: https://coveralls.io/github/deadratfink/jy-transform?branch=development [inch-image-master]: https://inch-ci.org/github/deadratfink/jy-transform.svg?branch=master&style=flat-square [inch-url-master]: https://inch-ci.org/github/deadratfink/jy-transform?branch=master -[inch-image-development]: https://inch-ci.org/github/deadratfink/jy-transform.svg?branch=development&style=flat-square -[inch-url-development]: https://inch-ci.org/github/deadratfink/jy-transform?branch=development [dep-image-master]: https://img.shields.io/david/deadratfink/jy-transform/master.svg?style=flat-square [dep-url-master]: https://david-dm.org/deadratfink/jy-transform/master -[dep-image-development]: https://img.shields.io/david/deadratfink/jy-transform/development.svg?style=flat-square -[dep-url-development]: https://david-dm.org/deadratfink/jy-transform/development [devdep-image-master]: https://img.shields.io/david/dev/deadratfink/jy-transform/master.svg?style=flat-square [devdep-url-master]: https://david-dm.org/deadratfink/jy-transform/master#info=devDependencies -[devdep-image-development]: https://img.shields.io/david/dev/deadratfink/jy-transform/development.svg?style=flat-square -[devdep-url-development]: https://david-dm.org/deadratfink/jy-transform/development#info=devDependencies diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index 5701a47..9af7e33 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -1,17 +1,68 @@ -## Motivation +## API in a Minute -Why this module? After struggling with some huge YAML file and accidentally -occurring wrong indentions which results in an annoying failure investigation, +```javascript +import { transform, read, write } from 'jy-transform'; + + +// --- transform from source to destination --- + +const transformOptions = { + src: 'foo/bar.yaml', + target: 'foo/bar.json', + indent: 4, +}; + +const transformFunc = async (object) => { + object.foo = 'new value'; + return object; +}; + +// of course, inside an async +try { + const msg = await transform(transformOptions, transformFunc); + console.log(msg); +} catch (err) { + console.error(err.stack); +} + + +// --- read into JS object from particular source (file, stream or JS object) --- + +let object; + +try { + object = await read({ src: 'foo/bar.yaml' }); // here: read from file + console.log(JSON.stringify(object)); +} catch (err) { + console.error(err.stack); +} + + +// --- write a JS object to particular destination --- + +try { + const msg = await write(object, { dest: 'foo/bar.yaml' }); + console.log(msg); +} catch (err) { + console.error(err.stack); +} +``` + +## Why This Module? + +After struggling with some huge YAML file and accidentally +occurring wrong indentions which results in an annoying investigation hell, I decided to get rid of the YAML file and therefore, create a module which should be aimed as the swiss army knife for transforming YAML, JS and JSON types into each other format. -# Usage +## Usage -The module can be used on CLI or as API (the latter is fully [Promise](http://bluebirdjs.com/docs/api-reference.html) +The module can be used on CLI or as API (the latter is fully +[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)) based). -## Usage Types +### Usage Types Since the module can be used in two different ways, use installation as follows: @@ -20,7 +71,7 @@ Since the module can be used in two different ways, use installation as follows: Both usage types are described in more detail in the following sections. -## Use Cases +### Use Cases So, what are the typical use cases for this module? In terms of _transformation_ these consists of different phases: @@ -30,25 +81,28 @@ these consists of different phases: - Apply dedicated actions on the intermediate JSON objects (`Transformer` + `Middleware`) - Writing files (`Writer`) -### Reading +#### Reading -Reading from: +From: - _*.yaml_ file - _*.js_ file - _*.json_ file -Additionally, on API level to a: +Additionally, on API level from a: - `stream.Readable` - - Serialized JSON and YAML - - Requires `options.origin` property set + - Contain serialized JS, JSON or YAML + - If not file stream it requires `options.origin` property set - Reads as UTF-8 -- JS `object` (actually, this means the reading phase is skipped, because object is in-memory already) +- JS `object` + - Actually, this means the reading phase is "skipped", because object is in-memory already + - Of course, this case _cannot_ not be applied to serialized JSON or YAML content -### Transformation +#### Transformation [+ Middleware] -The transformation can take place into several directions: +The _transformation_ is usually a format change, but can also be refer to content changes on the +intermediate JS object (the latter via _middleware_). All possible directions are: - YAML ⇒ JS - YAML ⇒ JSON @@ -66,14 +120,12 @@ while: - [JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript) = _*.js_ (JS object) - [JSON](http://json.org) = _*.json_ (JS object serialized as JSON) -### Middleware - -Apply actions on the intermediate JS object via injected [Promise](http://bluebirdjs.com/docs/api-reference.html) -functions. This is an optional part for [transformation](#transformation) phase. +As mentioned above a _middleware_ can apply particular actions on the intermediate JS object via injected functions. +This is an optional part for [transformation](#transformation) phase. -### Writing +#### Writing -Writing to: +To: - _*.yaml_ file - _*.js_ file @@ -85,19 +137,21 @@ Additionally, on API level to a: - Serialized JS, JSON and YAML - Requires `options.target` property set - Writes UTF-8 -- JS `object` +- JS `object + - JS as simple reference + - YAML and JSON as serialized string -## Limitations +### Limitations - Since this module is build to transform from and to different type formats, any `Function`s residing in JS type objects are _not_ supported, e.g. transforming ```javascript module.exports = { - fooKey: 'foo', - fooFunction: function foo() { - //... - } + fooKey: 'foo', + fooFunction: function foo() { + //... + } } ``` @@ -105,7 +159,7 @@ Additionally, on API level to a: ```json { - "fooKey": "foo" + "fooKey": "foo" } ``` @@ -124,15 +178,15 @@ Additionally, on API level to a: [#1](https://github.com/deadratfink/jy-transform/issues/1) and [#2](https://github.com/deadratfink/jy-transform/issues/2). -## CLI Usage +### CLI Usage -The CLI provides the `jyt.js` command (actually, this might require the use of options). +The CLI provides the `jyt` command which requires the use of some options. After the global installation you can access the `Transformer`'s command options with the usual `--help` option which prints an overview about all available CLI properties: ``` -$ jyt.js --help +$ jyt --help Usage: jyt.js INPUT-FILE [OUTPUT-FILE] [OPTIONS] @@ -153,7 +207,7 @@ Options: -h, --help Display help and usage details ``` -### CLI Args +#### CLI Args The ARGS are more formally defined in the following table: @@ -164,7 +218,7 @@ The ARGS are more formally defined in the following table: **NOTE:** the input file has to be specified and should be _first_ argument (in fact, it can be anywhere but it must be before an out file argument)! -### CLI Options +#### CLI Options The OPTIONS are more formally defined in the following table: @@ -184,7 +238,7 @@ The OPTIONS are more formally defined in the following table: **NOTE:** an invalid indention setting (1 > `-i`, `--indent` > 8) does not raise an error but a default of 4 SPACEs is applied instead. -### Examples +#### Examples Now we know which properties can be applied on CLI, so let's assume we have a YAML content located in _foo.yaml_ holding this data: @@ -192,7 +246,7 @@ have a YAML content located in _foo.yaml_ holding this data: ```yaml foo: bar ``` -#### Example: YAML ⇒ JSON +##### Example: YAML ⇒ JSON Then we can transform it to a JSON content as _foo.json_ file: @@ -205,7 +259,7 @@ Then we can transform it to a JSON content as _foo.json_ file: simply by using this command: ``` -$ jyt.js foo.yaml -t json -i 4 +$ jyt foo.yaml -t json -i 4 ``` In this example we have overwritten the standard target type (which is `js`) @@ -218,16 +272,16 @@ default `js` would have been applied! If the source would have been a `js` type like ``` -$ jyt.js foo.js -t json -i 4 +$ jyt foo.js -t json -i 4 ``` then the `js` value for `origin` is automatically inferred from file extension. Accordingly, this is also true for the `target` option. -#### Example: JSON ⇒ JS +##### Example: JSON ⇒ JS The command ``` -$ jyt.js foo.json -i 4 +$ jyt foo.json -i 4 ``` results in _foo.js_: ```javascript @@ -236,34 +290,34 @@ module.exports = { } ``` -#### Example: JS ⇒ YAML +##### Example: JS ⇒ YAML The command ``` -$ jyt.js foo.js -t yaml +$ jyt foo.js -t yaml ``` results in _foo.yaml_: ```yaml foo: bar ``` -#### Example: Transformation with Different Destination +##### Example: Transformation with Different Destination Simply specify the _output_ file with a different file name: ``` -$ jyt.js foo.json results/foobar.yaml +$ jyt foo.json results/foobar.yaml ``` -#### Example: Transformation with Unsupported Source File Extension +##### Example: Transformation with Unsupported Source File Extension As said, normally we infer from file extension to the type, but assume the source file has a file name which does not imply the type (here a JSON type in a TEXT file), then you can simply provide the `-o` option with the correct `origin` type (of course, the `-t` option works analogous): ``` -$ jyt.js foo.txt foobar.yaml -o json +$ jyt foo.txt foobar.yaml -o json ``` -#### Example: Read from File with Exports Identifier +##### Example: Read from File with Exports Identifier It could be that a JS source `exports` several objects and you want to read from exactly the one you specify, then provide the `-m` (`--imports`) option. @@ -281,7 +335,7 @@ module.exports.bar = { ``` but you want to convert only `bar` object, then call: ``` -$ jyt.js foo.js bar.yaml -m bar +$ jyt foo.js bar.yaml -m bar ``` to get the YAML result: ```yaml @@ -291,12 +345,12 @@ bar: foo **NOTE:** the same applies on API level when using JS objects as `dest`: ```javascript -var fooBar = { +const fooBar = { foo: 'bar', bar: 'foo' }; -var options = { +const options = { src: fooBar, dest: {}, exports: 'bar' @@ -315,7 +369,7 @@ bar: { ``` Of course, as sub-node of `options.dest`. -#### Example: Write Exports Identifier for JS File +##### Example: Write Exports Identifier for JS File Assume you want to generate a JS file with an exports string which gets an identifier. We reuse the YAML file from above: @@ -324,7 +378,7 @@ foo: bar ``` using this command: ``` -$ jyt.js foo.yaml foobar.js -x foobar +$ jyt foo.yaml foobar.js -x foobar ``` This generates the following output in JS file using `foobar` as identifier: ```javascript @@ -337,7 +391,7 @@ module.exports.foobar = { (see also [Valid JavaScript variable names in ECMAScript 6](https://mathiasbynens.be/notes/javascript-identifiers-es6) and [Generating a regular expression to match valid JavaScript identifiers](https://mathiasbynens.be/demo/javascript-identifier-regex)). -#### Example: Force Overwriting +##### Example: Force Overwriting **IMPORTANT NOTE:** when using this feature then any subsequent execution which uses the same target/file name, @@ -349,14 +403,14 @@ overwriting your input source or already generated targets. But let's say we want to overwrite the original source now because you want to change the indention from 2 to 4 SPACEs, then we can do this as follows: ``` -$ jyt.js foo.js -f +$ jyt foo.js -f ``` Of course, leaving out the `-f` switch creates a new file relatively to the `origin`, named as _foo(1).js_ (note the consecutive number). Naturally, another run of the command would result in a file called _foo(2).js_ and so forth. -## Origin and Target Type Inference +### Origin and Target Type Inference The examples above have shown that we have an automatic type inference from file extensions. This is supported as shown by the following table (from-to): @@ -371,7 +425,7 @@ extensions. This is supported as shown by the following table (from-to): **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! -## API Usage +### API Usage Since the usage on CLI is a 2-step process: @@ -391,7 +445,7 @@ For more details about this and all the functions provided by this module please The `origin` and `target` type inference is also standard for the API level. -### API Properties +#### API Properties The `Transformer` exposes the following function which takes besides an (optional) `middleware` function the necessary `options` for the transformation: @@ -400,15 +454,22 @@ The `Transformer` exposes the following function which takes besides an (optiona async function transform(options, middleware) ``` -#### Transformer Options +#### Options The `options` object has to follow this key-values table: +##### Reader Options + | Option | Type | Description | Default | Required | | --- | --- | --- | --- | --- | | `origin` | _String_ | The origin type. | If not given, the type is tried to be inferred from the extension of source path, else it is _yaml_. | no | | `target` | _String_ | The target type. | If not given, the type is tried to be inferred from the extension of destination path, else it is _js_. | no | | `src` | [ _String_ | _Stream.Readable_ | _Object_ ] | The source information object: `String` is used as file path, `Stream.Readable` provides a stringified source and `object` is used as direct JS source. | - | yes | + +##### Writer Options + +| Option | Type | Description | Default | Required | +| --- | --- | --- | --- | --- | | `dest` | [ _String_ | _Stream.Writable_ | _Object_ ] | The destination information object: `String` is used as file path, `Stream.Writable` writes a stringified source and `object` is used for direct JS object assignment of the (stringified or JS object) source. If a string is set as file path then the output and if input and output file path are the same, then the file overwriting is handled depending on the `force` value! | - | yes | | `indent` | _Number_ | The indention in files. | 2 | no | | `force` | _Boolean_ | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | @@ -417,10 +478,14 @@ The `options` object has to follow this key-values table: **NOTE:** an invalid indention setting (1 > indent > 8) does not raise an error but a default of 2 SPACEs is applied instead. -#### Example +##### Transformer Options + +These are a combination of the [Reader](#reader-options) and [Writer](#writer-options) Options. + +##### Example ```javascript -var options = { +const options = { origin: 'json', target: 'yaml', src: 'foo.json', @@ -429,7 +494,7 @@ var options = { } ``` -### Using Middleware +#### Using Middleware The `middleware` is optional but if provided it must be of type `Function` and a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). @@ -442,7 +507,7 @@ function as follows: ```javascript const identity = async (data) => { - return data; + return data; } ``` @@ -462,18 +527,16 @@ Applying this [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/ as middleware ```javascript +import { transform } from 'jy-transform'; + const middleware = async (data) => { data.foo = 'new bar'; return data; }; transform(options, middleware) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); + .then(console.log) + .catch(console.error); ``` will result in such JSON file: @@ -508,8 +571,8 @@ const key2 = async (data) => { }; const key3 = async (data) => { - objectPath.set(data, 'key3', 'value3'); - return data; + objectPath.set(data, 'key3', 'value3'); + return data; }; ``` @@ -518,20 +581,16 @@ Promise framework, e.g. using the [`Promise.all([...])`](https://developer.mozil function. This one can collect all three functions above and ensure their proper subsequent execution: ```javascript +import { transform } from 'jy-transform'; + const middleware = (data) => { return Promise.all([key1(data), key2(data), key3(data)]) .then(result => result[result.length - 1]); }; -var logger = new Logger(); -var transformer = new Transformer(logger); -var options = { - src: {} -}; - -return transform(options, middleware) - .then(msg => logger.info(msg)) - .catch(err => logger.error(err.stack)); +return transform({ src: {} }, middleware) + .then(console.log) + .catch(console.error); ``` Then the result in the `middleware` function can be retrieved from the returned @@ -558,46 +617,17 @@ you can do almost everything with the JS object, like Whatever you do during transformation, just keep it valid ;-) -## Using Custom Logger - -It is usual that you use an own `logger` in your application. This module supports you by -letting you inject your logger as constructor argument: the `Reader`, `Transformer` and -`Writer` constructor will accept an (optional) logger object. - -If you do not provide one, then the default logger is `console`. - -```javascript -var logger = ...; - -var reader = new Reader(logger); -var transformer = new Transformer(logger); -var writer = new Writer(logger); -``` - -At least, the passed `logger` object _has_ to support the following functions: - -```javascript -function info(msg) -function debug(msg) -function trace(msg) -function error(err|msg) -``` -Anyway, there are some fallbacks if a level is not supported: - -- DEBUG ⇒ INFO -- TRACE ⇒ DEBUG - -# API Reference +## API Reference For more details on how to use the API, please refer to the [API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v3) wiki which describes the full API and provides more examples. -# Contributing +## Contributing Pull requests and stars are always welcome. Anybody is invited to take part into this project. For bugs and feature requests, please create an [issue](https://github.com/deadratfink/jy-transform/issues). -See the wiki [Contributing](https://github.com/deadratfink/jy-transform/wiki/Changelog) +See the wiki [Contributing](https://github.com/deadratfink/jy-transform/wiki/Contributing) section for more details about conventions. diff --git a/readme/QA.md b/readme/QA.md new file mode 100644 index 0000000..9c13979 --- /dev/null +++ b/readme/QA.md @@ -0,0 +1,22 @@ +## Master Branch Coverage + +### Grid + +Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively. + +![codecov.io](https://codecov.io/gh/deadratfink/jy-transform/branch/master/graphs/tree.svg) + +### Sunburst +The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is represented by the number of statements and the coverage, respectively. + +![codecov.io](https://codecov.io/gh/deadratfink/jy-transform/branch/master/graphs/sunburst.svg) + +### Icicle + +The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is represented by the number of statements and the coverage, respectively. + +![codecov.io](https://codecov.io/gh/deadratfink/jy-transform/branch/master/graphs/icicle.svg) + + + + diff --git a/readme/TOC.md b/readme/TOC.md deleted file mode 100644 index 46af769..0000000 --- a/readme/TOC.md +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/jyt.js b/src/cli.js similarity index 100% rename from src/jyt.js rename to src/cli.js diff --git a/src/debug-log.js b/src/debug-log.js index 046b072..0bc339f 100644 --- a/src/debug-log.js +++ b/src/debug-log.js @@ -10,12 +10,18 @@ /** * DEBUG function. + * * @protected */ export const debug = process.env.JYT_DEBUG === 'true' ? console.log.bind(null, '[DEBUG][jyt.js]:') : (() => null); +/** + * DEBUG function. + * + * @protected + */ export const error = process.env.JYT_ERROR === 'true' ? console.error.bind(null, '[ERROR][jyt.js]:') : (() => null); diff --git a/src/middleware.js b/src/middleware.js deleted file mode 100644 index 8aea156..0000000 --- a/src/middleware.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @module jy-transform:middleware - * @description The middleware ensuring functions module. - * @private - */ - -/** - * Promise which reflects the identity of passed JSON: `f(object) → object`. - * - * @param {Object} object - The JS object which is resolved by Promise. - * @returns {Promise.} - A Promise resolving the passed JS `object`. - * @private - */ -function identity(object) { - return Promise.resolve(object); -} - -/** - * Middleware Promise which reflects the identity of passed JS: `f(object) → object`. - * - * @param {Object} object - The object which is returned in Promise. - * @returns {Promise.} A Promise resolving the passed JS object. - * @example - * import { identityMiddleware } from './lib/middleware'; - * transformer.transform(options, identityMiddleware) - * .then((object) => { - * // ... - * }): - * @protected - */ -export function identityMiddleware(object) { // TODO remove - return identity(object); -} - -/** - * Ensure that the given middleware Promise is a function if set. - * If not set a new JSON 'identity' Promise is returned which simply passes - * a JSON object. - * - * @param {Function} middleware - This middleware Promise can be used to intercept - * the JSON object for altering he passed JSON, the function signature is - * `function(object).` **NOTE:** the Promise has to return the processed JSON. - * @returns {Function} - The given middleware Promise or a new JSON 'identity' middleware Promise function. - * @throws {TypeError} - Will throw this error when the passed `middleware` is not type of `Function`. - * @example - * import { ensureMiddleware } from './lib/middleware'; - * const myMiddleware = async (object) => { - * //...do something with object - * return object; - * }; - * transformer.transform(options, ensureMiddleware(myMiddleware)) - * .then((transformedObject) => { - * //... - * }): - * @protected - */ -export function ensureMiddleware(middleware) { - if (middleware !== undefined && (typeof middleware !== 'function')) { - throw new TypeError('The provided middleware is not a Function type'); - } - if (!middleware) { - return identity; - } - return middleware; -} - -export default { - identityMiddleware, - ensureMiddleware, -}; diff --git a/src/reader.js b/src/reader.js index a11214e..ec2da13 100644 --- a/src/reader.js +++ b/src/reader.js @@ -35,7 +35,7 @@ const fsPromisified = promisify(fs); * @returns {Function} - The function which reads and buffers. * @private */ -function createReadableFunction(readable, bufs) { +function createOnReadableEventFunction(readable, bufs) { return () => { let chunk; while (null !== (chunk = readable.read())) { @@ -55,12 +55,12 @@ function createReadableFunction(readable, bufs) { * @private */ function readFromStream(readable, resolve, reject, origin) { - const bufs = []; + const buffers = []; readable - .on('readable', createReadableFunction(readable, bufs)) - .on('error', err => reject(err)) + .on('readable', createOnReadableEventFunction(readable, buffers)) // TODO better: .on('data', (data) => buffers.push(data)) ?? + .on('error', reject) .on('end', () => { - const buffer = Buffer.concat(bufs); + const buffer = Buffer.concat(buffers); try { logger.debug(origin + ' reading from Readable'); if (origin === TYPE_JSON || origin === TYPE_JS) { @@ -86,97 +86,46 @@ function readFromStream(readable, resolve, reject, origin) { * @param {Options} options - Contains the JS/JSON source reference to read from. * @returns {Promise.} Contains the read JS object. * @private - * @example - * var Reader = require('jy-transform').Reader; - * var logger = ...; - * var reader = new Reader(logger); - * - * // --- from file path - * - * var options = { - * src: 'foo.js' - * }; - * - * reader.readJs(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // --- from Readable - * - * options = { - * src: fs.createReadStream('foo.js') - * }; - * - * reader.readJs(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // --- from object - * - * options = { - * src: { - * foo: 'bar' - * } - * }; - * - * reader.readJs(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); */ async function readJs(options) { logger.debug('OPTIONS BEFORE ASSERTING IN readJs:::' + JSON.stringify(options)); - const assertedOptions = await Joi.validate(options, readerOptionsSchema); return new Promise((resolve, reject) => { - if (typeof assertedOptions.src === 'string') { + if (typeof options.src === 'string') { // path to JSON or JS file try { - const resolvedPath = path.resolve('', assertedOptions.src); + const resolvedPath = path.resolve('', options.src); - if ((path.extname(assertedOptions.src) === '.js' || options.origin === TYPE_JS) && options.imports) { + if (options.imports) { // eslint-disable-next-line import/no-dynamic-require, global-require - const json = require(resolvedPath)[options.imports]; - logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(json, null, 4)); - if (!json) { + const object = require(resolvedPath)[options.imports]; + logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(object, null, 4)); + if (!object) { reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object' + - ' but could not find this object, pls ensure that file ' + assertedOptions.src + + ' but could not find this object, pls ensure that file ' + options.src + ' contains it.')); } else { - resolve(json); + resolve(object); } } else { // eslint-disable-next-line import/no-dynamic-require, global-require - resolve(require(resolvedPath)); + resolve(require(resolvedPath)); // reads both: JS and JSON! } } catch (err) { // probably a SyntaxError logger.error('Unexpected error: ' + err.stack); reject(err); } - } else if (isStream.readable(assertedOptions.src)) { - readFromStream(assertedOptions.src, resolve, reject, TYPE_JSON); - } else if (options.imports) { - const obj = assertedOptions.src[options.imports]; - logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(obj, null, 4)); - if (!obj) { + } else if (isStream.readable(options.src)) { + readFromStream(options.src, resolve, reject, TYPE_JSON); // reads both: JS or JSON! + } else if (options.imports) { // options.src is JS object here! + const subObject = options.src[options.imports]; + logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(subObject, null, 4)); + if (!subObject) { reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object ' + 'but could not find this object, pls ensure that object source contains it.')); } else { - resolve(Object.assign({}, obj)); // clone, do not alter original object! + resolve(Object.assign({}, subObject)); // clone, do not alter original object! } - } else { - resolve(Object.assign({}, assertedOptions.src)); // clone, do not alter original object! + } else { // // options.src is JS object here! + resolve(Object.assign({}, options.src)); // clone, do not alter original object! } }); } @@ -189,49 +138,15 @@ async function readJs(options) { * @param {Options} options - Contains the YAML source reference to read from. * @returns {Promise.} Contains the read JS object. * @private - * @example - * var Reader = require('jy-transform').Reader; - * var logger = ...; - * var reader = new Reader(logger); - * - * // --- from file path - * - * options = { - * src: 'foo.yml' - * }; - * - * reader.readYaml(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // --- from Readable - * - * options = { - * src: fs.createReadStream('foo.yml') - * }; - * - * reader.readJs(options) - * .then(function (obj){ - * logger.info(JSON.stringify(obj)); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); */ async function readYaml(options) { logger.debug('OPTIONS BEFORE ASSERTING IN readYaml::: ' + JSON.stringify(options)); - const assertedOptions = await Joi.validate(options, readerOptionsSchema); return new Promise((resolve, reject) => { - if (typeof assertedOptions.src === 'string') { + if (typeof options.src === 'string') { // load source from YAML file - fsPromisified.readFile(assertedOptions.src, UTF8) + fsPromisified.readFile(options.src, UTF8) .then((yaml) => { - logger.debug('YAML loaded from file ' + assertedOptions.src); + logger.debug('YAML loaded from file ' + options.src); try { resolve(jsYaml.safeLoad(yaml)); } catch (err) { // probably a YAMLException @@ -239,10 +154,8 @@ async function readYaml(options) { reject(err); } }); - } else if (isStream.readable(assertedOptions.src)) { - readFromStream(assertedOptions.src, resolve, reject, TYPE_YAML); - } else { - resolve(assertedOptions.src); // TODO does that make sense? + } else { // as validation has passed already, this can only be stream here + readFromStream(options.src, resolve, reject, TYPE_YAML); } }); } @@ -266,11 +179,35 @@ async function readYaml(options) { // self.logger.trace(doc); jsDocs.push(doc); }); }); }); }; } /** - * TODO: doc me. + * Reads a particular content type from a source provided in the passed `options`. * - * @param {Options} options - The read options. - * @returns {Promise.} Resolves with read success message. + * @param {module:type-definitions~ReaderOptions} options - The read options. + * @returns {Promise.} Resolves with JS object result. * @public + * @example + * import { read } from 'jy-transform'; + * + * // --- from file path + * + * options = { + * src: 'foo.yml' + * }; + * + * read(options) + * .then(obj => console.log(JSON.stringify(obj))) + * .catch(console.error); + * + * + * // --- from Readable + * + * options = { + * src: fs.createReadStream('foo.yml') + * }; + * + * read(options) + * .then(function (obj){ + * .then(obj => console.log(JSON.stringify(obj))) + * .catch(console.error); */ export async function read(options) { const assertedOptions = await Joi.validate(options, readerOptionsSchema); @@ -278,10 +215,8 @@ export async function read(options) { case TYPE_JS: case TYPE_JSON: return await readJs(assertedOptions); - case TYPE_YAML: + default: return await readYaml(assertedOptions); - default: // TODO better handling - throw new Error('should not happen!'); } } diff --git a/src/transformer.js b/src/transformer.js index 468c18c..d92aae1 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -1,5 +1,4 @@ import logger from 'cli'; -import { ensureMiddleware } from './middleware'; import { read } from './reader'; import { write } from './writer'; @@ -9,6 +8,16 @@ import { write } from './writer'; * @public */ +const callMiddlewareIfExists = async (object, middleware) => { + if (middleware !== undefined && typeof middleware !== 'function') { + throw new TypeError('The provided middleware is not a Function type'); + } + if (!middleware) { + return object; + } + return middleware(object); +}; + // //////////////////////////////////////////////////////////////////////////// // PUBLIC API // //////////////////////////////////////////////////////////////////////////// @@ -20,35 +29,31 @@ import { write } from './writer'; * 2. Transform [ + Middleware] * 3. Output (write). * - * @param {Options} options - The configuration for a transformation. - * @param {Function} [middleware] - This middleware Promise can be used to intercept + * @param {module:type-definitions~TransformerOptions} options - The configuration for a transformation. + * @param {Function} [middleware] - This middleware Promise can be used to intercept * the JSON object for altering the passed JSON, the function signature is: * ``` - * function(json) + * function(object) * ``` *

    * **NOTE:** the Promise has to return the processed JSON. * @returns {Promise.} Containing the transformation result as message (e.g. to be logged by caller). * @throws {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. - * @throws {Error} Will throw plain error when writing to file failed due to any reason. + * @throws {Error} Will throw plain error when writing to _destination object_ failed due to any reason. * @public * @example * import { transform } from 'jy-transform'; * const options = {...}; - * const middleware = async (json) { - * json.myproperty = 'new value'; - * return json; + * const middleware = async (object) { + * object.myproperty = 'new value'; + * return object; * }; * * // ---- Promise style: * * transform(options, middleware) - * .then(function (msg){ - * console.log(msg); - * }) - * .catch(function (err) { - * console.error(err.stack); - * }); + * .then(console.log) + * .catch(console.error); * * // ---- async/await style: * try { @@ -61,8 +66,7 @@ import { write } from './writer'; export async function transform(options, middleware) { logger.debug('transform'); let object = await read(options); - const ensuredMiddleware = ensureMiddleware(middleware); - object = await ensuredMiddleware(object); + object = await callMiddlewareIfExists(object, middleware); return await write(object, options); } diff --git a/src/type-definitions.js b/src/type-definitions.js index 4b79f9f..ab1b086 100644 --- a/src/type-definitions.js +++ b/src/type-definitions.js @@ -1,7 +1,7 @@ /** * @module jy-transform:type-definitions * @description The type definitions for this module. - * @private + * @public */ // ///////////////////////////////////////////////////////////////////////////// @@ -12,6 +12,7 @@ * Hapi.js Joi. * @external joi * @see {@link https://github.com/hapijs/joi Hapi Joi} + * @private */ /** @@ -19,6 +20,7 @@ * @typedef ValidationError * @memberof external:joi * @see {@link hhttps://github.com/hapijs/joi/blob/v10.2.0/API.md#errors Joi errors} + * @private */ /** @@ -27,6 +29,7 @@ * @typedef Schema * @memberof external:joi * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#joi Joi API} + * @private */ /** @@ -34,6 +37,7 @@ * @typedef Extension * @memberof external:joi * @see {@link https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension Hapi Joi Extension} + * @private */ /** @@ -41,6 +45,7 @@ * @callback validate * @memberof external:joi * @see {@link https://github.com/hapijs/joi/blob/master/API.md#validatevalue-schema-options-callback Joi.validate} + * @private */ // ///////////////////////////////////////////////////////////////////////////// @@ -48,16 +53,49 @@ // ///////////////////////////////////////////////////////////////////////////// /** - * The configuration properties provided to the framework functions. - * @typedef {object} Options - * @property {string} [origin=yaml] - The origin type. - * @property {string} [target=js] - The target type. - * @property {(string|Stream.Readable|object)} src - The source (if `string` type is treated as a file path). + * The configuration properties provided to the reader function. + * @typedef {object} ReaderOptions + * @property {(string|Stream.Readable|object)} src - The source (if `string` type is treated as a file path). + * @property {string} [origin=yaml] - The origin type. + * @property {string} [imports=undefined] - The exports name for reading from JS source files or objects only. + * @public + */ + +/** + * The writer configuration properties provided to the writer function. + * @typedef {object} WriterOptions * @property {(string|Stream.Writable|object)} [dest] - The destination (if `string` type is treated as a file path). + * @property {string} [target=js] - The target type. * @property {number} [indent=2] - The indention in files. + * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. + * @property {string} [force=false] - Force overwriting of existing output files on write phase. + * @public + */ + +/** + * The configuration properties provided to the transformer function. + * @typedef {object} TransformerOptions + * @property {(string|Stream.Readable|object)} src - The source (if `string` type is treated as a file path). + * @property {string} [origin=yaml] - The origin type. * @property {string} [imports=undefined] - The exports name for reading from JS source files * or objects only. + * @property {(string|Stream.Writable|object)} [dest] - The destination (if `string` type is treated as a file path). + * @property {string} [target=js] - The target type. * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. + * @property {number} [indent=2] - The indention in files. * @property {string} [force=false] - Force overwriting of existing output files on write phase. * @public */ + +/** + * The configuration properties provided to the transformer function. + * @typedef {object} TEESTTransformerOptions + * @type {module:type-definitions~WriterOptions} + * @type {module:type-definitions~ReaderOptions} + * @public + */ +/** + * The configuration properties provided to the transformer function. + * @typedef {(WriterOptions|ReaderOptions)} TEESTT2ransformerOptions + * @public + */ diff --git a/src/validation/joi-extensions-file-helper.js b/src/validation/joi-extensions-file-helper.js index 73d1101..e43b663 100644 --- a/src/validation/joi-extensions-file-helper.js +++ b/src/validation/joi-extensions-file-helper.js @@ -1,6 +1,5 @@ import fs from 'fs'; import path from 'path'; -import { debug } from '../debug-log'; /** * @module jy-transform:validation:joi-extensions-file-helper @@ -16,8 +15,6 @@ import { debug } from '../debug-log'; * @protected */ export function isExistingFile(pathStr) { - //debug('>>>>>>>>>>>>>DEBUG ===================================') - //error('>>>>>>>>>>>>>ERROR ===================================' + new Error('JYT Error')) const filePath = path.resolve(pathStr); try { const stats = fs.statSync(filePath); diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-helper.js index fb5a08a..cad783f 100644 --- a/src/validation/options-schema-helper.js +++ b/src/validation/options-schema-helper.js @@ -20,12 +20,11 @@ import { * Infer from path extension to a type using default value as fallback. * * @param {string} pathStr - The file path with or without extension. - * @param {boolean} origin - If the type is origin (true) or target (false). * @param {string} defaultValue - The default value to use if type cannot be inferred from path. * @returns {string} A type value. * @private */ -const getTypeFromFilePath = (pathStr, origin, defaultValue) => { +const getTypeFromFilePath = (pathStr, defaultValue) => { let type; try { let ext = path.extname(pathStr); @@ -47,30 +46,21 @@ const getTypeFromFilePath = (pathStr, origin, defaultValue) => { * TODO describe me. * * @param {Object} context - TODO describe me. - * @returns {string} The origin type. + * @returns {string} The target type. * @protected */ -export const inferOriginDefaultFromStreamReadableFilePath = (context) => { +export const inferOriginDefault = (context) => { let type; - if (isStream.readable(context.dest)) { - type = getTypeFromFilePath(context.src.path, true, DEFAULT_ORIGIN); + if (isStream.readable(context.src)) { + type = getTypeFromFilePath(context.src.path, DEFAULT_ORIGIN); + } else if (typeof context.src === 'string') { + type = getTypeFromFilePath(context.src, DEFAULT_ORIGIN); } else { type = DEFAULT_ORIGIN; } return type; }; -/** - * TODO describe me. - * - * @param {Object} context - TODO describe me. - * @returns {string} The origin type. - * @protected - */ -export const inferOriginDefaultFromFilePath = (context) => { - return getTypeFromFilePath(context.src, true, DEFAULT_ORIGIN); -}; - /** * TODO describe me. * @@ -78,36 +68,19 @@ export const inferOriginDefaultFromFilePath = (context) => { * @returns {string} The target type. * @protected */ -export const inferTargetDefaultFromStreamWritableFilePath = (context) => { +export const inferTargetDefault = (context) => { let type; if (isStream.writable(context.dest)) { - type = getTypeFromFilePath(context.dest.path, false, DEFAULT_TARGET); + type = getTypeFromFilePath(context.dest.path, DEFAULT_TARGET); + } else if (typeof context.dest === 'string') { + type = getTypeFromFilePath(context.dest, DEFAULT_TARGET); } else { type = DEFAULT_TARGET; } return type; }; -/** - * TODO describe me. - * - * @param {Object} context - TODO describe me. - * @returns {string} The target type. - * @protected - */ -export const inferTargetDefaultFromFilePath = (context) => { - let destType; - if (typeof context.dest === 'string') { - destType = getTypeFromFilePath(context.dest, false, DEFAULT_TARGET); - } else { - destType = DEFAULT_TARGET; - } - return destType; -}; - export default { - inferOriginDefaultFromStreamReadableFilePath, - inferTargetDefaultFromStreamWritableFilePath, - inferOriginDefaultFromFilePath, - inferTargetDefaultFromFilePath, + inferOriginDefault, + inferTargetDefault, }; diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js index 67cd262..ae6c6e3 100644 --- a/src/validation/options-schema.js +++ b/src/validation/options-schema.js @@ -1,10 +1,8 @@ import { Stream } from 'stream'; import Joi from './joi-extensions'; import { - inferOriginDefaultFromStreamReadableFilePath, - inferTargetDefaultFromStreamWritableFilePath, - inferOriginDefaultFromFilePath, - inferTargetDefaultFromFilePath, + inferOriginDefault, + inferTargetDefault, } from './options-schema-helper'; import { TYPE_YAML, @@ -52,7 +50,7 @@ export const readerOptionsSchema = Joi.object().keys({ then: Joi .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferOriginDefaultFromStreamReadableFilePath, + .default(inferOriginDefault, 'tried origin default inferred from src type if not set (Stream.Readable)'), otherwise: Joi .when('src', { @@ -60,10 +58,10 @@ export const readerOptionsSchema = Joi.object().keys({ then: Joi .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferOriginDefaultFromFilePath, 'origin resolving from src type if latter not set (String)'), + .default(inferOriginDefault, 'origin resolving from src type if latter not set (String)'), otherwise: Joi // else could only be JS Object .string() - .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .valid(TYPE_JS) .default(TYPE_JS) }), }) @@ -98,7 +96,7 @@ export const writerOptionsSchema = Joi.object().keys({ then: Joi .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferTargetDefaultFromStreamWritableFilePath, + .default(inferTargetDefault, 'tried target default inferred from dest type if not set (Stream.Writable)'), otherwise: Joi .when('dest', { @@ -106,7 +104,7 @@ export const writerOptionsSchema = Joi.object().keys({ then: Joi .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferTargetDefaultFromFilePath, 'try target resolving from dest type if latter not set (String)'), + .default(inferTargetDefault, 'try target resolving from dest type if latter not set (String)'), otherwise: Joi // check .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) @@ -132,16 +130,7 @@ export const writerOptionsSchema = Joi.object().keys({ }).unknown() .required(); -/** - * The prepared {@link external:joi.JoiSchema} for validating the {@link Transformer} options. - * @type {JoiSchema} - * @constant - * @private - */ -export const transformerOptionsSchema = readerOptionsSchema.concat(writerOptionsSchema).required(); - export default { readerOptionsSchema, writerOptionsSchema, - transformerOptionsSchema, }; diff --git a/src/writer.js b/src/writer.js index 55a11d0..8b6d8da 100644 --- a/src/writer.js +++ b/src/writer.js @@ -153,7 +153,7 @@ function writeToFile(object, dest, target, resolve, reject, forceOverwrite) { mkdirAndWrite(); } }) - .catch((err) => { + .catch(() => { // ignore error (because file could possibly not exist at this point of time) mkdirAndWrite(); }); @@ -248,11 +248,8 @@ async function writeYaml(object, options) { } else if (isStream.writable(options.dest)) { // stream writeToStream(yaml, options.dest, TYPE_YAML, resolve, reject); } else { // object - console.log('YAAAMLLLLLL: ' + yaml) - console.log('options.dest1: ' + JSON.stringify(options.dest)) options.dest = yaml; - //options.dest.text = yaml; - resolve('Writing YAML to options.dest successful.'); + resolve('Writing serialized YAML to options.dest successful.'); } }); } @@ -344,61 +341,8 @@ async function writeJson(object, options) { * @see {@link MAX_INDENT} * @returns {Promise.} - Containing the write success message to handle by caller (e.g. for logging). * @private - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * var writer = new Writer(logger); - * - * // ---- write obj to file - * - * var obj = {...}; - * var options = { - * dest: 'result.js', - * indent: 2 - * } - * - * writer.writeJs(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to Writable - * - * options = { - * dest: fs.createWriteStream('result.json'), - * indent: 4 - * } - * - * writer.writeJs(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to object - * - * options = { - * dest: {}, - * indent: 2 - * } - * - * writer.writeJs(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); */ async function writeJs(object, options) { - //logger.debug('OPTIONS BEFORE ASSERTING IN writeJs:::' + JSON.stringify(options)); return new Promise((resolve, reject) => { if (typeof options.dest === 'string') { // file serializeJsToString(object, options.indent, options.exports) @@ -429,29 +373,65 @@ async function writeJs(object, options) { /** * TODO: doc me. * - * @param {Object} options - The source object to write. - * @param {Options} options - The write options. + * @param {Object} object - The JS source object to write. + * @param {module:type-definitions~WriterOptions} options - The write options. * @returns {Promise.} Resolves with write success message. * @public + * @example + * import { write } from 'jy-transform'; + * + * + * // ---- write obj to file --- + * + * var obj = {...}; + * var options = { + * dest: 'result.js', + * indent: 4 + * } + * + * write(obj, options) + * .then(console.log) + * .catch(console.error); + * + * + * // ---- write obj to Writable --- + * + * options = { + * dest: fs.createWriteStream('result.json'), + * indent: 4 + * } + * + * write(obj, options) + * .then(console.log) + * .catch(console.error); + * + * + * // ---- write obj to object --- + * + * options = { + * dest: {}, + * indent: 4 + * } + * + * write(obj, options) + * .then(console.log) + * .catch(console.error); */ export async function write(object, options) { - const assertedOptions = await Joi.validate(options, writerOptionsSchema); - console.log('options after validation 1: ' + JSON.stringify(options)) + const validatedOptions = await Joi.validate(options, writerOptionsSchema); + // HINT: we have to use the original options object because the caller must not loose the reference to options.dest, // so we copy everything here except the assertedOptions.dest (joi does not return the original reference)! - Object.assign(options, { target: assertedOptions.target }, { exports: assertedOptions.exports }, - { indent: assertedOptions.indent }, { force: assertedOptions.force }); - console.log('options after validation 2: ' + JSON.stringify(options)) - assertedOptions.dest = options.dest; - switch (assertedOptions.target) { - case TYPE_JS: - return await writeJs(object, options); + Object.assign(options, { target: validatedOptions.target }, { exports: validatedOptions.exports }, + { indent: validatedOptions.indent }, { force: validatedOptions.force }); + validatedOptions.dest = options.dest; + switch (validatedOptions.target) { case TYPE_JSON: return await writeJson(object, options); case TYPE_YAML: return await writeYaml(object, options); - default: // TODO better handling - throw new Error('should not happen!'); + default: + return await writeJs(object, options); } } diff --git a/test/data/writable-test-dummy.txt b/test/data/writable-test-dummy.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/data/writable-test-dummy.yaml b/test/data/writable-test-dummy.yaml new file mode 100644 index 0000000..e69de29 diff --git a/test/logger.js b/test/logger.js index d0446fe..b15c174 100644 --- a/test/logger.js +++ b/test/logger.js @@ -1,5 +1,5 @@ import winston from 'winston'; -import fsExtra from 'fs-extra'; +// import fsExtra from 'fs-extra'; // TODO clean up this file! // import promisify from 'promisify-es6'; // // import toYAML from 'combarnea-winston-console-formatter'; // diff --git a/test/unit/test-index.js b/test/unit/test-index.js index 967c887..32e132c 100644 --- a/test/unit/test-index.js +++ b/test/unit/test-index.js @@ -1,4 +1,4 @@ -import index from '../../index'; +import index, { transform, read, write, TYPE_YAML, TYPE_JS, TYPE_JSON } from '../../index'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; /** @@ -14,68 +14,43 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - index - ', () => { expect.assertions(3); expect(index).toBeDefined(); expect(index).toBeInstanceOf(Object); - expect(Object.keys(index)).toHaveLength(4); + expect(Object.keys(index)).toHaveLength(6); }) ); - describe('Exported Transformer', () => - it('should be an existing Transformer object', () => { - expect.assertions(5); - expect(index.Transformer).toBeDefined(); - expect(index.Transformer).toBeInstanceOf(Object); - expect(Object.keys(index.Transformer)).toHaveLength(1); - const { transform } = index.Transformer; + describe('Exported transform', () => + it('should be an existing function', () => { + expect.assertions(2); expect(transform).toBeDefined(); expect(transform).toBeInstanceOf(Function); }) ); - describe('Exported Reader', () => - it('should be an existing Reader object with read function', () => { - expect.assertions(5); - expect(index.Reader).toBeDefined(); - expect(index.Reader).toBeInstanceOf(Object); - expect(Object.keys(index.Reader)).toHaveLength(1); - const { read } = index.Reader; + describe('Exported read', () => + it('should be an existing function', () => { + expect.assertions(2); expect(read).toBeDefined(); expect(read).toBeInstanceOf(Function); }) ); - describe('Exported Writer', () => - it('should be an existing Writer object with write function', () => { - expect.assertions(5); - expect(index.Writer).toBeDefined(); - expect(index.Writer).toBeInstanceOf(Object); - expect(Object.keys(index.Writer)).toHaveLength(1); - const { write } = index.Writer; + describe('Exported write', () => + it('should be an existing function', () => { + expect.assertions(2); expect(write).toBeDefined(); expect(write).toBeInstanceOf(Function); }) ); - describe('Exported Constants', () => - it('should be an existing Constants instance', () => { - expect.assertions(4); - expect(index.Constants).toBeDefined(); - expect(typeof index.Constants === 'object').toBeTruthy(); - const { DEFAULT_FORCE_FILE_OVERWRITE } = index.Constants; - expect(DEFAULT_FORCE_FILE_OVERWRITE).toBeDefined(); - expect(DEFAULT_FORCE_FILE_OVERWRITE).toBeFalsy(); - // TODO - // {DEFAULT_FORCE_FILE_OVERWRITE: false, DEFAULT_INDENT: 2, DEFAULT_JS_EXPORTS_IDENTIFIER: undefined, - // DEFAULT_JS_IMPORTS_IDENTIFIER: undefined, DEFAULT_OPTIONS: {dest: storing relative to input file, exports: - // undefined, force: false, imports: undefined, indent: 2, origin: if not given, the type is tried to be - // inferred from the extension of source path, else it is 'yaml', target: if not given, the type is tried to be - // inferred from the extension of destination path, else it is 'js'}, DEFAULT_ORIGIN: yaml, DEFAULT_TARGET: js, - // DEST_DESCRIPTION: storing relative to input file, JS: js, JSON: json, JSON_TO_JS: json2js, JSON_TO_JSON: - // json2json, JSON_TO_YAML: json2yaml, JS_TO_JS: js2js, JS_TO_JSON: js2json, JS_TO_YAML: js2yaml, MAX_INDENT: - // 8, MIN_INDENT: 0, ORIGIN_DESCRIPTION: if not given, the type is tried to be inferred from the extension of - // source path, else it is 'yaml', TARGET_DESCRIPTION: if not given, the type is tried to be inferred from the - // extension of destination path, else it is 'js', TRANSFORMATIONS: [yaml2js, yaml2json, js2yaml, json2yaml, - // json2js, js2json, yaml2yaml, json2json, js2js], TYPES: [yaml, json, js], UTF8: utf8, YAML: yaml, YAML_TO_JS: - // yaml2js, YAML_TO_JSON: yaml2json, YAML_TO_YAML: yaml2yaml} - + describe('Exported constants', () => + it('should be existing string values', () => { + expect.assertions(6); + expect(TYPE_YAML).toBeDefined(); + expect(TYPE_YAML).toBe('yaml'); + expect(TYPE_JS).toBeDefined(); + expect(TYPE_JS).toBe('js'); + expect(TYPE_JSON).toBeDefined(); + expect(TYPE_JSON).toBe('json'); }) ); }); diff --git a/test/unit/test-middleware.js b/test/unit/test-middleware.js deleted file mode 100644 index 28a7084..0000000 --- a/test/unit/test-middleware.js +++ /dev/null @@ -1,117 +0,0 @@ -import objectPath from 'object-path'; -import { ensureMiddleware, identityMiddleware } from '../../src/middleware'; -import { transform } from '../../src/transformer'; -import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; -import { logger } from '../logger'; - -/** - * @module jy-transform:unit-test:test-middleware - * @description This unit test suite checks the validity and correctness of {@link middleware} module. - * @private - */ - -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - middleware - ', () => { - /** - * Middleware function for altering JSON. - * - * @param {Object} json - The JSON object to alter. - * @private - */ - const middleware = (json) => { - const key1 = async (jsonToAlter) => { - objectPath.set(jsonToAlter, 'key1', 'value1'); - logger.info('key1 json: ' + JSON.stringify(jsonToAlter)); - return jsonToAlter; - }; - - const key2 = async (jsonToAlter) => { - objectPath.set(jsonToAlter, 'key2', 'value2'); - logger.info('key2 json: ' + JSON.stringify(jsonToAlter)); - return jsonToAlter; - }; - - const key3 = async (jsonToAlter) => { - objectPath.set(jsonToAlter, 'key3', 'value3'); - logger.info('key3 json: ' + JSON.stringify(jsonToAlter)); - return jsonToAlter; - }; - - return Promise.all([key1(json), key2(json), key3(json)]) - .then((result) => { - expect(result).toHaveLength(3); - logger.info('all the elements were created'); - logger.info('result: ' + JSON.stringify(result[result.length - 1])); - return result[result.length - 1]; - }); - }; - - /** - * Helper function to assert the identity Promise. - * - * @param {Function} func - The identity Promise function. - * @returns {Promise} A promise. - * @private - */ - const assertIdentityPromise = (func) => { - const json = { test: 'identity' }; - return func(json).then(jsonResult => expect(jsonResult).toBe(json)); - }; - - describe('Testing Transformer middleware', () => { - it('should alter json', () => { - expect.assertions(4); - const options = { - src: {}, - dest: {} - }; - return transform(options, middleware) - .then((msg) => { - logger.info(msg); - logger.info('options.dest: ' + JSON.stringify(options.dest, null, 4)); - expect(options.dest.key1).toBe('value1'); - expect(options.dest.key2).toBe('value2'); - expect(options.dest.key3).toBe('value3'); - }); - }); - }); - - describe('Testing middleware.identityMiddleware()', () => { - it('should provide passed function', async () => { - const func = identityMiddleware; - expect(func).toBeInstanceOf(Function); - expect(func).toBe(identityMiddleware); - - const json = {}; - const jsonIdentity = await identityMiddleware(json); - expect(jsonIdentity).toBe(json); - }); - }); - - describe('Testing middleware.ensureMiddleware()', () => { - it('should provide passed function', () => { - expect.assertions(2); - const func = ensureMiddleware(identityMiddleware); - expect(func).toBeInstanceOf(Function); - expect(func).toBe(identityMiddleware); - }); - - it('should throw TypeError if middleware passed is not a function type', () => { - expect.assertions(1); - expect(() => ensureMiddleware('not a function')).toThrow(TypeError); - }); - - it('should provide identity Promise if middleware passed is null', async () => { - expect.assertions(2); - const func = ensureMiddleware(); - expect(func).toBeInstanceOf(Function); - await assertIdentityPromise(func); - }); - - it('should provide identity Promise if middleware passed is undefined', async () => { - expect.assertions(2); - const func = ensureMiddleware(undefined); - expect(func).toBeInstanceOf(Function); - await assertIdentityPromise(func); - }); - }); -}); diff --git a/test/unit/test-reader.js b/test/unit/test-reader.js index 7423b1f..b8ff935 100644 --- a/test/unit/test-reader.js +++ b/test/unit/test-reader.js @@ -1,7 +1,6 @@ import YAMLException from 'js-yaml/lib/js-yaml/exception'; import fs from 'fs'; import { read } from '../../src/reader'; -import { logger } from '../logger'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; import { TYPE_JS, @@ -55,12 +54,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { expect(json[key]).toBe(expectedValue); }; - describe('Testing Reader.readJs(...)', () => { + describe('The reading from JS', () => { const exports = 'fooBar'; const exportsNotExists = 'notFooBar'; const invalidIdentifier = '#3/-'; - it('should reject on reading JS with options.imports == \'\'', () => { + it('should reject with options.imports == \'\'', () => { const options = { src: './test/data/test-data.js', imports: '', @@ -68,11 +67,26 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { return expectReaderError(options, { name: 'ValidationError', isJoi: true }); }); - it('should read JS from file', async () => + it('should read JS successfully', async () => + await expectReaderSuccess({ src: { myproperty: 'value' } }, 'myproperty', 'value') + ); + + it('should read JS from file successfully', async () => await expectReaderSuccess({ src: './test/data/test-data.js' }, 'myproperty', 'old value') ); - it('should read JS from file with options.imports == \'' + exports + '\'', async () => { + it('should read JS from JS object successfully and both object references are different', async () => { + expect.assertions(3); + const options = { + src: {}, + }; + const object = await read(options); + expect(object).toBeDefined(); + expect(object).toMatchObject({}); + expect(object === options.src).toBe(false); + }); + + it('should read JS from file with options.imports == \'' + exports + '\' successfully', async () => { expect.assertions(5); const options = { src: './test/data/test-imports.js', @@ -87,7 +101,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { }); it('should read JS from file with options.imports == \'' + exports - + '\' and given origin for unsupported file extension', async () => { + + '\' and given origin for unsupported file extension successfully', async () => { expect.assertions(5); const options = { src: './test/data/test-imports.txt', @@ -102,7 +116,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { expect(json.foo).toBe('bar'); }); - it('should reject read JS from file with Error on invalid identifier for options.imports: ' + it('should reject reading JS from file with Error on invalid identifier for options.imports: ' + invalidIdentifier, () => { const options = { src: './test/data/test-imports.js', @@ -111,7 +125,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { return expectReaderError(options, { name: 'ValidationError', isJoi: true }); }); - it('should reject read JS from file with Error on non-existent identifier for options.imports: ' + it('should reject reading JS from file with Error on non-existent identifier for options.imports: ' + exportsNotExists, () => { const options = { src: './test/data/test-imports.js', @@ -120,11 +134,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { return expectReaderErrorByType(options, Error); }); - it('should read JSON from file', async () => + it('should read JSON from file successfully', async () => await expectReaderSuccess({ src: './test/data/test-data.json' }, 'myproperty', 'old value') ); - it('should read JS from object', async () => { + it('should read JS from object successfully', async () => { const options = { src: { foo: 'bar', @@ -133,7 +147,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { await expectReaderSuccess(options, 'foo', 'bar'); }); - it('should read JS from Object with options.imports == \'' + exports + '\'', async () => { + it('should read JS from Object with options.imports == \'' + exports + '\' successfully', async () => { expect.assertions(6); const options = { src: { @@ -153,7 +167,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { expect(json.foo).toBe('bar'); }); - it('should reject read JS from Object with Error on invalid identifier for options.imports: ' + it('should reject reading JS from Object with Error on invalid identifier for options.imports: ' + invalidIdentifier, () => { const options = { src: { @@ -167,7 +181,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { return expectReaderError(options, { name: 'ValidationError', isJoi: true }); }); - it('should reject read (deep) JS from file with Error on non-existent identifier for options.imports: ' + it('should reject reading JS (deeply) from file with Error on non-existent identifier for options.imports: ' + exportsNotExists, () => { const options = { src: { @@ -181,7 +195,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { return expectReaderErrorByType(options, Error); }); - it('should read JSON from stream', async () => + it('should read JSON from stream successfully', async () => await expectReaderSuccess({ origin: TYPE_JSON, src: fs.createReadStream('./test/data/test-data.json') @@ -226,16 +240,16 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { }); }); - describe('Testing Reader.readYaml(...)', () => { - it('should read YAML from file', async () => + describe('Testing reading from YAML', () => { + it('should read YAML from file successfully', async () => await expectReaderSuccess({ src: './test/data/test-data.yaml' }, 'myproperty', 'old value') ); - it('should read JS from object', async () => + it('should read JS from object successfully', async () => // TODO await expectReaderSuccess({ src: { test: 'value' } }, 'test', 'value') ); - it('should read YAML from stream', async () => + it('should read YAML from stream successfully', async () => await expectReaderSuccess({ origin: TYPE_YAML, src: fs.createReadStream('./test/data/test-data.yaml'), @@ -253,19 +267,23 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { }, YAMLException); }); - it('should fail YAML read by missing input options', () => { + it('should fail reading YAML by providing empty JS object as options.src', () => { + return expectReaderError({ src: {}, origin: TYPE_YAML }, { name: 'ValidationError', isJoi: true }); + }); + + it('should fail YAML reading by missing input options', () => { return expectReaderError(null, { name: 'ValidationError', isJoi: true }); }); - it('should fail YAML read by missing options.src', () => { + it('should fail reading YAML by missing options.src', () => { return expectReaderError({}, { name: 'ValidationError', isJoi: true }); }); - it('should fail read YAML from configured directory source', async () => + it('should fail reading YAML from configured directory source', async () => await expectReaderError({ src: './test/data' }, { name: 'ValidationError', isJoi: true }) ); - it('should fail read YAML from file', async () => + it('should fail reading YAML from non-existing file', async () => await expectReaderError({ src: './test/data/non-existing.yml' }, { name: 'ValidationError', isJoi: true diff --git a/test/unit/test-transformer.js b/test/unit/test-transformer.js index b82f0bd..034184f 100644 --- a/test/unit/test-transformer.js +++ b/test/unit/test-transformer.js @@ -5,12 +5,7 @@ import fs from 'fs'; import path from 'path'; import { logger } from '../logger'; import { transform } from '../../src/transformer'; -import { - UTF8, - TYPE_YAML, - TYPE_JS, - TYPE_JSON, -} from '../../src/constants'; +import { UTF8 } from '../../src/constants'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; const fsPromised = promisify(fs); @@ -111,6 +106,38 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { }); } + describe('Testing transform with middleware', () => { + it('should throw TypeError if middleware passed is not a function type', async () => { + expect.assertions(1); + await expect(transform({ src: {}, dest: {} }, 'not a function')).rejects.toBeInstanceOf(TypeError); + }); + + it('should not fail if middleware passed is returning a Promise', () => { + expect.assertions(1); + const returningPromise = async (object) => { + return object; + }; + return expect(transform({ src: {}, dest: {} }, returningPromise)) + .resolves.toBe('Writing JS to options.dest successful.'); + }); + + it('should not fail if middleware passed is not returning a Promise', () => { + expect.assertions(1); + const notReturningPromise = (object) => { + return object; + }; + return expect(transform({ src: {}, dest: {} }, notReturningPromise)) + .resolves.toBe('Writing JS to options.dest successful.'); + }); + }); + + describe('Testing transform without middleware', () => { + it('should not fail', () => { + expect.assertions(1); + return expect(transform({ src: {}, dest: {} })).resolves.toBe('Writing JS to options.dest successful.'); + }); + }); + describe('Testing Transformer transforming from YAML to JS to relative path', () => { const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data.js'; diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js index 317a776..ab43113 100644 --- a/test/unit/test-writer.js +++ b/test/unit/test-writer.js @@ -1,6 +1,5 @@ import promisify from 'promisify-es6'; import fsExtra from 'fs-extra'; -import YAMLException from 'js-yaml/lib/js-yaml/exception'; import fs from 'fs'; import os from 'os'; import stream from 'stream'; @@ -23,13 +22,13 @@ const fsPromised = promisify(fs); describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { /** - * Sample JSON content used in tests. + * Sample JS content used in tests. * * @type {{test: string}} * @constant * @private */ - const JSON_CONTENT = { test: 'value' }; + const JS_CONTENT = { test: 'value' }; /** * Temporary base dir for writer test output. @@ -92,8 +91,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { /** * Assert an `Error` for a given writer function. * + * @param {Object} object - The JS object to write. * @param {Object} options - The options which potentially produce the error. - * @param {Object} [match={name:'Error'}] - The propertie(s) error should contain. + * @param {Object} [match={name:'Error'}] - The propertie(s) an error should contain. * @private */ const expectWriteError = (object, options, match = { name: 'Error' }) => { @@ -104,6 +104,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { /** * Assert an `Error` for a given writer function. * + * @param {Object} object - The JS object to write. * @param {Object} options - The options which potentially produce the error. * @param {Error} [match=Error'] - The error type to match. * @private @@ -113,11 +114,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { return expect(write(object, options)).rejects.toBeInstanceOf(match); }; - describe('Testing writeJs(...)', () => { + describe('The write function', () => { it('should write JS to file', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.js'; - const msg = await write(JSON_CONTENT, { dest: file }); + const msg = await write(JS_CONTENT, { dest: file }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -125,7 +126,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JS to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.js'; - const msg = await write(JSON_CONTENT, { dest: fs.createWriteStream(file) }); + const msg = await write(JS_CONTENT, { dest: fs.createWriteStream(file) }); expect(msg).toBeDefined(); await expectDestFileExists(file); }); @@ -138,7 +139,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: 'test', }; - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(file); // eslint-disable-next-line import/no-unresolved, global-require @@ -152,7 +153,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js', exports: '#3/-', }; - return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'#3/-\')', () => { @@ -161,7 +162,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: '#3/-', }; - return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by invalid exports identifier (\'if\')', () => { @@ -170,7 +171,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: 'if', }; - return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); it('should write JS to stream and fail by provoked error', () => { @@ -181,7 +182,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { this.emit('error', new Error('Dummy Error')); done(); }; - return expectWriteErrorByType(JSON_CONTENT, { + return expectWriteErrorByType(JS_CONTENT, { target: TYPE_JS, dest: errorThrowingStream, }, Error); @@ -190,7 +191,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JS to JS object', async () => { expect.assertions(4); const options = { dest: {} }; - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(options.dest, 'test')).toBeTruthy(); @@ -202,7 +203,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: {}, exports: '', }; - return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); const exports = 'foo'; @@ -212,7 +213,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: {}, exports, }; - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); expect(Object.prototype.hasOwnProperty.call(options.dest, exports)).toBeTruthy(); @@ -226,11 +227,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest: {}, exports: invalidIdentifier, }; - return expectWriteError(JSON_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS with Error on missing destination', () => { - return expectWriteError(JSON_CONTENT, {}, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, {}, { name: 'ValidationError', isJoi: true }); }); it('should reject write JS to file by invalid file path', (done) => { @@ -241,7 +242,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_' + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/test-data-by-js-to-file.js' }; - write(JSON_CONTENT, options) + write(JS_CONTENT, options) .then((msg) => { done(new Error('Error expected, but got success message: ' + msg)); }) @@ -269,7 +270,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { const options = { dest: WRITER_TEST_BASE_DIR + '/test-data-by-json-to-file.json' }; - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(options.dest); }); @@ -277,7 +278,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write JSON to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-json-stream.json'; - const msg = await write(JSON_CONTENT, { + const msg = await write(JS_CONTENT, { target: TYPE_JSON, dest: fs.createWriteStream(file), }); @@ -285,15 +286,28 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { await expectDestFileExists(file); }); + it('should write (stringified) JSON to JS object', async () => { + expect.assertions(4); + const options = { + dest: {}, + target: TYPE_JSON + }; + const msg = await write(JS_CONTENT, options); + expect(msg).toBeDefined(); + expect(options.dest).toBeDefined(); + const result = JSON.parse(options.dest); + expect(Object.prototype.hasOwnProperty.call(result, 'test')).toBeDefined(); + expect(result.test).toBe('value'); + }); + it('should write JS to JS object', async () => { expect.assertions(4); const options = { dest: {}, }; - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); - //const result = JSON.parse(options.dest); // TODO is this correct? expect(Object.prototype.hasOwnProperty.call(options.dest, 'test')).toBeDefined(); expect(options.dest.test).toBe('value'); }); @@ -303,7 +317,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write YAML to file', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file.yaml'; - const msg = await write(JSON_CONTENT, { + const msg = await write(JS_CONTENT, { dest: file }); expect(msg).toBeDefined(); @@ -313,7 +327,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { it('should write YAML to stream', async () => { expect.assertions(2); const file = WRITER_TEST_BASE_DIR + '/test-data-by-js-stream.yaml'; - const msg = await write(JSON_CONTENT, { + const msg = await write(JS_CONTENT, { target: TYPE_YAML, dest: fs.createWriteStream(file), }); @@ -327,13 +341,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { target: TYPE_YAML, dest: { XXXX: 'YYXXXX' }, }; - const msg = await write(JSON_CONTENT, options); - - console.log('options.dest 2: ' + JSON.stringify(options.dest)) + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); expect(options.dest).toBeDefined(); - const key = Object.keys(JSON_CONTENT)[0]; - expect(options.dest).toBe(key + ': ' + JSON_CONTENT[key] + os.EOL); + const key = Object.keys(JS_CONTENT)[0]; + expect(options.dest).toBe(key + ': ' + JS_CONTENT[key] + os.EOL); }); it('should reject with Error by invalid src object', () => { @@ -345,13 +357,13 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { }); it('should reject with Error on missing destination', () => { - return expectWriteError(JSON_CONTENT, {}, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, {}, { name: 'ValidationError', isJoi: true }); }); }); describe('Testing force overwrite file', () => { it('should reject when options.dest is a directory', () => { - return expectWriteErrorByType(JSON_CONTENT, { + return expectWriteErrorByType(JS_CONTENT, { dest: './test/data', target: TYPE_YAML }, Error); @@ -368,7 +380,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { const asyncFunctions = [ async () => { - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(dest); return 'overwrite test #1 should initially write YAML to file \'' + dest + '\''; @@ -379,7 +391,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest, force: true }; - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(dest); await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); @@ -391,7 +403,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest, force: false, }; - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml'); return 'overwrite test #3 shouldn\'t overwrite existing YAML file \'' + dest + @@ -403,10 +415,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { dest, force: true }; - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileDoesNotExist(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); - //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); return 'overwrite test #4 should overwrite existing YAML file \'' + dest + '\''; }, async () => { @@ -414,10 +425,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { indent: 4, dest, }; - const msg = await write(JSON_CONTENT, options); + const msg = await write(JS_CONTENT, options); expect(msg).toBeDefined(); await expectDestFileExists(WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml'); - //logger.info('testing overwrite #' + (index++) + '/' + asyncFunctions.length + ': ' + msg); return 'overwrite test #5 shouldn\'t overwrite existing YAML file \'' + dest + '\' and \'' + WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(1).yaml\', but write ' + 'new file \'' + WRITER_TEST_BASE_DIR + '/test-data-file-overwriting(2).yaml\''; @@ -434,19 +444,6 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { return fn().then(result => result).catch(err => err.message); }); }, Promise.resolve()); - - // .reduce((p, fn) => { - // return p.then(async (msg) => { - // if (msg) { - // logger.info('testing overwrite #' + (index + 1) + '/' + asyncFunctions.length + ': ' + msg); - // } - // return await fn(); - // }); - // }, Promise.resolve()); - - // await Promise.all(, (value, index, length) => { - // return value().then(msg => logger.info('testing overwrite #' + (index + 1) + '/' + asyncFunctions.length + - // ': ' + msg)); }).then(); }, 5000); // we have to set higher timeout here because some travis jobs failed due to 2 sec timeout! }); }); diff --git a/test/unit/validation/test-options-schema-helper.js b/test/unit/validation/test-options-schema-helper.js index 415c132..6c699ad 100644 --- a/test/unit/validation/test-options-schema-helper.js +++ b/test/unit/validation/test-options-schema-helper.js @@ -1,25 +1,16 @@ -import stringify from 'json-stringify-safe'; import stream from 'stream'; +import fs from 'fs'; import { - inferOriginDefaultFromFilePath, - inferTargetDefaultFromFilePath, + inferOriginDefault, + inferTargetDefault, } from '../../../src/validation/options-schema-helper'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; import { TYPE_YAML, TYPE_JS, - TYPE_JSON, - DEFAULT_FORCE_FILE_OVERWRITE, - DEFAULT_JS_IMPORTS_IDENTIFIER, - DEFAULT_JS_EXPORTS_IDENTIFIER, DEFAULT_ORIGIN, DEFAULT_TARGET, - DEFAULT_INDENT, - MIN_INDENT, - MAX_INDENT, } from '../../../src/constants'; -import { transformerOptionsSchema } from '../../../src/validation/options-schema'; -import Joi from '../../../src/validation/joi-extensions'; /** * @module jy-transform:unit-test:test-options-schema-helper @@ -27,10 +18,81 @@ import Joi from '../../../src/validation/joi-extensions'; * @private */ +// TODO write all tests describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { - describe('Method isExistingFile(pathStr) ', () => { - it('should return true on relative path string with existing file', () => - expect(inferOriginDefaultFromFilePath('test/unit/validation/test-joi-extensions-file-helper.js')).toBeTruthy() + describe('Function inferOriginDefault', () => { + it('should infer the correct origin from relative path string with existing file having a known file type', () => + expect(inferOriginDefault({ + src: 'test/unit/validation/test-joi-extensions-file-helper.js' + })).toBe(TYPE_JS) + ); + + it('should infer the default origin from relative path string with existing file having an unknown file type', () => + expect(inferOriginDefault({ + src: 'test/data/readable-test-dummy.txt' + })).toBe(DEFAULT_ORIGIN) + ); + + it('should infer the correct origin from read stream of existing file having a known file ending', () => + expect(inferOriginDefault({ + src: fs.createReadStream('test/unit/validation/test-joi-extensions-file-helper.js'), + })).toBe(TYPE_JS) + ); + + it('should infer the correct origin from read stream of existing file having an unknown file ending', () => + expect(inferOriginDefault({ + src: fs.createReadStream('test/data/readable-test-dummy.txt'), + })).toBe(DEFAULT_ORIGIN) + ); + + it('should infer the correct origin from plain read stream', () => + expect(inferOriginDefault({ + src: new stream.Readable(), + })).toBe(DEFAULT_ORIGIN) + ); + + it('should infer the default origin from unsupported origin.src', () => + expect(inferOriginDefault({ + src: {}, + })).toBe(DEFAULT_ORIGIN) + ); + }); + + describe('Function inferTargetDefault', () => { + it('should infer the correct target from relative path string with existing file having a known file ending', () => + expect(inferTargetDefault({ + dest: 'test/unit/validation/test-joi-extensions-file-helper.yaml' + })).toBe(TYPE_YAML) + ); + + it('should infer the default target from relative path string with existing file having an unknown file type', () => + expect(inferTargetDefault({ + dest: 'test/data/readable-test-dummy.txt' + })).toBe(DEFAULT_TARGET) + ); + + it('should infer the correct target from relative path string with existing file having a known file type', () => + expect(inferTargetDefault({ + dest: fs.createWriteStream('test/data/writable-test-dummy.yaml'), + })).toBe(TYPE_YAML) + ); + + it('should infer the default target from relative path string with existing file having an unknown file type', () => + expect(inferTargetDefault({ + dest: fs.createWriteStream('test/data/writable-test-dummy.txt'), + })).toBe(DEFAULT_TARGET) + ); + + it('should infer the correct origin from plain write stream', () => + expect(inferTargetDefault({ + dest: new stream.Writable(), + })).toBe(DEFAULT_TARGET) + ); + + it('should infer the default target from unsupported origin.dest', () => + expect(inferTargetDefault({ + dest: {}, + })).toBe(DEFAULT_TARGET) ); }); }); diff --git a/test/unit/validation/test-options-schema.js b/test/unit/validation/test-options-schema.js index 5e0a258..8782cb0 100644 --- a/test/unit/validation/test-options-schema.js +++ b/test/unit/validation/test-options-schema.js @@ -15,7 +15,10 @@ import { MIN_INDENT, MAX_INDENT, } from '../../../src/constants'; -import { transformerOptionsSchema } from '../../../src/validation/options-schema'; +import { + readerOptionsSchema, + writerOptionsSchema, +} from '../../../src/validation/options-schema'; import Joi from '../../../src/validation/joi-extensions'; /** @@ -24,78 +27,71 @@ import Joi from '../../../src/validation/joi-extensions'; * @private */ +/** + * Expect a `ValidationError` for a given options function. + * + * @param {Options} invalidOptions - The options which potentially produce the error. + * @param {Schema} schema - The validation schema. + * @private + */ +function expectOptionsValidationError(invalidOptions, schema) { + expect.assertions(1); + return expect(Joi.validate(invalidOptions, schema)).rejects.toMatchObject({ + name: 'ValidationError', + isJoi: true, + }); +} + +/** + * Expect a validation success for a given options. + * + * @param {Options} validOptions - The options which should be correct. + * @param {Schema} schema - The validation schema. + * @private + */ +function expectOptionsValidationSuccess(validOptions, schema) { + expect.assertions(1); + return expect(Joi.validate(validOptions, schema)).resolves.toMatchObject(validOptions); +} + describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { - /** - * Expect a `ValidationError` for a given options function. - * - * @param {Options} options - The options which potentially produce the error. - * @private - */ - function expectOptionsValidationError(options) { - expect.assertions(1); - return expect(Joi.validate(options, transformerOptionsSchema)).rejects.toMatchObject({ - name: 'ValidationError', - isJoi: true, - }); - } - - /** - * Expect a validation success for a given options. - * - * @param {Options} options - The options which should be correct. - * @private - */ - function expectOptionsValidationSuccess(options) { - expect.assertions(1); - return expect(Joi.validate(options, transformerOptionsSchema)).resolves.toMatchObject(options); - } - - describe('Testing options schema validation', () => { + describe('Testing readerOptionsSchema validation', () => { it('should reject when options is missing (null)', async () => - await expectOptionsValidationError(null) + await expectOptionsValidationError(null, readerOptionsSchema) ); it('should reject when options is missing (undefined)', async () => - await expectOptionsValidationError(undefined) + await expectOptionsValidationError(undefined, readerOptionsSchema) ); it('should set all defaults', async () => { - expect.assertions(6); + expect.assertions(2); const options = { src: './test/data/test-data.yaml', dest: './test/tmp/test-data.js', }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + const validatedOptions = await Joi.validate(options, readerOptionsSchema); expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); - expect(validatedOptions.target).toBe(DEFAULT_TARGET); - expect(validatedOptions.indent).toBe(DEFAULT_INDENT); expect(validatedOptions.imports).toBe(DEFAULT_JS_IMPORTS_IDENTIFIER); - expect(validatedOptions.exports).toBe(DEFAULT_JS_EXPORTS_IDENTIFIER); - expect(validatedOptions.force).toBe(DEFAULT_FORCE_FILE_OVERWRITE); }); - it('should infer options.origin and options.target from file type', async () => { - expect.assertions(2); + it('should infer options.origin from file type', async () => { + expect.assertions(1); const options = { src: './test/data/test-data.js', // non default type - dest: 'some-file.yml', // non default type }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + const validatedOptions = await Joi.validate(options, readerOptionsSchema); expect(validatedOptions.origin).toBe(TYPE_JS); - expect(validatedOptions.target).toBe(TYPE_YAML); }); - it('should infer options.origin and options.target from set origin and target', async () => { - expect.assertions(2); + it('should infer options.origin from set origin', async () => { + expect.assertions(1); const options = { origin: TYPE_JS, - target: TYPE_YAML, src: new Stream.Readable(), // no inference possible - dest: new Stream.Writable(), // no inference possible }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + const validatedOptions = await Joi.validate(options, readerOptionsSchema); expect(validatedOptions.origin).toBe(TYPE_JS); - expect(validatedOptions.target).toBe(TYPE_YAML); }); it('should infer options.origin to JS from object type', async () => { @@ -104,7 +100,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: {}, dest: 'some-file', }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + const validatedOptions = await Joi.validate(options, readerOptionsSchema); expect(validatedOptions.origin).toBe(TYPE_JS); }); }); @@ -114,18 +110,18 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { await expectOptionsValidationError({ src: './test', dest: 'some-file', - }) + }, readerOptionsSchema) ); it('should reject when options.src is undefined', async () => - await expectOptionsValidationError({ dest: 'some-file' }) + await expectOptionsValidationError({ dest: 'some-file' }, readerOptionsSchema) ); it('should reject when options.src is null', async () => await expectOptionsValidationError({ src: null, dest: 'some-file', - }) + }, readerOptionsSchema) ); it('should resolve to default origin ' + DEFAULT_ORIGIN + ' when options.src is Stream.Readable and ' + @@ -136,289 +132,332 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { dest: new Stream.Writable(), target: TYPE_YAML, }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + const validatedOptions = await Joi.validate(options, readerOptionsSchema); expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); expect(validatedOptions.target).toBe(TYPE_YAML); }); - }); - - describe('Testing options.dest schema validation', () => { - it('should reject when options.dest is undefined', async () => - await expectOptionsValidationError({ src: './test/data/test-data.js' }) - ); - - it('should reject when options.dest is null', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: null, - }) - ); - it('should resolve output when options.dest is Stream.Writable and options.target is set', async () => { - expect.assertions(2); - const options = { - src: './test/data/test-data.js', - dest: new Stream.Writable(), - target: TYPE_YAML, - }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); - expect(validatedOptions.origin).toBe(TYPE_JS); - expect(validatedOptions.target).toBe(TYPE_YAML); - }); - - it('should resolve output when options.dest is Stream.Writable (to YAML file) and options.target is not set', - async () => { - expect.assertions(2); + describe('Testing options.origin schema validation', () => { + it('should resolve with default ' + DEFAULT_ORIGIN + ' when options.origin is undefined and options.src does ' + + 'not allow to infer the type', async () => { const options = { - src: './test/data/test-data.js', - dest: fs.createWriteStream('./test/tmp/test-data.yaml'), + src: './test/data/test-data', + dest: 'some-file' }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); - expect(validatedOptions.origin).toBe(TYPE_JS); - expect(validatedOptions.target).toBe(TYPE_YAML); + const validatedOptions = await Joi.validate(options, readerOptionsSchema); + expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); }); - it('should resolve default ' + DEFAULT_TARGET + ' output when options.dest is Stream.Writable (without path) ' + - 'and options.target is not set)', async () => { - expect.assertions(2); - const options = { - src: './test/data/test-data.js', - dest: new Stream.Writable(), - }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); - expect(validatedOptions.origin).toBe(TYPE_JS); - expect(validatedOptions.target).toBe(DEFAULT_TARGET); - }); - }); - - describe('Testing options.origin schema validation', () => { - it('should resolve with default ' + DEFAULT_ORIGIN + ' when options.origin is undefined and options.src does ' + - 'not allow to infer the type', async () => { - const options = { - src: './test/data/test-data', - dest: 'some-file' - }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); - expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); - }); + it('should resolve when options.origin has valid target ' + TYPE_JS, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data', + dest: 'some-file', + origin: TYPE_JS, + }, readerOptionsSchema); + }); - it('should resolve when options.origin has valid target ' + TYPE_JS, async () => { - await expectOptionsValidationSuccess({ - src: './test/data/test-data', - dest: 'some-file', - origin: TYPE_JS, + it('should resolve when options.origin has valid target ' + TYPE_JSON, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data-json', + dest: 'some-file', + origin: TYPE_JSON, + }, readerOptionsSchema); }); - }); - it('should resolve when options.origin has valid target ' + TYPE_JSON, async () => { - await expectOptionsValidationSuccess({ - src: './test/data/test-data-json', - dest: 'some-file', - origin: TYPE_JSON, + it('should resolve when options.origin has valid target ' + TYPE_YAML, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data-yaml', + dest: 'some-file', + origin: TYPE_YAML, + }, readerOptionsSchema); }); + + it('should reject when options.origin is null', async () => + await expectOptionsValidationError({ + src: './test/data/test-data-yaml', + dest: 'some-file', + origin: null, + }, readerOptionsSchema) + ); + + it('should reject when options.origin is not allowed value', async () => + await expectOptionsValidationError({ + src: './test/data/test-data-yaml', + dest: 'some-file', + origin: 'not-allowed', + }, readerOptionsSchema) + ); }); - it('should resolve when options.origin has valid target ' + TYPE_YAML, async () => { - await expectOptionsValidationSuccess({ - src: './test/data/test-data-yaml', - dest: 'some-file', - origin: TYPE_YAML, - }); + describe('Testing options.imports schema validation', () => { + const nonStringIdentifier = {}; + it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + imports: nonStringIdentifier, + }, readerOptionsSchema) + ); + + const invalidIdentifier = '#3/-'; + it('should reject invalid identifier \'' + invalidIdentifier + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + imports: invalidIdentifier, + }, readerOptionsSchema) + ); + + const validIdentifier = 'bar'; + it('should accept valid \'' + validIdentifier + '\' identifier', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + imports: validIdentifier, + }, readerOptionsSchema) + ); }); + }); - it('should reject when options.origin is null', async () => - await expectOptionsValidationError({ - src: './test/data/test-data-yaml', - dest: 'some-file', - origin: null, - }) + describe('Testing writerOptionsSchema validation', () => { + it('should reject when options is missing (null)', async () => + await expectOptionsValidationError(null, writerOptionsSchema) ); - it('should reject when options.origin is not allowed value', async () => - await expectOptionsValidationError({ - src: './test/data/test-data-yaml', - dest: 'some-file', - origin: 'not-allowed', - }) + it('should reject when options is missing (undefined)', async () => + await expectOptionsValidationError(undefined, writerOptionsSchema) ); - }); - describe('Testing options.target schema validation', () => { - it('should resolve with default ' + DEFAULT_TARGET + ' when options.target is undefined', async () => { + it('should set all defaults', async () => { + expect.assertions(4); const options = { - src: './test/data/test-data.js', - dest: 'some-file' + dest: './test/tmp/test-data.js', }; - const validatedOptions = await Joi.validate(options, transformerOptionsSchema); + const validatedOptions = await Joi.validate(options, writerOptionsSchema); expect(validatedOptions.target).toBe(DEFAULT_TARGET); + expect(validatedOptions.indent).toBe(DEFAULT_INDENT); + expect(validatedOptions.exports).toBe(DEFAULT_JS_EXPORTS_IDENTIFIER); + expect(validatedOptions.force).toBe(DEFAULT_FORCE_FILE_OVERWRITE); }); - it('should resolve when options.target has valid target ' + TYPE_JS, async () => { - await expectOptionsValidationSuccess({ - src: './test/data/test-data.js', - dest: 'some-file', - target: TYPE_JS, - }); + it('should infer options.target from file type', async () => { + expect.assertions(1); + const options = { + dest: 'some-file.yml', // non default type + }; + const validatedOptions = await Joi.validate(options, writerOptionsSchema); + expect(validatedOptions.target).toBe(TYPE_YAML); }); - it('should resolve when options.target has valid target ' + TYPE_JSON, async () => { - await expectOptionsValidationSuccess({ - src: './test/data/test-data.json', - dest: 'some-file', - target: TYPE_JS, - }); + it('should infer options.target from set target', async () => { + expect.assertions(1); + const options = { + target: TYPE_YAML, + dest: new Stream.Writable(), // no inference possible + }; + const validatedOptions = await Joi.validate(options, writerOptionsSchema); + expect(validatedOptions.target).toBe(TYPE_YAML); }); - it('should resolve when options.target has valid target ' + TYPE_YAML, async () => { - await expectOptionsValidationSuccess({ - src: './test/data/test-data.yaml', - dest: 'some-file', - target: TYPE_YAML, - }); + it('should infer options.target to JS from object type', async () => { + expect.assertions(1); + const options = { + dest: {}, + }; + const validatedOptions = await Joi.validate(options, writerOptionsSchema); + expect(validatedOptions.target).toBe(TYPE_JS); }); - it('should reject when options.target is null', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - target: null, - }) - ); + describe('Testing options.dest schema validation', () => { + it('should reject when options.dest is undefined', async () => + await expectOptionsValidationError({ src: './test/data/test-data.js' }, writerOptionsSchema) + ); - it('should reject when options.target is not allowed value', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - target: 'not-allowed', - }) - ); - }); + it('should reject when options.dest is null', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: null, + }, writerOptionsSchema) + ); - describe('Testing options.imports schema validation', () => { - const nonStringIdentifier = {}; - it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - imports: nonStringIdentifier, - }) - ); + it('should resolve output when options.dest is Stream.Writable and options.target is set', async () => { + expect.assertions(1); + const options = { + dest: new Stream.Writable(), + target: TYPE_YAML, + }; + const validatedOptions = await Joi.validate(options, writerOptionsSchema); + expect(validatedOptions.target).toBe(TYPE_YAML); + }); - const invalidIdentifier = '#3/-'; - it('should reject invalid identifier \'' + invalidIdentifier + '\'', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - imports: invalidIdentifier, - }) - ); + it('should resolve output when options.dest is Stream.Writable (to YAML file) and options.target is not set', + async () => { + expect.assertions(1); + const options = { + dest: fs.createWriteStream('./test/tmp/test-data.yaml'), + }; + const validatedOptions = await Joi.validate(options, writerOptionsSchema); + expect(validatedOptions.target).toBe(TYPE_YAML); + }); + + it('should resolve default ' + DEFAULT_TARGET + ' output when options.dest is Stream.Writable (without path) ' + + 'and options.target is not set)', async () => { + expect.assertions(1); + const options = { + dest: new Stream.Writable(), + }; + const validatedOptions = await Joi.validate(options, writerOptionsSchema); + expect(validatedOptions.target).toBe(DEFAULT_TARGET); + }); + }); - const validIdentifier = 'bar'; - it('should accept valid \'' + validIdentifier + '\' identifier', async () => - await expectOptionsValidationSuccess({ - src: './test/data/test-data.js', - dest: 'some-file', - imports: validIdentifier, - }) - ); - }); + describe('Testing options.target schema validation', () => { + it('should resolve with default ' + DEFAULT_TARGET + ' when options.target is undefined', async () => { + const options = { + dest: 'some-file' + }; + const validatedOptions = await Joi.validate(options, writerOptionsSchema); + expect(validatedOptions.target).toBe(DEFAULT_TARGET); + }); - describe('Testing options.exports schema validation', () => { - const nonStringIdentifier = {}; - it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - exports: nonStringIdentifier, - }) - ); + it('should resolve when options.target has valid target ' + TYPE_JS, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + target: TYPE_JS, + }, writerOptionsSchema); + }); - const invalidIdentifier = '#3/-'; - it('should reject invalid identifier \'' + invalidIdentifier + '\'', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - exports: invalidIdentifier, - }) - ); + it('should resolve when options.target has valid target ' + TYPE_JSON, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data.json', + dest: 'some-file', + target: TYPE_JS, + }, writerOptionsSchema); + }); - const validIdentifier = 'bar'; - it('should accept valid \'' + validIdentifier + '\' identifier', async () => - await expectOptionsValidationSuccess({ - src: './test/data/test-data.js', - dest: 'some-file', - exports: validIdentifier, - }) - ); - }); + it('should resolve when options.target has valid target ' + TYPE_YAML, async () => { + await expectOptionsValidationSuccess({ + src: './test/data/test-data.yaml', + dest: 'some-file', + target: TYPE_YAML, + }, writerOptionsSchema); + }); - describe('Testing options.force schema validation', () => { - const notBoolean = {}; - it('should reject non-boolean value \'' + stringify(notBoolean) + '\'', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - force: notBoolean, - }) - ); + it('should reject when options.target is null', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + target: null, + }, writerOptionsSchema) + ); - it('should accept valid value \'false\'', async () => - await expectOptionsValidationSuccess({ - src: './test/data/test-data.js', - dest: 'some-file', - force: false, - }) - ); + it('should reject when options.target is not allowed value', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + target: 'not-allowed', + }, writerOptionsSchema) + ); + }); - it('should accept valid value \'true\'', async () => - await expectOptionsValidationSuccess({ - src: './test/data/test-data.js', - dest: 'some-file', - force: true, - }) - ); - }); + describe('Testing options.exports schema validation', () => { + const nonStringIdentifier = {}; + it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + exports: nonStringIdentifier, + }, writerOptionsSchema) + ); + + const invalidIdentifier = '#3/-'; + it('should reject invalid identifier \'' + invalidIdentifier + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + exports: invalidIdentifier, + }, writerOptionsSchema) + ); + + const validIdentifier = 'bar'; + it('should accept valid \'' + validIdentifier + '\' identifier', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + exports: validIdentifier, + }, writerOptionsSchema) + ); + }); - describe('Testing options.indent schema validation', () => { - const notInteger = 0.5; - it('should reject non-integer value \'' + stringify(notInteger) + '\'', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - indent: notInteger, - }) - ); + describe('Testing options.force schema validation', () => { + const notBoolean = {}; + it('should reject non-boolean value \'' + stringify(notBoolean) + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + force: notBoolean, + }, writerOptionsSchema) + ); - it('should accept valid \'' + MIN_INDENT + '\'', async () => - await expectOptionsValidationSuccess({ - src: './test/data/test-data.js', - dest: 'some-file', - indent: MIN_INDENT, - }) - ); + it('should accept valid value \'false\'', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + force: false, + }, writerOptionsSchema) + ); - it('should accept valid \'' + MAX_INDENT + '\'', async () => - await expectOptionsValidationSuccess({ - src: './test/data/test-data.js', - dest: 'some-file', - indent: MAX_INDENT, - }) - ); + it('should accept valid value \'true\'', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + force: true, + }, writerOptionsSchema) + ); + }); - it('should reject < \'' + MIN_INDENT + '\'', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - indent: MIN_INDENT - 1, - }) - ); + describe('Testing options.indent schema validation', () => { + const notInteger = 0.5; + it('should reject non-integer value \'' + stringify(notInteger) + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: notInteger, + }, writerOptionsSchema) + ); - it('should reject > \'' + MAX_INDENT + '\'', async () => - await expectOptionsValidationError({ - src: './test/data/test-data.js', - dest: 'some-file', - indent: MAX_INDENT + 1, - }) - ); + it('should accept valid \'' + MIN_INDENT + '\'', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: MIN_INDENT, + }, writerOptionsSchema) + ); + + it('should accept valid \'' + MAX_INDENT + '\'', async () => + await expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: MAX_INDENT, + }, writerOptionsSchema) + ); + + it('should reject < \'' + MIN_INDENT + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: MIN_INDENT - 1, + }, writerOptionsSchema) + ); + + it('should reject > \'' + MAX_INDENT + '\'', async () => + await expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + indent: MAX_INDENT + 1, + }, writerOptionsSchema) + ); + }); }); }); From 764cd451e012b6f1483813921e966c57a186d55e Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 30 Jun 2017 10:31:49 +0200 Subject: [PATCH 12/58] Code improvements --- API-PRIVATE.md | 796 ++++++++++--------------------- API-PUBLIC.md | 374 ++++++--------- CHANGELOG.md | 5 +- README.md | 44 +- readme/DOCUMENTATION.md | 6 +- src/cli.js | 2 +- src/constants.js | 29 +- src/reader.js | 168 +++---- src/transformer.js | 13 +- src/type-definitions.js | 13 - src/validation/options-schema.js | 6 +- src/writer.js | 341 +++++-------- test/unit/test-writer.js | 6 +- 13 files changed, 635 insertions(+), 1168 deletions(-) diff --git a/API-PRIVATE.md b/API-PRIVATE.md index fca0540..301b40d 100644 --- a/API-PRIVATE.md +++ b/API-PRIVATE.md @@ -5,9 +5,8 @@ - [Modules](#modules) - [Members](#members) - [jy-transform:jyt ℗](#jy-transformjyt-%E2%84%97) -- [jy-transform:constants](#jy-transformconstants) +- [jy-transform:constants ℗](#jy-transformconstants-%E2%84%97) - [jy-transform:debug-log ℗](#jy-transformdebug-log-%E2%84%97) -- [jy-transform:middleware ℗](#jy-transformmiddleware-%E2%84%97) - [jy-transform:reader](#jy-transformreader) - [jy-transform:transformer](#jy-transformtransformer) - [jy-transform:type-definitions](#jy-transformtype-definitions) @@ -20,7 +19,6 @@ - [jy-transform:unit:helper-constants : Object ℗](#jy-transformunithelper-constants--codeobjectcode-%E2%84%97) - [jy-transform:unit:logger : Object ℗](#jy-transformunitlogger--codeobjectcode-%E2%84%97) - [jy-transform:test-unit:index ℗](#jy-transformtest-unitindex-%E2%84%97) -- [jy-transform:unit-test:test-middleware ℗](#jy-transformunit-testtest-middleware-%E2%84%97) - [jy-transform:unit-test:test-reader ℗](#jy-transformunit-testtest-reader-%E2%84%97) - [jy-transform:unit-test:test-transformer ℗](#jy-transformunit-testtest-transformer-%E2%84%97) - [jy-transform:unit-test:test-writer ℗](#jy-transformunit-testtest-writer-%E2%84%97) @@ -31,6 +29,11 @@ - [readJs ⇒ Promise.<Object> ℗](#readjs-%E2%87%92-codepromiseltobjectgtcode-%E2%84%97) - [readYaml ⇒ Promise.<Object> ℗](#readyaml-%E2%87%92-codepromiseltobjectgtcode-%E2%84%97) - [read ⇒ Promise.<string>](#read-%E2%87%92-codepromiseltstringgtcode) +- [transform ⇒ Promise.<String>](#transform-%E2%87%92-codepromiseltstringgtcode) +- [createExportsString ⇒ Promise.<string> ℗](#createexportsstring-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) +- [serializeJsToString ⇒ Promise.<string> ℗](#serializejstostring-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) +- [serializeJsToJsonString ⇒ string ℗](#serializejstojsonstring-%E2%87%92-codestringcode-%E2%84%97) +- [mkdirAndWrite ℗](#mkdirandwrite-%E2%84%97) - [writeYaml ⇒ Promise.<string> ℗](#writeyaml-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [writeJson ⇒ Promise.<string> ℗](#writejson-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [writeJs ⇒ Promise.<string> ℗](#writejs-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) @@ -44,7 +47,7 @@

    jy-transform:jyt

    The command line interface.

    -
    jy-transform:constants
    +
    jy-transform:constants

    Useful constants used for the module and its usage.

    jy-transform:debug-log
    @@ -54,9 +57,6 @@
  • JYT_DEBUG: (only ERROR logging via console.error)
  • -
    jy-transform:middleware
    -

    The middleware ensuring functions module.

    -
    jy-transform:reader

    This module provides the read functionality for YAML, JS or JSON sources.

    @@ -95,9 +95,6 @@ destination (file, object or stream.Readable).

    jy-transform:test-unit:index

    This unit test module tests the correct exporting from ./index.js.

    -
    jy-transform:unit-test:test-middleware
    -

    This unit test suite checks the validity and correctness of middleware module.

    -
    jy-transform:unit-test:test-reader

    This unit test suite checks the validity and correctness of ./src/reader.js module.

    @@ -133,7 +130,28 @@ destination (file, object or stream.Readable).

    exception on those.

    readPromise.<string>
    -

    TODO: doc me.

    +

    Reads a particular content type from a source provided in the passed options.

    +
    +
    transformPromise.<String>
    +

    The entry method for all transformation accepting a configuration object and +an (optional) middleware function. It executes the transformation logic:

    +
      +
    1. Input (read)
    2. +
    3. Transform [ + Middleware]
    4. +
    5. Output (write).
    6. +
    +
    +
    createExportsStringPromise.<string>
    +

    Creates a potential named 'module.exports[.exportsTo]' string.

    +
    +
    serializeJsToStringPromise.<string>
    +

    Serialize a JS object to string.

    +
    +
    serializeJsToJsonStringstring
    +

    Serialize a JS object to JSON string.

    +
    +
    mkdirAndWrite
    +

    Ensures that all dirs exists for file type dest and writes the JS object to file.

    writeYamlPromise.<string>

    Writes a JS object to a YAML destination.

    @@ -145,7 +163,7 @@ exception on those.

    Writes a JS object to a JS destination. The object is prefixed by module.exports[.${options.exports}] =.

    writePromise.<string>
    -

    TODO: doc me.

    +

    Writes the passe JS object to a particular destination described by the passed options.

    @@ -212,40 +230,41 @@ prints the result to the CLI. | Param | Type | Description | | --- | --- | --- | | args | Array | The first mandatory argument is the input file (`args[0]`), the second (optional) argument is the output file (`args[1]`). | -| cliOptions | module:type-definitions~Options | The options provided via CLI. | +| cliOptions | module:jy-transform:type-definitions~Options | The options provided via CLI. | -## jy-transform:constants +## jy-transform:constants ℗ Useful constants used for the module and its usage. -**Access:** public +**Access:** private -* [jy-transform:constants](#module_jy-transform_constants) - * [~DEFAULT_OPTIONS](#module_jy-transform_constants..DEFAULT_OPTIONS) : object - * [~UTF8](#module_jy-transform_constants..UTF8) : string +* [jy-transform:constants](#module_jy-transform_constants) ℗ + * [~DEFAULT_OPTIONS](#module_jy-transform_constants..DEFAULT_OPTIONS) : object ℗ + * [~UTF8](#module_jy-transform_constants..UTF8) : string ℗ * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string * [~TYPE_JSON](#module_jy-transform_constants..TYPE_JSON) : string * [~TYPE_JS](#module_jy-transform_constants..TYPE_JS) : string - * [~TYPE_MAP](#module_jy-transform_constants..TYPE_MAP) : Object - * [~DEFAULT_INDENT](#module_jy-transform_constants..DEFAULT_INDENT) : number - * [~MIN_INDENT](#module_jy-transform_constants..MIN_INDENT) : number - * [~MAX_INDENT](#module_jy-transform_constants..MAX_INDENT) : number - * [~DEFAULT_ORIGIN](#module_jy-transform_constants..DEFAULT_ORIGIN) : string - * [~DEFAULT_TARGET](#module_jy-transform_constants..DEFAULT_TARGET) : string - * [~DEFAULT_FORCE_FILE_OVERWRITE](#module_jy-transform_constants..DEFAULT_FORCE_FILE_OVERWRITE) : boolean - * [~ORIGIN_DESCRIPTION](#module_jy-transform_constants..ORIGIN_DESCRIPTION) : string - * [~TARGET_DESCRIPTION](#module_jy-transform_constants..TARGET_DESCRIPTION) : string - * [~DEST_DESCRIPTION](#module_jy-transform_constants..DEST_DESCRIPTION) : string - * [~DEFAULT_JS_IMPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_IMPORTS_IDENTIFIER) : string - * [~DEFAULT_JS_EXPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_EXPORTS_IDENTIFIER) : string + * [~TYPE_MAP](#module_jy-transform_constants..TYPE_MAP) : Object ℗ + * [~DEFAULT_INDENT](#module_jy-transform_constants..DEFAULT_INDENT) : number ℗ + * [~MIN_INDENT](#module_jy-transform_constants..MIN_INDENT) : number ℗ + * [~MAX_INDENT](#module_jy-transform_constants..MAX_INDENT) : number ℗ + * [~DEFAULT_ORIGIN](#module_jy-transform_constants..DEFAULT_ORIGIN) : string ℗ + * [~DEFAULT_TARGET](#module_jy-transform_constants..DEFAULT_TARGET) : string ℗ + * [~DEFAULT_FORCE_FILE_OVERWRITE](#module_jy-transform_constants..DEFAULT_FORCE_FILE_OVERWRITE) : boolean ℗ + * [~ORIGIN_DESCRIPTION](#module_jy-transform_constants..ORIGIN_DESCRIPTION) : string ℗ + * [~TARGET_DESCRIPTION](#module_jy-transform_constants..TARGET_DESCRIPTION) : string ℗ + * [~DEST_DESCRIPTION](#module_jy-transform_constants..DEST_DESCRIPTION) : string ℗ + * [~DEFAULT_JS_IMPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_IMPORTS_IDENTIFIER) : string ℗ + * [~DEFAULT_JS_EXPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_EXPORTS_IDENTIFIER) : string -### jy-transform:constants~DEFAULT_OPTIONS : object +### jy-transform:constants~DEFAULT_OPTIONS : object ℗ The default options. **Kind**: inner namespace of [jy-transform:constants](#module_jy-transform_constants) +**Access:** private **See** - [ORIGIN_DESCRIPTION](ORIGIN_DESCRIPTION) @@ -266,11 +285,11 @@ The default options. -### jy-transform:constants~UTF8 : string +### jy-transform:constants~UTF8 : string ℗ The 'utf8' constant. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private ### jy-transform:constants~TYPE_YAML : string @@ -294,92 +313,92 @@ The 'js' type constant. **Access:** public -### jy-transform:constants~TYPE_MAP : Object +### jy-transform:constants~TYPE_MAP : Object ℗ A map for extensions to type. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~DEFAULT_INDENT : number +### jy-transform:constants~DEFAULT_INDENT : number ℗ The default file indention (4 SPACEs). **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~MIN_INDENT : number +### jy-transform:constants~MIN_INDENT : number ℗ The minimum file indention (0 SPACE). **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~MAX_INDENT : number +### jy-transform:constants~MAX_INDENT : number ℗ The maximum file indention (8 SPACEs). **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~DEFAULT_ORIGIN : string +### jy-transform:constants~DEFAULT_ORIGIN : string ℗ The default `origin` value: 'yaml'. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~DEFAULT_TARGET : string +### jy-transform:constants~DEFAULT_TARGET : string ℗ The default `origin` value: 'js'. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~DEFAULT_FORCE_FILE_OVERWRITE : boolean +### jy-transform:constants~DEFAULT_FORCE_FILE_OVERWRITE : boolean ℗ Whether to overwrite existing file or object on output. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~ORIGIN_DESCRIPTION : string +### jy-transform:constants~ORIGIN_DESCRIPTION : string ℗ The `origin` description value. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~TARGET_DESCRIPTION : string +### jy-transform:constants~TARGET_DESCRIPTION : string ℗ The `target` description value. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~DEST_DESCRIPTION : string +### jy-transform:constants~DEST_DESCRIPTION : string ℗ The `dest` description value. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private -### jy-transform:constants~DEFAULT_JS_IMPORTS_IDENTIFIER : string +### jy-transform:constants~DEFAULT_JS_IMPORTS_IDENTIFIER : string ℗ The `src` exports identifier value to read. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private **Example** ```js module.exports.foo = {...}; // here 'foo' is the identifier for an object to read from the source! ``` -### jy-transform:constants~DEFAULT_JS_EXPORTS_IDENTIFIER : string +### jy-transform:constants~DEFAULT_JS_EXPORTS_IDENTIFIER : string ℗ The `dest` exports identifier value to write. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Access:** private ## jy-transform:debug-log ℗ @@ -388,6 +407,11 @@ The debug logger. Can be enabled via environment variables (set to `true`): - `JYT_DEBUG`: (only ERROR logging via `console.error`) **Access:** private + +* [jy-transform:debug-log](#module_jy-transform_debug-log) ℗ + * [~debug](#module_jy-transform_debug-log..debug) + * [~error](#module_jy-transform_debug-log..error) + ### jy-transform:debug-log~debug @@ -395,83 +419,13 @@ DEBUG function. **Kind**: inner constant of [jy-transform:debug-log](#module_jy-transform_debug-log) **Access:** protected - - -## jy-transform:middleware ℗ -The middleware ensuring functions module. - -**Access:** private - -* [jy-transform:middleware](#module_jy-transform_middleware) ℗ - * [~identity(object)](#module_jy-transform_middleware..identity) ⇒ Promise.<object> ℗ - * [~identityMiddleware(object)](#module_jy-transform_middleware..identityMiddleware) ⇒ Promise.<object> - * [~ensureMiddleware(middleware)](#module_jy-transform_middleware..ensureMiddleware) ⇒ function - - - -### jy-transform:middleware~identity(object) ⇒ Promise.<object> ℗ -Promise which reflects the identity of passed JSON: `f(object) → object`. - -**Kind**: inner method of [jy-transform:middleware](#module_jy-transform_middleware) -**Returns**: Promise.<object> - - A Promise resolving the passed JS `object`. -**Access:** private - -| Param | Type | Description | -| --- | --- | --- | -| object | Object | The JS object which is resolved by Promise. | - - - -### jy-transform:middleware~identityMiddleware(object) ⇒ Promise.<object> -Middleware Promise which reflects the identity of passed JS: `f(object) → object`. - -**Kind**: inner method of [jy-transform:middleware](#module_jy-transform_middleware) -**Returns**: Promise.<object> - A Promise resolving the passed JS object. -**Access:** protected - -| Param | Type | Description | -| --- | --- | --- | -| object | Object | The object which is returned in Promise. | - -**Example** -```js -import { identityMiddleware } from './lib/middleware'; -transformer.transform(options, identityMiddleware) - .then((object) => { - // ... - }): -``` - + -### jy-transform:middleware~ensureMiddleware(middleware) ⇒ function -Ensure that the given middleware Promise is a function if set. -If not set a new JSON 'identity' Promise is returned which simply passes -a JSON object. - -**Kind**: inner method of [jy-transform:middleware](#module_jy-transform_middleware) -**Returns**: function - - The given middleware Promise or a new JSON 'identity' middleware Promise function. -**Throws**: - -- TypeError - Will throw this error when the passed `middleware` is not type of `Function`. +### jy-transform:debug-log~error +DEBUG function. +**Kind**: inner constant of [jy-transform:debug-log](#module_jy-transform_debug-log) **Access:** protected - -| Param | Type | Description | -| --- | --- | --- | -| middleware | function | This middleware Promise can be used to intercept the JSON object for altering he passed JSON, the function signature is `function(object).` **NOTE:** the Promise has to return the processed JSON. | - -**Example** -```js -import { ensureMiddleware } from './lib/middleware'; -const myMiddleware = async (object) => { - //...do something with object - return object; -}; -transformer.transform(options, ensureMiddleware(myMiddleware)) - .then((transformedObject) => { - //... - }): -``` ## jy-transform:reader @@ -481,8 +435,7 @@ This module provides the _read_ functionality for YAML, JS or JSON sources. * [jy-transform:reader](#module_jy-transform_reader) * [~fsPromisified](#module_jy-transform_reader..fsPromisified) ℗ - * [~createOnReadableEventFunction(readable, bufs)](#module_jy-transform_reader..createOnReadableEventFunction) ⇒ function ℗ - * [~readFromStream(readable, resolve, reject, origin)](#module_jy-transform_reader..readFromStream) ℗ + * [~readFromStream(readable, origin)](#module_jy-transform_reader..readFromStream) ⇒ Promise.<Object> @@ -491,33 +444,18 @@ Promisified `fs` module. **Kind**: inner constant of [jy-transform:reader](#module_jy-transform_reader) **Access:** private - - -### jy-transform:reader~createOnReadableEventFunction(readable, bufs) ⇒ function ℗ -Creates a function to read from the passed source in to the given buffer array. - -**Kind**: inner method of [jy-transform:reader](#module_jy-transform_reader) -**Returns**: function - - The function which reads and buffers. -**Access:** private - -| Param | Type | Description | -| --- | --- | --- | -| readable | stream.Readable | The source to read from. | -| bufs | Array | The temporary buffer array. | - -### jy-transform:reader~readFromStream(readable, resolve, reject, origin) ℗ +### jy-transform:reader~readFromStream(readable, origin) ⇒ Promise.<Object> ℗ Reads from a passed stream and resolves by callback. **Kind**: inner method of [jy-transform:reader](#module_jy-transform_reader) +**Returns**: Promise.<Object> - The read content as JS object representation. **Access:** private | Param | Type | Description | | --- | --- | --- | | readable | Stream.Readable | The source to read from. | -| resolve | function | Callback for success case. | -| reject | function | Callback for Error case. | | origin | string | Origin type, must be 'yaml' or 'json'/'js'. | @@ -526,52 +464,6 @@ Reads from a passed stream and resolves by callback. This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. **Access:** public - - -### jy-transform:transformer~transform ⇒ Promise.<String> -The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic: -1. Input (read) -2. Transform [ + Middleware] -3. Output (write). - -**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: - -- TypeError Will throw this error when the passed `middleware` is not type of `Function`. -- Error Will throw plain error when writing to file failed due to any reason. - -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | module:type-definitions~TransformerOptions | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(object) ```

    **NOTE:** the Promise has to return the processed JSON. | - -**Example** -```js -import { transform } from 'jy-transform'; -const options = {...}; -const middleware = async (object) { - object.myproperty = 'new value'; - return object; -}; - -// ---- Promise style: - -transform(options, middleware) - .then(console.log) - .catch(console.error); - -// ---- async/await style: -try { - const msg = await transform(options, middleware); - console.log(msg); -} catch (err) { - console.error(err.stack); -}; -``` ## jy-transform:type-definitions @@ -583,8 +475,6 @@ The type definitions for this module. * [~ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) : object * [~WriterOptions](#module_jy-transform_type-definitions..WriterOptions) : object * [~TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) : object - * [~TEESTTransformerOptions](#module_jy-transform_type-definitions..TEESTTransformerOptions) : object - * [~TEESTT2ransformerOptions](#module_jy-transform_type-definitions..TEESTT2ransformerOptions) : WriterOptions | ReaderOptions * [~joi](#external_joi) ℗ * [.ValidationError](#external_joi.ValidationError) ℗ * [.Schema](#external_joi.Schema) ℗ @@ -643,20 +533,6 @@ The configuration properties provided to the transformer function. | indent | number | 2 | The indention in files. | | force | string | false | Force overwriting of existing output files on write phase. | - - -### jy-transform:type-definitions~TEESTTransformerOptions : object -The configuration properties provided to the transformer function. - -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public - - -### jy-transform:type-definitions~TEESTT2ransformerOptions : WriterOptions | ReaderOptions -The configuration properties provided to the transformer function. - -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public ### jy-transform:type-definitions~joi ℗ @@ -868,13 +744,7 @@ destination (file, object or [stream.Readable](stream.Readable)). * [jy-transform:writer](#module_jy-transform_writer) * [~fsPromisified](#module_jy-transform_writer..fsPromisified) ℗ - * [~createExportsString([exportsTo])](#module_jy-transform_writer..createExportsString) ⇒ Promise.<string> ℗ - * [~serializeJsToString(object, indent, [exportsTo])](#module_jy-transform_writer..serializeJsToString) ⇒ Promise ℗ - * [~serializeJsToJsonString(object, indent)](#module_jy-transform_writer..serializeJsToJsonString) ⇒ string ℗ - * [~getConsecutiveDestName(dest)](#module_jy-transform_writer..getConsecutiveDestName) ⇒ string ℗ - * [~writeToFile(object, dest, target, resolve, reject, [forceOverwrite])](#module_jy-transform_writer..writeToFile) ⇒ Promise.<string> ℗ - * [~mkdirAndWrite()](#module_jy-transform_writer..writeToFile..mkdirAndWrite) ℗ - * [~writeToStream(object, dest, target, resolve, reject)](#module_jy-transform_writer..writeToStream) ⇒ Promise.<string> ℗ + * [~writeToStream(object, dest, target)](#module_jy-transform_writer..writeToStream) ⇒ Promise.<string> @@ -883,107 +753,9 @@ Promisified `fs` module. **Kind**: inner constant of [jy-transform:writer](#module_jy-transform_writer) **Access:** private - - -### jy-transform:writer~createExportsString([exportsTo]) ⇒ Promise.<string> ℗ -Creates a potential named `'module.exports[.exportsTo]'` string. - -**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) -**Returns**: Promise.<string> - Resolves with the exports string. -**Access:** private - -| Param | Type | Description | -| --- | --- | --- | -| [exportsTo] | string | The export name. | - - - -### jy-transform:writer~serializeJsToString(object, indent, [exportsTo]) ⇒ Promise ℗ -Serialize a JS object to string. - -**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) -**Returns**: Promise - - Promise resolve with the serialized JS object. -**Access:** private -**Todo** - -- [ ] [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> - `'\'use strict\';' + os.EOL + os.EOL + ...`)? - - -| Param | Type | Description | -| --- | --- | --- | -| object | Object | The JS Object to serialize. | -| indent | number | The indention. | -| [exportsTo] | string | Name for export (*IMPORTANT:* must be a valid ES6 identifier). | - - - -### jy-transform:writer~serializeJsToJsonString(object, indent) ⇒ string ℗ -Serialize a JS object to JSON string. - -**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) -**Returns**: string - The serialized JSON. -**Access:** private - -| Param | Type | Description | -| --- | --- | --- | -| object | Object | Object to serialize. | -| indent | number | Indention. | - - - -### jy-transform:writer~getConsecutiveDestName(dest) ⇒ string ℗ -Turns the destination file name into a name containing a consecutive -number if it exists. It iterates over the files until it finds a file -name which does not exist. - -**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) -**Returns**: string - - A consecutive file name or the original one if - `dest` file does not exist. -**Access:** private - -| Param | Type | Description | -| --- | --- | --- | -| dest | string | The destination file. | - - - -### jy-transform:writer~writeToFile(object, dest, target, resolve, reject, [forceOverwrite]) ⇒ Promise.<string> ℗ -Writes a serialized object to file. - -**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) -**Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). -**Throws**: - -- Error If serialized JSON file could not be written due to any reason. - -**Access:** private -**See** - -- [TYPE_YAML](TYPE_YAML) -- [TYPE_JSON](TYPE_JSON) -- [TYPE_JS](TYPE_JS) - - -| Param | Type | Description | -| --- | --- | --- | -| object | string | The object to write into file. | -| dest | string | The file destination path. | -| target | string | The target type, one of [ 'yaml' | 'json' | 'js' ]. | -| resolve | function | The Promise `resolve` callback. | -| reject | function | The Promise `reject` callback. | -| [forceOverwrite] | boolean | Forces overwriting the destination file if `true`. | - - - -#### writeToFile~mkdirAndWrite() ℗ -Ensures that all dirs exists for `dest` and writes the file. - -**Kind**: inner method of [writeToFile](#module_jy-transform_writer..writeToFile) -**Access:** private -### jy-transform:writer~writeToStream(object, dest, target, resolve, reject) ⇒ Promise.<string> ℗ +### jy-transform:writer~writeToStream(object, dest, target) ⇒ Promise.<string> ℗ Writes a string serialized data object to a stream. **Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) @@ -1005,8 +777,6 @@ Writes a string serialized data object to a stream. | object | string | The data to write into stream. | | dest | string | The stream destination. | | target | string | The target type, one of [ 'yaml' | 'json' | 'js' ]. | -| resolve | function | The Promise `resolve` callback. | -| reject | function | The Promise `reject` callback. | @@ -1119,12 +889,6 @@ This function formats the log string by given options to log. ## jy-transform:test-unit:index ℗ This unit test module tests the correct exporting from _./index.js_. -**Access:** private - - -## jy-transform:unit-test:test-middleware ℗ -This unit test suite checks the validity and correctness of [middleware](middleware) module. - **Access:** private @@ -1210,122 +974,172 @@ Reads the data from a given JS or JSON source. | Param | Type | Description | | --- | --- | --- | -| options | Options | Contains the JS/JSON source reference to read from. | +| options | [ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) | Contains the JS/JSON source reference to read from. | + + + +## readYaml ⇒ Promise.<Object> ℗ +Loads a single YAML source containing document and returns a JS object. +*NOTE:* this function does not understand multi-document sources, it throws +exception on those. + +**Kind**: global variable +**Returns**: Promise.<Object> - Contains the read JS object. +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| options | [ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) | Contains the YAML source reference to read from. | + + + +## read ⇒ Promise.<string> +Reads a particular content type from a source provided in the passed `options`. + +**Kind**: global variable +**Returns**: Promise.<string> - Resolves with JS object result. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) | The read options. | **Example** ```js -var Reader = require('jy-transform').Reader; -var logger = ...; -var reader = new Reader(logger); +import { read } from 'jy-transform'; + // --- from file path -var options = { - src: 'foo.js' +options = { + src: 'foo.yml' }; -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); +read(options) + .then(obj => console.log(JSON.stringify(obj))) + .catch(console.error); // --- from Readable options = { - src: fs.createReadStream('foo.js') + src: fs.createReadStream('foo.yml') }; -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); +read(options) + .then(obj => console.log(JSON.stringify(obj))) + .catch(console.error); +``` + + +## transform ⇒ Promise.<String> +The entry method for all transformation accepting a configuration object and +an (optional) middleware function. It executes the transformation logic: +1. Input (read) +2. Transform [ + Middleware] +3. Output (write). + +**Kind**: global variable +**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError Will throw this error when the passed `middleware` is not type of `Function`. +- Error Will throw plain error when writing to _destination object_ failed due to any reason. +**Access:** public -// --- from object +| Param | Type | Description | +| --- | --- | --- | +| options | [TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` async function(object) ```

    **NOTE:** the Promise has to return the processed JS object. | -options = { - src: { - foo: 'bar' - } +**Example** +```js +import { transform } from 'jy-transform'; +const options = {...}; + +const middleware = async (object) { + object.myproperty = 'new value'; + return object; }; -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); +// ---- Promise style: + +transform(options, middleware) + .then(console.log) + .catch(console.error); + + +// ---- async/await style: + +try { + const msg = await transform(options, middleware); + console.log(msg); +} catch (err) { + console.error(err.stack); +}; ``` - + -## readYaml ⇒ Promise.<Object> ℗ -Loads a single YAML source containing document and returns a JS object. -*NOTE:* this function does not understand multi-document sources, it throws -exception on those. +## createExportsString ⇒ Promise.<string> ℗ +Creates a potential named `'module.exports[.exportsTo]'` string. **Kind**: global variable -**Returns**: Promise.<Object> - Contains the read JS object. +**Returns**: Promise.<string> - Resolves with the exports string. **Access:** private | Param | Type | Description | | --- | --- | --- | -| options | Options | Contains the YAML source reference to read from. | +| [exportsTo] | string | The export name. | -**Example** -```js -var Reader = require('jy-transform').Reader; -var logger = ...; -var reader = new Reader(logger); + -// --- from file path +## serializeJsToString ⇒ Promise.<string> ℗ +Serialize a JS object to string. -options = { - src: 'foo.yml' -}; +**Kind**: global variable +**Returns**: Promise.<string> - - Promise resolve with the serialized JS content. +**Access:** private +**Todo** -reader.readYaml(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); +- [ ] [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> + `'\'use strict\';' + os.EOL + os.EOL + ...`)? -// --- from Readable +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JS Object to serialize. | +| indent | number | The indention. | +| [exportsTo] | string | Name for export (*IMPORTANT:* must be a valid ES6 identifier). | -options = { - src: fs.createReadStream('foo.yml') -}; + -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - +## serializeJsToJsonString ⇒ string ℗ +Serialize a JS object to JSON string. -## read ⇒ Promise.<string> -TODO: doc me. +**Kind**: global variable +**Returns**: string - The serialized JSON. +**Access:** private + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The object to serialize. | +| indent | number | The code indention. | + + + +## mkdirAndWrite ℗ +Ensures that all dirs exists for file type `dest` and writes the JS object to file. **Kind**: global variable -**Returns**: Promise.<string> - Resolves with read success message. -**Access:** public +**Access:** private | Param | Type | Description | | --- | --- | --- | -| options | module:type-definitions~ReaderOptions | The read options. | +| object | string | The object to write into file. | +| dest | string | The file destination path. | +| target | string | The target type, one of [ 'yaml' | 'json' | 'js' ]. | +| [forceOverwrite] | boolean | Forces overwriting the destination file if `true`. | @@ -1349,46 +1163,8 @@ Writes a JS object to a YAML destination. | Param | Type | Description | | --- | --- | --- | | object | Object | The JS object to write into YAML destination. | -| options | Options | The write destination and indention. | - -**Example** -```js -var Writer = require('jy-transform').Writer; -var logger = ...; -var writer = new Writer(logger); - -// ---- write obj to file - -var obj = {...}, -var options = { - dest: 'result.yml', - indent: 2 -} - -writer.writeYaml(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to Writable - -options = { - dest: fs.createWriteStream('result.yml'), - indent: 4 -} +| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write destination and indention. | -writer.writeYaml(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` ## writeJson ⇒ Promise.<string> ℗ @@ -1407,61 +1183,8 @@ Writes a JS object to a JSON destination. | Param | Type | Description | | --- | --- | --- | | object | Object | The JS object to write into JSON destination. | -| options | Options | The write destination and indention. | - -**Example** -```js -var Writer = require('jy-transform').Writer; -var logger = ...; -var writer = new Writer(logger); +| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write destination and indention. | -// ---- write obj to file - -var obj = {...}; -var options = { - dest: 'result.json', - indent: 2 -} - -writer.writeJson(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to Writable - -options = { - dest: fs.createWriteStream('result.json'), - indent: 4 -} - -writer.writeJson(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - -// ---- write obj to object - -options = { - dest: {}, - indent: 4 -} - -writer.writeJson(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` ## writeJs ⇒ Promise.<string> ℗ @@ -1480,73 +1203,60 @@ Writes a JS object to a JS destination. The object is prefixed by `module.export | Param | Type | Description | | --- | --- | --- | | object | Object | The JSON to write into JS destination. | -| options | Options | The write destination and indention. | +| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write destination and indention. | + + + +## write ⇒ Promise.<string> +Writes the passe JS object to a particular destination described by the passed `options`. + +**Kind**: global variable +**Returns**: Promise.<string> - Resolves with write success message. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JS source object to write. | +| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write options. | **Example** ```js -var Writer = require('jy-transform').Writer; -var logger = ...; -var writer = new Writer(logger); +import { write } from 'jy-transform'; + -// ---- write obj to file +// ---- write obj to file --- -var obj = {...}; -var options = { - dest: 'result.js', - indent: 2 +const obj = {...}; +const options = { + dest: 'result.js', + indent: 4 } -writer.writeJs(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); +write(obj, options) + .then(console.log) + .catch(console.error); -// ---- write obj to Writable +// ---- write obj to Writable --- options = { - dest: fs.createWriteStream('result.json'), - indent: 4 + dest: fs.createWriteStream('result.json'), + indent: 4 } -writer.writeJs(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); +write(obj, options) + .then(console.log) + .catch(console.error); -// ---- write obj to object +// ---- write obj to object --- options = { - dest: {}, - indent: 2 + dest: {}, + indent: 4 } -writer.writeJs(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); +write(obj, options) + .then(console.log) + .catch(console.error); ``` - - -## write ⇒ Promise.<string> -TODO: doc me. - -**Kind**: global variable -**Returns**: Promise.<string> - Resolves with write success message. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | Object | The source object to write. | -| options | module:type-definitions~WriterOptions | The write options. | - diff --git a/API-PUBLIC.md b/API-PUBLIC.md index b1dbf10..3bfdcc4 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -4,12 +4,12 @@ - [Modules](#modules) - [Members](#members) -- [jy-transform:constants](#jy-transformconstants) - [jy-transform:reader](#jy-transformreader) - [jy-transform:transformer](#jy-transformtransformer) - [jy-transform:type-definitions](#jy-transformtype-definitions) - [jy-transform:writer](#jy-transformwriter) - [read ⇒ Promise.<string>](#read-%E2%87%92-codepromiseltstringgtcode) +- [transform ⇒ Promise.<String>](#transform-%E2%87%92-codepromiseltstringgtcode) - [write ⇒ Promise.<string>](#write-%E2%87%92-codepromiseltstringgtcode) @@ -17,9 +17,6 @@ ## Modules

    -
    jy-transform:constants
    -

    Useful constants used for the module and its usage.

    -
    jy-transform:reader

    This module provides the read functionality for YAML, JS or JSON sources.

    @@ -39,179 +36,22 @@ destination (file, object or stream.Readable).

    readPromise.<string>
    -

    TODO: doc me.

    +

    Reads a particular content type from a source provided in the passed options.

    +
    +
    transformPromise.<String>
    +

    The entry method for all transformation accepting a configuration object and +an (optional) middleware function. It executes the transformation logic:

    +
      +
    1. Input (read)
    2. +
    3. Transform [ + Middleware]
    4. +
    5. Output (write).
    6. +
    writePromise.<string>
    -

    TODO: doc me.

    +

    Writes the passe JS object to a particular destination described by the passed options.

    - - -## jy-transform:constants -Useful constants used for the module and its usage. - -**Access:** public - -* [jy-transform:constants](#module_jy-transform_constants) - * [~DEFAULT_OPTIONS](#module_jy-transform_constants..DEFAULT_OPTIONS) : object - * [~UTF8](#module_jy-transform_constants..UTF8) : string - * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string - * [~TYPE_JSON](#module_jy-transform_constants..TYPE_JSON) : string - * [~TYPE_JS](#module_jy-transform_constants..TYPE_JS) : string - * [~TYPE_MAP](#module_jy-transform_constants..TYPE_MAP) : Object - * [~DEFAULT_INDENT](#module_jy-transform_constants..DEFAULT_INDENT) : number - * [~MIN_INDENT](#module_jy-transform_constants..MIN_INDENT) : number - * [~MAX_INDENT](#module_jy-transform_constants..MAX_INDENT) : number - * [~DEFAULT_ORIGIN](#module_jy-transform_constants..DEFAULT_ORIGIN) : string - * [~DEFAULT_TARGET](#module_jy-transform_constants..DEFAULT_TARGET) : string - * [~DEFAULT_FORCE_FILE_OVERWRITE](#module_jy-transform_constants..DEFAULT_FORCE_FILE_OVERWRITE) : boolean - * [~ORIGIN_DESCRIPTION](#module_jy-transform_constants..ORIGIN_DESCRIPTION) : string - * [~TARGET_DESCRIPTION](#module_jy-transform_constants..TARGET_DESCRIPTION) : string - * [~DEST_DESCRIPTION](#module_jy-transform_constants..DEST_DESCRIPTION) : string - * [~DEFAULT_JS_IMPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_IMPORTS_IDENTIFIER) : string - * [~DEFAULT_JS_EXPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_EXPORTS_IDENTIFIER) : string - - - -### jy-transform:constants~DEFAULT_OPTIONS : object -The default options. - -**Kind**: inner namespace of [jy-transform:constants](#module_jy-transform_constants) -**See** - -- [ORIGIN_DESCRIPTION](ORIGIN_DESCRIPTION) -- [TARGET_DESCRIPTION](TARGET_DESCRIPTION) -- [DEST_DESCRIPTION](DEST_DESCRIPTION) - -**Properties** - -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| origin | string | "yaml" | The default origin type. | -| target | string | "js" | The default target type. | -| dest | string | "relative_to_input_file" | The default dest description. | -| indent | number | 4 | The default indention for files. | -| force | boolean | false | Whether to overwrite existing file on output. | -| imports | string | | The exports name for reading from JS source file or objects only. | -| exports | string | | The exports name for usage in JS file or object only. | - - - -### jy-transform:constants~UTF8 : string -The 'utf8' constant. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~TYPE_YAML : string -The 'yaml' type constant. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~TYPE_JSON : string -The 'json' type constant. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~TYPE_JS : string -The 'js' type constant. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~TYPE_MAP : Object -A map for extensions to type. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~DEFAULT_INDENT : number -The default file indention (4 SPACEs). - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~MIN_INDENT : number -The minimum file indention (0 SPACE). - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~MAX_INDENT : number -The maximum file indention (8 SPACEs). - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~DEFAULT_ORIGIN : string -The default `origin` value: 'yaml'. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~DEFAULT_TARGET : string -The default `origin` value: 'js'. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~DEFAULT_FORCE_FILE_OVERWRITE : boolean -Whether to overwrite existing file or object on output. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~ORIGIN_DESCRIPTION : string -The `origin` description value. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~TARGET_DESCRIPTION : string -The `target` description value. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~DEST_DESCRIPTION : string -The `dest` description value. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public - - -### jy-transform:constants~DEFAULT_JS_IMPORTS_IDENTIFIER : string -The `src` exports identifier value to read. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public -**Example** -```js -module.exports.foo = {...}; // here 'foo' is the identifier for an object to read from the source! -``` - - -### jy-transform:constants~DEFAULT_JS_EXPORTS_IDENTIFIER : string -The `dest` exports identifier value to write. - -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public ## jy-transform:reader @@ -224,52 +64,6 @@ This module provides the _read_ functionality for YAML, JS or JSON sources. This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. **Access:** public - - -### jy-transform:transformer~transform ⇒ Promise.<String> -The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic: -1. Input (read) -2. Transform [ + Middleware] -3. Output (write). - -**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) -**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: - -- TypeError Will throw this error when the passed `middleware` is not type of `Function`. -- Error Will throw plain error when writing to file failed due to any reason. - -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | module:type-definitions~TransformerOptions | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(object) ```

    **NOTE:** the Promise has to return the processed JSON. | - -**Example** -```js -import { transform } from 'jy-transform'; -const options = {...}; -const middleware = async (object) { - object.myproperty = 'new value'; - return object; -}; - -// ---- Promise style: - -transform(options, middleware) - .then(console.log) - .catch(console.error); - -// ---- async/await style: -try { - const msg = await transform(options, middleware); - console.log(msg); -} catch (err) { - console.error(err.stack); -}; -``` ## jy-transform:type-definitions @@ -281,8 +75,6 @@ The type definitions for this module. * [~ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) : object * [~WriterOptions](#module_jy-transform_type-definitions..WriterOptions) : object * [~TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) : object - * [~TEESTTransformerOptions](#module_jy-transform_type-definitions..TEESTTransformerOptions) : object - * [~TEESTT2ransformerOptions](#module_jy-transform_type-definitions..TEESTT2ransformerOptions) : WriterOptions | ReaderOptions @@ -336,20 +128,6 @@ The configuration properties provided to the transformer function. | indent | number | 2 | The indention in files. | | force | string | false | Force overwriting of existing output files on write phase. | - - -### jy-transform:type-definitions~TEESTTransformerOptions : object -The configuration properties provided to the transformer function. - -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public - - -### jy-transform:type-definitions~TEESTT2ransformerOptions : WriterOptions | ReaderOptions -The configuration properties provided to the transformer function. - -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public ## jy-transform:writer @@ -360,20 +138,95 @@ destination (file, object or [stream.Readable](stream.Readable)). ## read ⇒ Promise.<string> -TODO: doc me. +Reads a particular content type from a source provided in the passed `options`. + +**Kind**: global variable +**Returns**: Promise.<string> - Resolves with JS object result. +**Access:** public + +| Param | Type | Description | +| --- | --- | --- | +| options | [ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) | The read options. | + +**Example** +```js +import { read } from 'jy-transform'; + + +// --- from file path + +options = { + src: 'foo.yml' +}; + +read(options) + .then(obj => console.log(JSON.stringify(obj))) + .catch(console.error); + + +// --- from Readable + +options = { + src: fs.createReadStream('foo.yml') +}; + +read(options) + .then(obj => console.log(JSON.stringify(obj))) + .catch(console.error); +``` + + +## transform ⇒ Promise.<String> +The entry method for all transformation accepting a configuration object and +an (optional) middleware function. It executes the transformation logic: +1. Input (read) +2. Transform [ + Middleware] +3. Output (write). **Kind**: global variable -**Returns**: Promise.<string> - Resolves with read success message. +**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). +**Throws**: + +- TypeError Will throw this error when the passed `middleware` is not type of `Function`. +- Error Will throw plain error when writing to _destination object_ failed due to any reason. + **Access:** public | Param | Type | Description | | --- | --- | --- | -| options | module:type-definitions~ReaderOptions | The read options. | +| options | [TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) | The configuration for a transformation. | +| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` async function(object) ```

    **NOTE:** the Promise has to return the processed JS object. | + +**Example** +```js +import { transform } from 'jy-transform'; +const options = {...}; + +const middleware = async (object) { + object.myproperty = 'new value'; + return object; +}; + +// ---- Promise style: + +transform(options, middleware) + .then(console.log) + .catch(console.error); + +// ---- async/await style: + +try { + const msg = await transform(options, middleware); + console.log(msg); +} catch (err) { + console.error(err.stack); +}; +``` ## write ⇒ Promise.<string> -TODO: doc me. +Writes the passe JS object to a particular destination described by the passed `options`. **Kind**: global variable **Returns**: Promise.<string> - Resolves with write success message. @@ -381,6 +234,47 @@ TODO: doc me. | Param | Type | Description | | --- | --- | --- | -| options | Object | The source object to write. | -| options | module:type-definitions~WriterOptions | The write options. | +| object | Object | The JS source object to write. | +| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write options. | + +**Example** +```js +import { write } from 'jy-transform'; + + +// ---- write obj to file --- + +const obj = {...}; +const options = { + dest: 'result.js', + indent: 4 +} + +write(obj, options) + .then(console.log) + .catch(console.error); + +// ---- write obj to Writable --- + +options = { + dest: fs.createWriteStream('result.json'), + indent: 4 +} + +write(obj, options) + .then(console.log) + .catch(console.error); + + +// ---- write obj to object --- + +options = { + dest: {}, + indent: 4 +} + +write(obj, options) + .then(console.log) + .catch(console.error); +``` diff --git a/CHANGELOG.md b/CHANGELOG.md index f46204a..8d03d41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ #### v3.0.0 +- New Features: + - The _middleware_ function must not need a Promise anymore, but is still recommended. + - **CLI & API Changes (Backwards Incompatible!):** - Removed support for Node.js < v4.0 - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community @@ -23,7 +26,7 @@ - Removal of _development_ branch - Usage of [babel](https://babeljs.io/) and therefore most modern language features - Code base could be shrinked and readabilty was improved - - Usage of _native promises_ instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html) + - Usage of _native_ Promises instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html) - Tests re-written in [Jest](https://facebook.github.io/jest) (could get rid of [assert](https://github.com/defunctzombie/commonjs-assert), [mocha](https://mochajs.org/) and [istanbul](https://github.com/gotwarlost/istanbul)) - Add travis build for Node.js v8.x diff --git a/README.md b/README.md index 34a61b9..31cfefd 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,8 @@ npm install jy-transform --global ```javascript import { transform, read, write } from 'jy-transform'; -// --- transform + +// --- transform from source to destination --- const transformOptions = { src: 'foo/bar.yaml', @@ -49,6 +50,7 @@ const transformFunc = async (object) => { return object; }; +// of course, inside an async try { const msg = await transform(transformOptions, transformFunc); console.log(msg); @@ -56,18 +58,20 @@ try { console.error(err.stack); } -// --- read + +// --- read into JS object from particular source (file, stream or JS object) --- let object; try { - object = await read({ src: 'foo/bar.yaml' }); + object = await read({ src: 'foo/bar.yaml' }); // here: read from file console.log(JSON.stringify(object)); } catch (err) { console.error(err.stack); } -// --- write + +// --- write a JS object to particular destination --- try { const msg = await write(object, { dest: 'foo/bar.yaml' }); @@ -87,7 +91,8 @@ types into each other format. ## Usage -The module can be used on CLI or as API (the latter is fully [Promise](http://bluebirdjs.com/docs/api-reference.html) +The module can be used on CLI or as API (the latter is fully +[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)) based). ### Usage Types @@ -111,23 +116,26 @@ these consists of different phases: #### Reading -Reading from: +From: - _*.yaml_ file - _*.js_ file - _*.json_ file -Additionally, on API level to a: +Additionally, on API level from a: - `stream.Readable` - - Serialized JSON and YAML - - Requires `options.origin` property set + - Contain serialized JS, JSON or YAML + - If not file stream it requires `options.origin` property set - Reads as UTF-8 -- JS `object` (actually, this means the reading phase is skipped, because object is in-memory already) +- JS `object` + - Actually, this means the reading phase is "skipped", because object is in-memory already + - Of course, this case _cannot_ not be applied to serialized JSON or YAML content -#### Transformation +#### Transformation [+ Middleware] -The transformation can take place into several directions: +The _transformation_ is usually a format change, but can also be refer to content changes on the +intermediate JS object (the latter via _middleware_). All possible directions are: - YAML ⇒ JS - YAML ⇒ JSON @@ -145,14 +153,12 @@ while: - [JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript) = _*.js_ (JS object) - [JSON](http://json.org) = _*.json_ (JS object serialized as JSON) -#### Middleware - -Apply actions on the intermediate JS object via injected [Promise](http://bluebirdjs.com/docs/api-reference.html) -functions. This is an optional part for [transformation](#transformation) phase. +As mentioned above a _middleware_ can apply particular actions on the intermediate JS object via injected functions. +This is an optional part for [transformation](#transformation) phase. #### Writing -Writing to: +To: - _*.yaml_ file - _*.js_ file @@ -164,7 +170,9 @@ Additionally, on API level to a: - Serialized JS, JSON and YAML - Requires `options.target` property set - Writes UTF-8 -- JS `object` +- JS `object + - JS as simple reference + - YAML and JSON as serialized string ### Limitations diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index 9af7e33..e05a5b0 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -561,17 +561,17 @@ given (initially empty) JS object. ```javascript const key1 = async (data) => { - objectPath.set(data, 'key1', 'value1'); + odata.key1 = 'value1'; return data; }; const key2 = async (data) => { - objectPath.set(data, 'key2', 'value2'); + data.key2 = 'value2'; return data; }; const key3 = async (data) => { - objectPath.set(data, 'key3', 'value3'); + data.key3 = 'value3'; return data; }; ``` diff --git a/src/cli.js b/src/cli.js index 4dc0a4f..79348fc 100755 --- a/src/cli.js +++ b/src/cli.js @@ -81,7 +81,7 @@ function error(err) { * * @param {Array} args - The first mandatory argument is the input file (`args[0]`), the second (optional) * argument is the output file (`args[1]`). - * @param {module:type-definitions~Options} cliOptions - The options provided via CLI. + * @param {module:jy-transform:type-definitions~Options} cliOptions - The options provided via CLI. * @private */ function main(args, cliOptions) { diff --git a/src/constants.js b/src/constants.js index 0b92e79..9dcd9dc 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,7 +1,7 @@ /** * @module jy-transform:constants * @description Useful constants used for the module and its usage. - * @public + * @private */ /** @@ -9,7 +9,7 @@ * * @type {string} * @constant - * @public + * @private */ export const UTF8 = 'utf8'; @@ -42,7 +42,7 @@ export const TYPE_JS = 'js'; * A map for extensions to type. * * @type {{yml: string, yaml: string, js: string, json: string}} - * @public + * @private */ export const TYPE_MAP = { yml: TYPE_YAML, @@ -55,7 +55,7 @@ export const TYPE_MAP = { * The default file indention (4 SPACEs). * @type {number} * @constant - * @public + * @private */ export const DEFAULT_INDENT = 2; @@ -63,7 +63,7 @@ export const DEFAULT_INDENT = 2; * The minimum file indention (0 SPACE). * @type {number} * @constant - * @public + * @private */ export const MIN_INDENT = 0; @@ -71,7 +71,7 @@ export const MIN_INDENT = 0; * The maximum file indention (8 SPACEs). * @type {number} * @constant - * @public + * @private */ export const MAX_INDENT = 8; @@ -79,7 +79,7 @@ export const MAX_INDENT = 8; * The default `origin` value: 'yaml'. * @type {string} * @constant - * @public + * @private */ export const DEFAULT_ORIGIN = TYPE_YAML; @@ -87,7 +87,7 @@ export const DEFAULT_ORIGIN = TYPE_YAML; * The default `origin` value: 'js'. * @type {string} * @constant - * @public + * @private */ export const DEFAULT_TARGET = TYPE_JS; @@ -95,7 +95,7 @@ export const DEFAULT_TARGET = TYPE_JS; * Whether to overwrite existing file or object on output. * @type {boolean} * @constant - * @public + * @private */ export const DEFAULT_FORCE_FILE_OVERWRITE = false; @@ -103,7 +103,7 @@ export const DEFAULT_FORCE_FILE_OVERWRITE = false; * The `origin` description value. * @type {string} * @constant - * @public + * @private */ export const ORIGIN_DESCRIPTION = 'if not given, the type is tried to be inferred from the extension of source path, ' + 'else it is \'' + DEFAULT_ORIGIN + '\''; @@ -112,7 +112,7 @@ export const ORIGIN_DESCRIPTION = 'if not given, the type is tried to be inferre * The `target` description value. * @type {string} * @constant - * @public + * @private */ export const TARGET_DESCRIPTION = 'if not given, the type is tried to be inferred from the extension of destination' + ' path, else it is \'' + DEFAULT_TARGET + '\''; @@ -121,14 +121,14 @@ export const TARGET_DESCRIPTION = 'if not given, the type is tried to be inferre * The `dest` description value. * @type {string} * @constant - * @public + * @private */ export const DEST_DESCRIPTION = 'storing relative to input file'; /** * The `src` exports identifier value to read. * @type {string} - * @public + * @private * @constant * @example * module.exports.foo = {...}; // here 'foo' is the identifier for an object to read from the source! @@ -138,7 +138,7 @@ export const DEFAULT_JS_IMPORTS_IDENTIFIER = undefined; /** * The `dest` exports identifier value to write. * @type {string} - * @public + * @private * @constant */ export const DEFAULT_JS_EXPORTS_IDENTIFIER = undefined; @@ -157,6 +157,7 @@ export const DEFAULT_JS_EXPORTS_IDENTIFIER = undefined; * @see {@link ORIGIN_DESCRIPTION} * @see {@link TARGET_DESCRIPTION} * @see {@link DEST_DESCRIPTION} + * @private */ export const DEFAULT_OPTIONS = { origin: ORIGIN_DESCRIPTION, diff --git a/src/reader.js b/src/reader.js index ec2da13..121e66e 100644 --- a/src/reader.js +++ b/src/reader.js @@ -27,107 +27,81 @@ import { */ const fsPromisified = promisify(fs); -/** - * Creates a function to read from the passed source in to the given buffer array. - * - * @param {stream.Readable} readable - The source to read from. - * @param {Array} bufs - The temporary buffer array. - * @returns {Function} - The function which reads and buffers. - * @private - */ -function createOnReadableEventFunction(readable, bufs) { - return () => { - let chunk; - while (null !== (chunk = readable.read())) { - logger.debug('JSON chunk: ', chunk); - bufs.push(chunk); - } - }; -} - /** * Reads from a passed stream and resolves by callback. * * @param {Stream.Readable} readable - The source to read from. - * @param {Function} resolve - Callback for success case. - * @param {Function} reject - Callback for Error case. * @param {string} origin - Origin type, must be 'yaml' or 'json'/'js'. + * @returns {Promise.} The read content as JS object representation. * @private */ -function readFromStream(readable, resolve, reject, origin) { - const buffers = []; - readable - .on('readable', createOnReadableEventFunction(readable, buffers)) // TODO better: .on('data', (data) => buffers.push(data)) ?? - .on('error', reject) - .on('end', () => { - const buffer = Buffer.concat(buffers); - try { - logger.debug(origin + ' reading from Readable'); - if (origin === TYPE_JSON || origin === TYPE_JS) { - resolve(JSON.parse(buffer.toString(UTF8))); - } else { // HINT: commented (see below): if (origin === YAML) { - resolve(jsYaml.safeLoad(buffer.toString(UTF8))); +function readFromStream(readable, origin) { + return new Promise((resolve, reject) => { + const buffers = []; + readable + .on('data', data => buffers.push(data)) + .on('error', reject) + .on('end', () => { + const buffer = Buffer.concat(buffers); + try { + logger.debug(origin + ' reading from Readable'); + if (origin === TYPE_JSON || origin === TYPE_JS) { + resolve(JSON.parse(buffer.toString(UTF8))); + } else { // HINT: commented (see below): if (origin === YAML) { + resolve(jsYaml.safeLoad(buffer.toString(UTF8))); + } + } catch (err) { // probably a SyntaxError for JSON or a YAMLException + logger.error('Unexpected error: ' + err.stack); + readable.emit('error', err); // send to .on('error',... } - // HINT: for the sake of test coverage it's commented, since this is a private method - // we have control over options.origin inside this class! - // else { - // reject(new Error('Unsupported type: ' + origin + ' to read from Readable')); - // } - } catch (err) { // probably a SyntaxError for JSON or a YAMLException - logger.error('Unexpected error: ' + err.stack); - readable.emit('error', err); // send to .on('error',... - } - }); + }); + }); } /** * Reads the data from a given JS or JSON source. * - * @param {Options} options - Contains the JS/JSON source reference to read from. + * @param {module:jy-transform:type-definitions~ReaderOptions} options - Contains the JS/JSON source reference + * to read from. * @returns {Promise.} Contains the read JS object. * @private */ async function readJs(options) { - logger.debug('OPTIONS BEFORE ASSERTING IN readJs:::' + JSON.stringify(options)); - return new Promise((resolve, reject) => { - if (typeof options.src === 'string') { // path to JSON or JS file - try { - const resolvedPath = path.resolve('', options.src); - - if (options.imports) { - // eslint-disable-next-line import/no-dynamic-require, global-require - const object = require(resolvedPath)[options.imports]; - logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(object, null, 4)); - if (!object) { - reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object' + - ' but could not find this object, pls ensure that file ' + options.src + - ' contains it.')); - } else { - resolve(object); - } + if (typeof options.src === 'string') { // path to JSON or JS file + try { + const resolvedPath = path.resolve('', options.src); + if (options.imports) { + // eslint-disable-next-line import/no-dynamic-require, global-require + const object = require(resolvedPath)[options.imports]; + logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(object, null, 4)); + if (!object) { + throw new Error('an identifier string \'' + options.imports + '\' was specified for JS object' + + ' but could not find this object, pls ensure that file ' + options.src + ' contains it.'); } else { - // eslint-disable-next-line import/no-dynamic-require, global-require - resolve(require(resolvedPath)); // reads both: JS and JSON! + return object; } - } catch (err) { // probably a SyntaxError - logger.error('Unexpected error: ' + err.stack); - reject(err); - } - } else if (isStream.readable(options.src)) { - readFromStream(options.src, resolve, reject, TYPE_JSON); // reads both: JS or JSON! - } else if (options.imports) { // options.src is JS object here! - const subObject = options.src[options.imports]; - logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(subObject, null, 4)); - if (!subObject) { - reject(new Error('an identifier string \'' + options.imports + '\' was specified for JS object ' + - 'but could not find this object, pls ensure that object source contains it.')); } else { - resolve(Object.assign({}, subObject)); // clone, do not alter original object! + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(resolvedPath); // reads both: JS and JSON! } - } else { // // options.src is JS object here! - resolve(Object.assign({}, options.src)); // clone, do not alter original object! + } catch (err) { // probably a SyntaxError + logger.error('Unexpected error: ' + err.stack); + throw err; } - }); + } else if (isStream.readable(options.src)) { + return await readFromStream(options.src, TYPE_JSON); // reads both: JS or JSON! + } else if (options.imports) { // options.src is JS object here! + const subObject = options.src[options.imports]; + logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(subObject, null, 4)); + if (!subObject) { + throw new Error('an identifier string \'' + options.imports + '\' was specified for JS object ' + + 'but could not find this object, pls ensure that object source contains it.'); + } else { + return Object.assign({}, subObject); // clone, do not alter original object! + } + } else { // // options.src is JS object here! + return Object.assign({}, options.src); // clone, do not alter original object! + } } /** @@ -135,29 +109,25 @@ async function readJs(options) { * *NOTE:* this function does not understand multi-document sources, it throws * exception on those. * - * @param {Options} options - Contains the YAML source reference to read from. + * @param {module:jy-transform:type-definitions~ReaderOptions} options - Contains the YAML source reference + * to read from. * @returns {Promise.} Contains the read JS object. * @private */ async function readYaml(options) { - logger.debug('OPTIONS BEFORE ASSERTING IN readYaml::: ' + JSON.stringify(options)); - return new Promise((resolve, reject) => { - if (typeof options.src === 'string') { - // load source from YAML file - fsPromisified.readFile(options.src, UTF8) - .then((yaml) => { - logger.debug('YAML loaded from file ' + options.src); - try { - resolve(jsYaml.safeLoad(yaml)); - } catch (err) { // probably a YAMLException - logger.error('Unexpected error: ' + err.stack); - reject(err); - } - }); - } else { // as validation has passed already, this can only be stream here - readFromStream(options.src, resolve, reject, TYPE_YAML); + if (typeof options.src === 'string') { + // load source from YAML file + const yaml = await fsPromisified.readFile(options.src, UTF8); + logger.debug('YAML loaded from file ' + options.src); + try { + return jsYaml.safeLoad(yaml); + } catch (err) { // probably a YAMLException + logger.error('Unexpected error: ' + err.stack); + throw err; } - }); + } + // as validation has passed already, this can only be stream here + return await readFromStream(options.src, TYPE_YAML); } // /** @@ -181,12 +151,13 @@ async function readYaml(options) { /** * Reads a particular content type from a source provided in the passed `options`. * - * @param {module:type-definitions~ReaderOptions} options - The read options. + * @param {module:jy-transform:type-definitions~ReaderOptions} options - The read options. * @returns {Promise.} Resolves with JS object result. * @public * @example * import { read } from 'jy-transform'; * + * * // --- from file path * * options = { @@ -205,7 +176,6 @@ async function readYaml(options) { * }; * * read(options) - * .then(function (obj){ * .then(obj => console.log(JSON.stringify(obj))) * .catch(console.error); */ diff --git a/src/transformer.js b/src/transformer.js index d92aae1..fc32a88 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -29,14 +29,14 @@ const callMiddlewareIfExists = async (object, middleware) => { * 2. Transform [ + Middleware] * 3. Output (write). * - * @param {module:type-definitions~TransformerOptions} options - The configuration for a transformation. - * @param {Function} [middleware] - This middleware Promise can be used to intercept - * the JSON object for altering the passed JSON, the function signature is: + * @param {module:jy-transform:type-definitions~TransformerOptions} options - The configuration for a transformation. + * @param {Function} [middleware] - This middleware Promise can be used to + * intercept the JSON object for altering the passed JSON, the function signature is: * ``` - * function(object) + * async function(object) * ``` *

    - * **NOTE:** the Promise has to return the processed JSON. + * **NOTE:** the Promise has to return the processed JS object. * @returns {Promise.} Containing the transformation result as message (e.g. to be logged by caller). * @throws {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. * @throws {Error} Will throw plain error when writing to _destination object_ failed due to any reason. @@ -44,6 +44,7 @@ const callMiddlewareIfExists = async (object, middleware) => { * @example * import { transform } from 'jy-transform'; * const options = {...}; + * * const middleware = async (object) { * object.myproperty = 'new value'; * return object; @@ -55,7 +56,9 @@ const callMiddlewareIfExists = async (object, middleware) => { * .then(console.log) * .catch(console.error); * + * * // ---- async/await style: + * * try { * const msg = await transform(options, middleware); * console.log(msg); diff --git a/src/type-definitions.js b/src/type-definitions.js index ab1b086..7e891fe 100644 --- a/src/type-definitions.js +++ b/src/type-definitions.js @@ -86,16 +86,3 @@ * @property {string} [force=false] - Force overwriting of existing output files on write phase. * @public */ - -/** - * The configuration properties provided to the transformer function. - * @typedef {object} TEESTTransformerOptions - * @type {module:type-definitions~WriterOptions} - * @type {module:type-definitions~ReaderOptions} - * @public - */ -/** - * The configuration properties provided to the transformer function. - * @typedef {(WriterOptions|ReaderOptions)} TEESTT2ransformerOptions - * @public - */ diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js index ae6c6e3..05f2c9d 100644 --- a/src/validation/options-schema.js +++ b/src/validation/options-schema.js @@ -22,10 +22,6 @@ import { * @private */ -// ///////////////////////////////////////////////////////////////////////////// -// SCHEMAS -// ///////////////////////////////////////////////////////////////////////////// - /** * The prepared {@link external:joi.JoiSchema} for validating the {@link Reader} options. * @type {JoiSchema} @@ -82,7 +78,7 @@ export const readerOptionsSchema = Joi.object().keys({ export const writerOptionsSchema = Joi.object().keys({ dest: Joi .alternatives().try( - Joi.string() // TODO must be existing file (relative or not? -> check) + Joi.string() .min(1) .label('dest - OUTPUT-FILE'), Joi.object().type(Stream.Writable), diff --git a/src/writer.js b/src/writer.js index 8b6d8da..524b113 100644 --- a/src/writer.js +++ b/src/writer.js @@ -41,16 +41,14 @@ const fsPromisified = promisify(fs); * @returns {Promise.} Resolves with the exports string. * @private */ -function createExportsString(exportsTo) { - return new Promise((resolve) => { - let exports = 'module.exports'; - if (exportsTo) { - exports += '.' + exportsTo + ' = '; - } else { - exports += ' = '; - } - resolve(exports); - }); +async function createExportsString(exportsTo) { + let exports = 'module.exports'; + if (exportsTo) { + exports += '.' + exportsTo + ' = '; + } else { + exports += ' = '; + } + return exports; } /** @@ -59,25 +57,25 @@ function createExportsString(exportsTo) { * @param {Object} object - The JS Object to serialize. * @param {number} indent - The indention. * @param {string} [exportsTo] - Name for export (*IMPORTANT:* must be a valid ES6 identifier). - * @returns {Promise} - Promise resolve with the serialized JS object. + * @returns {Promise.} - Promise resolve with the serialized JS content. * @private * @todo [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> * `'\'use strict\';' + os.EOL + os.EOL + ...`)? */ -function serializeJsToString(object, indent, exportsTo) { - return createExportsString(exportsTo) - .then(exportsStr => exportsStr + serializeJs.serialize(object, { indent }) + ';' + os.EOL); +async function serializeJsToString(object, indent, exportsTo) { + const exportsStr = await createExportsString(exportsTo); + return exportsStr + serializeJs.serialize(object, { indent }) + ';' + os.EOL; } /** * Serialize a JS object to JSON string. * - * @param {Object} object - Object to serialize. - * @param {number} indent - Indention. - * @returns {string} The serialized JSON. + * @param {Object} object - The object to serialize. + * @param {number} indent - The code indention. + * @returns {string} The serialized JSON. * @private */ -function serializeJsToJsonString(object, indent) { +async function serializeJsToJsonString(object, indent) { return jsonStringifySafe(object, null, indent) + os.EOL; } @@ -103,14 +101,35 @@ function getConsecutiveDestName(dest) { return tmpDest; } +/** + * Ensures that all dirs exists for file type `dest` and writes the JS object to file. + * + * @param {string} object - The object to write into file. + * @param {string} dest - The file destination path. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. + * @private + */ +async function mkdirAndWrite(object, dest, target, forceOverwrite) { + const destDir = path.dirname(dest); + logger.debug('Destination dir: ' + destDir); + await mkdirp(destDir); + logger.debug('Destination dir ' + destDir + ' successfully written'); + let finalDestination = dest; + if (forceOverwrite === undefined || forceOverwrite === false) { + finalDestination = getConsecutiveDestName(dest); + logger.debug('Setting was: do not overwrite, using destination ' + finalDestination + '.'); + } + await fsPromisified.writeFile(finalDestination, object, UTF8); + return 'Writing \'' + target + '\' file \'' + finalDestination + '\' successful.'; +} + /** * Writes a serialized object to file. * * @param {string} object - The object to write into file. * @param {string} dest - The file destination path. * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @param {Function} resolve - The Promise `resolve` callback. - * @param {Function} reject - The Promise `reject` callback. * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. * @see {@link TYPE_YAML} * @see {@link TYPE_JSON} @@ -119,54 +138,30 @@ function getConsecutiveDestName(dest) { * @throws {Error} If serialized JSON file could not be written due to any reason. * @private */ -function writeToFile(object, dest, target, resolve, reject, forceOverwrite) { - /** - * Ensures that all dirs exists for `dest` and writes the file. - * - * @private - */ - function mkdirAndWrite() { - const destDir = path.dirname(dest); - logger.debug('Destination dir: ' + destDir); - mkdirp(destDir) - .then(() => { - logger.debug('Destination dir ' + destDir + ' successfully written'); - if (forceOverwrite === undefined || forceOverwrite === false) { // TODO shorten - dest = getConsecutiveDestName(dest); - logger.debug('Setting was: do not overwrite, using destination ' + dest + '.'); +function writeToFile(object, dest, target, forceOverwrite) { + return new Promise((resolve, reject) => { + return fsPromisified.stat(dest) + .then((stats) => { + if (stats.isDirectory()) { + reject(new Error('Destination file is a directory, pls specify a valid file resource!')); + return; } - return fsPromisified.writeFile(dest, object, UTF8); + // file exists + resolve(mkdirAndWrite(object, dest, target, forceOverwrite)); }) - .then(() => resolve('Writing \'' + target + '\' file \'' + dest + '\' successful.')) - .catch((err) => { - err.message = 'Could not write \'' + target + '\' file \'' + dest + '\', cause: ' + err.message; - reject(err); + .catch(() => { + // ignore error (because file could possibly not exist at this point of time) + resolve(mkdirAndWrite(object, dest, target, forceOverwrite)); }); - } - - return fsPromisified.stat(dest) - .then((stats) => { - if (stats.isDirectory()) { // TODO remove when checked by Schema? Hmm, it could not exist at this point of time...! - reject(new Error('Destination file is a directory, pls specify a valid file resource!')); - } else { - // file exists - mkdirAndWrite(); - } - }) - .catch(() => { - // ignore error (because file could possibly not exist at this point of time) - mkdirAndWrite(); - }); + }); } /** * Writes a string serialized data object to a stream. * - * @param {string} object - The data to write into stream. - * @param {string} dest - The stream destination. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @param {Function} resolve - The Promise `resolve` callback. - * @param {Function} reject - The Promise `reject` callback. + * @param {string} object - The data to write into stream. + * @param {string} dest - The stream destination. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. * @see {@link TYPE_YAML} * @see {@link TYPE_JSON} * @see {@link TYPE_JS} @@ -174,168 +169,77 @@ function writeToFile(object, dest, target, resolve, reject, forceOverwrite) { * @throws {Error} If serialized JS object could not be written due to any reason. * @private */ -function writeToStream(object, dest, target, resolve, reject) { - dest - .on('error', reject) - .on('finish', () => resolve('Writing ' + target + ' to stream successful.')); +function writeToStream(object, dest, target) { + return new Promise((resolve, reject) => { + dest + .on('error', reject) + .on('finish', () => resolve('Writing ' + target + ' to stream successful.')); - // write stringified data - dest.write(object); - dest.end(); + // write stringified data + dest.write(object); + dest.end(); + }); } /** * Writes a JS object to a YAML destination. * - * @param {Object} object - The JS object to write into YAML destination. - * @param {Options} options - The write destination and indention. + * @param {Object} object - The JS object to write into YAML destination. + * @param {module:jy-transform:type-definitions~WriterOptions} options - The write destination and indention. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). * @throws {Error} If YAML destination could not be written due to any reason. * @private - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * var writer = new Writer(logger); - * - * // ---- write obj to file - * - * var obj = {...}, - * var options = { - * dest: 'result.yml', - * indent: 2 - * } - * - * writer.writeYaml(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to Writable - * - * options = { - * dest: fs.createWriteStream('result.yml'), - * indent: 4 - * } - * - * writer.writeYaml(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); */ async function writeYaml(object, options) { - return new Promise((resolve, reject) => { - let yaml; - try { - yaml = jsYaml.safeDump(object, { indent: options.indent, noRefs: true }); - } catch (err) { - err.message = 'Could not write YAML to \'' + options.dest + '\', cause: ' + err.message; - reject(err); - return; - } + let yaml; + try { + yaml = jsYaml.safeDump(object, { indent: options.indent, noRefs: true }); + } catch (err) { + err.message = 'Could not write YAML to \'' + options.dest + '\', cause: ' + err.message; + throw err; + } - if (typeof options.dest === 'string') { // file - writeToFile(yaml, options.dest, TYPE_YAML, resolve, reject, options.force); - } else if (isStream.writable(options.dest)) { // stream - writeToStream(yaml, options.dest, TYPE_YAML, resolve, reject); - } else { // object - options.dest = yaml; - resolve('Writing serialized YAML to options.dest successful.'); - } - }); + if (typeof options.dest === 'string') { // file + return await writeToFile(yaml, options.dest, TYPE_YAML, options.force); + } else if (isStream.writable(options.dest)) { // stream + return await writeToStream(yaml, options.dest, TYPE_YAML); + } + // object + options.dest = yaml; + return 'Writing serialized YAML to options.dest successful.'; } /** * Writes a JS object to a JSON destination. * - * @param {Object} object - The JS object to write into JSON destination. - * @param {Options} options - The write destination and indention. + * @param {Object} object - The JS object to write into JSON destination. + * @param {module:jy-transform:type-definitions~WriterOptions} options - The write destination and indention. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). * @private - * @example - * var Writer = require('jy-transform').Writer; - * var logger = ...; - * var writer = new Writer(logger); - * - * // ---- write obj to file - * - * var obj = {...}; - * var options = { - * dest: 'result.json', - * indent: 2 - * } - * - * writer.writeJson(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * - * // ---- write obj to Writable - * - * options = { - * dest: fs.createWriteStream('result.json'), - * indent: 4 - * } - * - * writer.writeJson(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); - * - * // ---- write obj to object - * - * options = { - * dest: {}, - * indent: 4 - * } - * - * writer.writeJson(obj, options) - * .then(function (msg){ - * logger.info(msg); - * }) - * .catch(function (err) { - * logger.error(err.stack); - * }); */ async function writeJson(object, options) { - return new Promise((resolve, reject) => { - if (typeof options.dest === 'string') { // file - writeToFile(serializeJsToJsonString(object, options.indent), options.dest, TYPE_JSON, - resolve, reject, options.force); - } else if (isStream.writable(options.dest)) { // stream - writeToStream(serializeJsToJsonString(object, options.indent), options.dest, - TYPE_JSON, resolve, reject); - } else { // object - options.dest = serializeJsToJsonString(object, options.indent); - resolve('Writing JSON to options.dest successful.'); - } - }); + const jsonString = await serializeJsToJsonString(object, options.indent); + if (typeof options.dest === 'string') { // file + return await writeToFile(jsonString, options.dest, TYPE_JSON, options.force); + } else if (isStream.writable(options.dest)) { // stream + return await writeToStream(jsonString, options.dest, TYPE_JSON); + } + // object + options.dest = jsonString; + return 'Writing JSON to options.dest successful.'; } /** * Writes a JS object to a JS destination. The object is prefixed by `module.exports[.${options.exports}] = `. * - * @param {Object} object - The JSON to write into JS destination. - * @param {Options} options - The write destination and indention. + * @param {Object} object - The JSON to write into JS destination. + * @param {module:jy-transform:type-definitions~WriterOptions} options - The write destination and indention. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} @@ -343,38 +247,29 @@ async function writeJson(object, options) { * @private */ async function writeJs(object, options) { - return new Promise((resolve, reject) => { - if (typeof options.dest === 'string') { // file - serializeJsToString(object, options.indent, options.exports) - .then((data) => { - writeToFile(data, options.dest, TYPE_JS, resolve, reject, options.force); - }) - .catch(reject); - } else if (isStream.writable(options.dest)) { // stream - serializeJsToString(object, options.indent, options.exports) - .then((data) => { - writeToStream(data, options.dest, TYPE_JS, resolve, reject); - }) - .catch(reject); - } else { // object - let msg; - if (options.exports) { - options.dest[options.exports] = object; - msg = 'Writing JS to options.dest.' + options.exports + ' successful.'; - } else { - Object.assign(options.dest, object); - msg = 'Writing JS to options.dest successful.'; - } - resolve(msg); - } - }); + const data = await serializeJsToString(object, options.indent, options.exports); + if (typeof options.dest === 'string') { // file + return await writeToFile(data, options.dest, TYPE_JS, options.force); + } else if (isStream.writable(options.dest)) { // stream + return await writeToStream(data, options.dest, TYPE_JS); + } + // object + let msg; + if (options.exports) { + options.dest[options.exports] = object; + msg = 'Writing JS to options.dest.' + options.exports + ' successful.'; + } else { + Object.assign(options.dest, object); + msg = 'Writing JS to options.dest successful.'; + } + return msg; } /** - * TODO: doc me. + * Writes the passe JS object to a particular destination described by the passed `options`. * - * @param {Object} object - The JS source object to write. - * @param {module:type-definitions~WriterOptions} options - The write options. + * @param {Object} object - The JS source object to write. + * @param {module:jy-transform:type-definitions~WriterOptions} options - The write options. * @returns {Promise.} Resolves with write success message. * @public * @example @@ -383,11 +278,11 @@ async function writeJs(object, options) { * * // ---- write obj to file --- * - * var obj = {...}; - * var options = { - * dest: 'result.js', - * indent: 4 - * } + * const obj = {...}; + * const options = { + * dest: 'result.js', + * indent: 4 + * } * * write(obj, options) * .then(console.log) diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js index ab43113..79667ab 100644 --- a/test/unit/test-writer.js +++ b/test/unit/test-writer.js @@ -143,9 +143,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { expect(msg).toBeDefined(); await expectDestFileExists(file); // eslint-disable-next-line import/no-unresolved, global-require - const json = require('../tmp/writer/test-data-by-js-stream-with-exports-identifier.js').test; - expect(json.test).toBeDefined(); - expect(json.test).toBe('value'); + const object = require('../tmp/writer/test-data-by-js-stream-with-exports-identifier.js').test; + expect(object.test).toBeDefined(); + expect(object.test).toBe('value'); }); it('should fail writing JS to file by invalid exports identifier (\'#3/-\')', () => { From 6c204ada72ecfcc8565ae2d08f3b782fe1620dad Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 30 Jun 2017 16:26:34 +0200 Subject: [PATCH 13/58] Run tests in parallel --- package.json | 2 +- src/cli.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1a2ebf3..c1f9206 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "readme": "bin/create-readme.sh -a true -c false -m true -l false", "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", "pretest": "mkdir -pv test/tmp", - "test": "JYT_DEBUG=true JYT_ERROR=true jest --expand --no-cache --coverage --runInBand --config=./.jestrc.js", + "test": "JYT_DEBUG=true JYT_ERROR=true jest --expand --no-cache --coverage --config=./.jestrc.js", "eslint": "eslint ." }, "engines": { diff --git a/src/cli.js b/src/cli.js index 79348fc..6f6d9e2 100755 --- a/src/cli.js +++ b/src/cli.js @@ -103,8 +103,8 @@ function main(args, cliOptions) { // transform with options return transform(cliOptions) - .then(msg => cli.info(msg)) - .catch(err => error(err)); + .then(cli.info) + .catch(error); } /* From 8729606eced2066e239270ed05084fee94777041 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 30 Jun 2017 16:49:15 +0200 Subject: [PATCH 14/58] Adjust some code to make it compatible with node versions < v6 --- API-PRIVATE.md | 8 ++++---- README.md | 6 +++--- src/validation/options-schema-helper.js | 17 +++++++---------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/API-PRIVATE.md b/API-PRIVATE.md index 301b40d..8ec17ef 100644 --- a/API-PRIVATE.md +++ b/API-PRIVATE.md @@ -671,7 +671,7 @@ values for origin and target depending on the `options.src` or `options.dest` va ### jy-transform:validation:options-schema-helper~inferOriginDefault ⇒ string -TODO describe me. +Infers the _origin_ type value from current validation context. **Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) **Returns**: string - The target type. @@ -679,12 +679,12 @@ TODO describe me. | Param | Type | Description | | --- | --- | --- | -| context | Object | TODO describe me. | +| context | Object | The validation context. | ### jy-transform:validation:options-schema-helper~inferTargetDefault ⇒ string -TODO describe me. +Infers the _target_ type value from current validation context. **Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) **Returns**: string - The target type. @@ -692,7 +692,7 @@ TODO describe me. | Param | Type | Description | | --- | --- | --- | -| context | Object | TODO describe me. | +| context | Object | The validation context. | diff --git a/README.md b/README.md index 31cfefd..6d79cfd 100644 --- a/README.md +++ b/README.md @@ -594,17 +594,17 @@ given (initially empty) JS object. ```javascript const key1 = async (data) => { - objectPath.set(data, 'key1', 'value1'); + odata.key1 = 'value1'; return data; }; const key2 = async (data) => { - objectPath.set(data, 'key2', 'value2'); + data.key2 = 'value2'; return data; }; const key3 = async (data) => { - objectPath.set(data, 'key3', 'value3'); + data.key3 = 'value3'; return data; }; ``` diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-helper.js index cad783f..e3271d4 100644 --- a/src/validation/options-schema-helper.js +++ b/src/validation/options-schema-helper.js @@ -26,26 +26,23 @@ import { */ const getTypeFromFilePath = (pathStr, defaultValue) => { let type; - try { + if (typeof pathStr === 'string') { // this is needed since node.js versions < v6 do not support throwing a TypeError! let ext = path.extname(pathStr); if (ext.charAt(0) === '.') { ext = ext.substr(1); } type = TYPE_MAP[ext]; - if (!type) { - type = defaultValue; - } - } catch (err) { - logger.error(err.stack); + } + if (!type) { type = defaultValue; } return type; }; /** - * TODO describe me. + * Infers the _origin_ type value from current validation context. * - * @param {Object} context - TODO describe me. + * @param {Object} context - The validation context. * @returns {string} The target type. * @protected */ @@ -62,9 +59,9 @@ export const inferOriginDefault = (context) => { }; /** - * TODO describe me. + * Infers the _target_ type value from current validation context. * - * @param {Object} context - TODO describe me. + * @param {Object} context - The validation context. * @returns {string} The target type. * @protected */ From 1b1a271ecbb9cdd24d74f82288199bddf230f6dc Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Sun, 9 Jul 2017 09:29:29 +0200 Subject: [PATCH 15/58] Further refactoring and fixes --- .babelrc | 6 - .eslintrc.js | 54 +- .jestrc.js | 7 +- .jsdoc-public.json | 26 + .travis.yml | 3 +- API-PRIVATE.md | 637 ++++---- API-PUBLIC.md | 237 +-- CHANGELOG.md | 136 +- PACKAGE.md | 19 +- README.md | 92 +- bin/create-readme.sh | 47 +- codecov.yml | 21 +- jyt | 5 +- package.json | 75 +- readme/API.md | 1371 ----------------- readme/BADGES.md | 2 +- readme/DOCUMENTATION.md | 42 +- src/cli.js | 55 +- src/constants.js | 48 +- src/reader.js | 43 +- src/transformer.js | 105 +- src/type-definitions.js | 26 +- src/validation/options-schema-helper.js | 5 +- src/validation/options-schema.js | 27 +- src/writer.js | 50 +- test/logger.js | 23 - test/unit/test-cli.js | 104 ++ test/unit/test-reader.js | 115 +- test/unit/test-transformer.js | 43 +- test/unit/test-writer.js | 2 +- .../test-joi-extensions-file-helper.js | 8 +- .../test-joi-extensions-identifier-helper.js | 6 +- .../validation/test-options-schema-helper.js | 4 +- test/unit/validation/test-options-schema.js | 148 +- 34 files changed, 1241 insertions(+), 2351 deletions(-) create mode 100644 .jsdoc-public.json delete mode 100644 readme/API.md create mode 100644 test/unit/test-cli.js diff --git a/.babelrc b/.babelrc index 0ab1c57..aec1179 100644 --- a/.babelrc +++ b/.babelrc @@ -1,15 +1,9 @@ { "presets": [ - "stage-0", ["env", { "targets": { "node": "current" } }] - ], - "plugins": [ - ["babel-plugin-transform-builtin-extend", { - "globals": ["Object", "Error", "Array"] - }] ] } diff --git a/.eslintrc.js b/.eslintrc.js index 3200edc..8a79d29 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,6 +5,7 @@ module.exports = { ecmaVersion: 8, }, plugins: [ + 'import', 'jsdoc', 'filenames', 'jest', @@ -14,6 +15,7 @@ module.exports = { 'jest/globals': true }, rules: { + 'arrow-body-style': 'off', 'comma-dangle': [ 'error', { arrays: 'only-multiline', @@ -22,23 +24,37 @@ module.exports = { exports: 'only-multiline', functions: 'ignore', }], - 'prefer-template': 'off', 'consistent-return': 'error', - 'no-case-declarations': 'error', - 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], - 'arrow-body-style': 'off', - 'import/no-commonjs': 'error', + 'filenames/match-regex': ['error', '^[a-z0-9-]+$'], 'import/no-amd': 'error', + 'import/no-commonjs': 'error', 'import/prefer-default-export': 'off', - strict: ['error', 'never'], + 'jest/no-disabled-tests': 'warn', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/valid-expect': 'error', + 'jest-async/expect-return': 'error', + 'jsdoc/check-param-names': 'error', + 'jsdoc/check-tag-names': 'error', + 'jsdoc/check-types': 'error', + 'jsdoc/newline-after-description': 'error', + 'jsdoc/require-description-complete-sentence': 'error', + 'jsdoc/require-hyphen-before-param-description': 'error', + 'jsdoc/require-param': 'error', + 'jsdoc/require-param-description': 'error', + 'jsdoc/require-param-type': 'error', + 'jsdoc/require-returns-description': 'error', + 'jsdoc/require-returns-type': 'error', 'max-len': [ 'error', 120, 4, { - code: 120, - tabWidth: 4, ignoreUrls: true, ignoreRegExpLiterals: true, - }], + }, + ], + 'no-case-declarations': 'error', 'no-param-reassign': ['error', { props: false }], + 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], + 'prefer-template': 'off', 'require-jsdoc': [ 'error', { require: { @@ -47,17 +63,13 @@ module.exports = { ClassDeclaration: true, } }], - 'jsdoc/check-param-names': 'error', - 'jsdoc/check-tag-names': 'error', - 'jsdoc/check-types': 'error', - 'jsdoc/newline-after-description': 'error', - 'jsdoc/require-description-complete-sentence': 'error', - 'jsdoc/require-hyphen-before-param-description': 'error', - 'jsdoc/require-param': 'error', - 'jsdoc/require-param-description': 'error', - 'jsdoc/require-param-type': 'error', - 'jsdoc/require-returns-description': 'error', - 'jsdoc/require-returns-type': 'error', - 'filenames/match-regex': ['error', '^[a-z0-9-]+$'], + strict: ['error', 'never'], + }, + settings: { + jsdoc: { + additionalTagNames: { + customTags: ['resolve', 'reject'] + }, + } } }; diff --git a/.jestrc.js b/.jestrc.js index 054736f..c4e1b71 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -2,7 +2,7 @@ module.exports = { // this is a workaround that jest does not create a jest_0/ folder in the project root dir! cacheDirectory: '/tmp/jest-cache', collectCoverageFrom: [ - //'lib/**/*.js', + '!lib/**/*.js', 'src/**/*.js', '!src/cli.js', // TODO: maybe later! '!src/debug-log.js', // TODO: maybe later! @@ -20,12 +20,13 @@ module.exports = { }, mapCoverage: true, testMatch: [ + // '!**/test/unit/test-cli.js', // '**/test/unit/validation/test-joi-extensions-file-helper.js', // '**/test/unit/validation/test-joi-extensions-identifier-helper.js', - //'**/test/unit/test-transformer.js', + // '**/test/unit/test-transformer.js', // '**/test/unit/test-index.js', //'**/test/unit/test-reader.js', - // '**/test/unit/test-writer.js', + '**/test/unit/test-writer.js', //'**/test/unit/validation/test-options-schema.js', '**/test/unit/**/*.js', //'**/test/unit/validation/test-options-schema-helper.js', diff --git a/.jsdoc-public.json b/.jsdoc-public.json new file mode 100644 index 0000000..86923ee --- /dev/null +++ b/.jsdoc-public.json @@ -0,0 +1,26 @@ +{ + "source": { + "include": [ + "./" + ], + "exclude": [ + "test", + "lib", + "node_modules" + ], + "includePattern": ".+\\.js$", + "excludePattern": "(^|\\/|\\\\)(_|\\.)" + }, + "opts": { + "encoding": "utf8", + "recurse": true + }, + "plugins": [ + "node_modules/jsdoc-babel" + ], + "babel": { + "plugins": [ + "transform-async-to-generator" + ] + } +} diff --git a/.travis.yml b/.travis.yml index a3a77f7..53479f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,5 +18,4 @@ after_success: branches: only: # whitelist - master - - /^(bugfix|feature|refactor)\/#\d+.*$/ - - /^hotfix\/.*$/ + - /^(bugfix|feature|refactor)\/(#\d)?.*$/ diff --git a/API-PRIVATE.md b/API-PRIVATE.md index 8ec17ef..a548c4d 100644 --- a/API-PRIVATE.md +++ b/API-PRIVATE.md @@ -4,20 +4,22 @@ - [Modules](#modules) - [Members](#members) +- [Typedefs](#typedefs) +- [External](#external) - [jy-transform:jyt ℗](#jy-transformjyt-%E2%84%97) -- [jy-transform:constants ℗](#jy-transformconstants-%E2%84%97) +- [jy-transform:constants](#jy-transformconstants) - [jy-transform:debug-log ℗](#jy-transformdebug-log-%E2%84%97) -- [jy-transform:reader](#jy-transformreader) -- [jy-transform:transformer](#jy-transformtransformer) -- [jy-transform:type-definitions](#jy-transformtype-definitions) +- [jy-transform:reader ℗](#jy-transformreader-%E2%84%97) +- [jy-transform:transformer ℗](#jy-transformtransformer-%E2%84%97) - [jy-transform:validation:joi-extensions-file-helper ℗](#jy-transformvalidationjoi-extensions-file-helper-%E2%84%97) - [jy-transform:validation:joi-extensions-identifier-helper ℗](#jy-transformvalidationjoi-extensions-identifier-helper-%E2%84%97) - [jy-transform:validation:joi-extension ℗](#jy-transformvalidationjoi-extension-%E2%84%97) - [jy-transform:validation:options-schema-helper : Object ℗](#jy-transformvalidationoptions-schema-helper--codeobjectcode-%E2%84%97) - [jy-transform:validation:options-schema : Object ℗](#jy-transformvalidationoptions-schema--codeobjectcode-%E2%84%97) -- [jy-transform:writer](#jy-transformwriter) +- [jy-transform:writer ℗](#jy-transformwriter-%E2%84%97) - [jy-transform:unit:helper-constants : Object ℗](#jy-transformunithelper-constants--codeobjectcode-%E2%84%97) - [jy-transform:unit:logger : Object ℗](#jy-transformunitlogger--codeobjectcode-%E2%84%97) +- [jy-transform:unit-test:test-cli ℗](#jy-transformunit-testtest-cli-%E2%84%97) - [jy-transform:test-unit:index ℗](#jy-transformtest-unitindex-%E2%84%97) - [jy-transform:unit-test:test-reader ℗](#jy-transformunit-testtest-reader-%E2%84%97) - [jy-transform:unit-test:test-transformer ℗](#jy-transformunit-testtest-transformer-%E2%84%97) @@ -28,8 +30,8 @@ - [jy-transform:unit-test:test-options-schema ℗](#jy-transformunit-testtest-options-schema-%E2%84%97) - [readJs ⇒ Promise.<Object> ℗](#readjs-%E2%87%92-codepromiseltobjectgtcode-%E2%84%97) - [readYaml ⇒ Promise.<Object> ℗](#readyaml-%E2%87%92-codepromiseltobjectgtcode-%E2%84%97) -- [read ⇒ Promise.<string>](#read-%E2%87%92-codepromiseltstringgtcode) -- [transform ⇒ Promise.<String>](#transform-%E2%87%92-codepromiseltstringgtcode) +- [read ⇒ Promise](#read-%E2%87%92-codepromisecode) +- [transform ⇒ Promise](#transform-%E2%87%92-codepromisecode) - [createExportsString ⇒ Promise.<string> ℗](#createexportsstring-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [serializeJsToString ⇒ Promise.<string> ℗](#serializejstostring-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [serializeJsToJsonString ⇒ string ℗](#serializejstojsonstring-%E2%87%92-codestringcode-%E2%84%97) @@ -37,7 +39,11 @@ - [writeYaml ⇒ Promise.<string> ℗](#writeyaml-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [writeJson ⇒ Promise.<string> ℗](#writejson-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [writeJs ⇒ Promise.<string> ℗](#writejs-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) -- [write ⇒ Promise.<string>](#write-%E2%87%92-codepromiseltstringgtcode) +- [write ⇒ Promise](#write-%E2%87%92-codepromisecode) +- [ReaderOptions : object](#readeroptions--codeobjectcode) +- [WriterOptions : object](#writeroptions--codeobjectcode) +- [TransformerOptions : object](#transformeroptions--codeobjectcode) +- [joi ℗](#joi-%E2%84%97) @@ -47,7 +53,7 @@

    jy-transform:jyt

    The command line interface.

    -
    jy-transform:constants
    +
    jy-transform:constants

    Useful constants used for the module and its usage.

    jy-transform:debug-log
    @@ -57,15 +63,12 @@
  • JYT_DEBUG: (only ERROR logging via console.error)
  • -
    jy-transform:reader
    +
    jy-transform:reader

    This module provides the read functionality for YAML, JS or JSON sources.

    -
    jy-transform:transformer
    +
    jy-transform:transformer

    This module provides the transform functionality for YAML, JS or JSON source to destination mapping.

    -
    jy-transform:type-definitions
    -

    The type definitions for this module.

    -
    jy-transform:validation:joi-extensions-file-helper

    An (extended) Joi validation schema helper functions for the module options on FS validation.

    @@ -82,7 +85,7 @@ values for origin and target depending on the options.src or
    jy-transform:validation:options-schema : Object

    The module options schema used in module:options-validator.

    -
    jy-transform:writer
    +
    jy-transform:writer

    This module provides the write functionality to write JS objects from memory to a JSON/JS/YAML destination (file, object or stream.Readable).

    @@ -92,17 +95,20 @@ destination (file, object or stream.Readable).

    jy-transform:unit:logger : Object

    The test suite logger.

    +
    jy-transform:unit-test:test-cli
    +

    This unit test suite checks the correct transformation behaviour of the CLI interface.

    +
    jy-transform:test-unit:index

    This unit test module tests the correct exporting from ./index.js.

    jy-transform:unit-test:test-reader
    -

    This unit test suite checks the validity and correctness of ./src/reader.js module.

    +

    This unit test suite checks the validity and correctness of the Reader module.

    jy-transform:unit-test:test-transformer
    -

    This unit test suite checks the correct transformation behaviour of Transformer class.

    +

    This unit test suite checks the correct transformation behaviour of the Transformer module.

    jy-transform:unit-test:test-writer
    -

    This unit test suite checks the validity and correctness of ./src/js module.

    +

    This unit test suite checks the validity and correctness of Writer module.

    jy-transform:test-unit:test-joi-extension-file-helper

    This unit test module tests validation FS helper method.

    @@ -129,12 +135,12 @@ destination (file, object or stream.Readable).

    NOTE: this function does not understand multi-document sources, it throws exception on those.

    -
    readPromise.<string>
    +
    readPromise

    Reads a particular content type from a source provided in the passed options.

    -
    transformPromise.<String>
    +
    transformPromise

    The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic:

    +an (optional) middleware function. It executes the transformation logic.

    1. Input (read)
    2. Transform [ + Middleware]
    3. @@ -162,17 +168,39 @@ an (optional) middleware function. It executes the transformation logic:

      writeJsPromise.<string>

      Writes a JS object to a JS destination. The object is prefixed by module.exports[.${options.exports}] =.

      -
      writePromise.<string>
      +
      writePromise

      Writes the passe JS object to a particular destination described by the passed options.

      +## Typedefs + +
      +
      ReaderOptions : object
      +

      The configuration properties provided to the read function.

      +
      +
      WriterOptions : object
      +

      The writer configuration properties provided to the write function.

      +
      +
      TransformerOptions : object
      +

      The configuration properties provided to the transform function.

      +
      +
      + +## External + +
      +
      joi
      +

      Hapi.js Joi.

      +
      +
      + ## jy-transform:jyt ℗ The command line interface. -**Access:** private +**Access**: private * [jy-transform:jyt](#module_jy-transform_jyt) ℗ * [~usage](#module_jy-transform_jyt..usage) : string ℗ @@ -186,15 +214,15 @@ The command line interface. ### jy-transform:jyt~usage : string ℗ How to use the CLI. -**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private +**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) +**Access**: private ### jy-transform:jyt~packagePath : string ℗ The path to package.json. -**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private +**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) +**Access**: private ### jy-transform:jyt~options : Object ℗ @@ -203,19 +231,19 @@ The options description for parsing the command line input, must be an object wi long_tag: [short_tag, description, value_type, default_value]; ``` -**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private +**Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) +**Access**: private ### jy-transform:jyt~error(err) ℗ -Prints the error to console and exit with 1. +Prints the error to console and exits process with 1. -**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private +**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) +**Access**: private | Param | Type | Description | | --- | --- | --- | -| err | string | Error | The error to print. | +| err | string \| Error | The error to print. | @@ -224,22 +252,22 @@ The main entry callback. When calling `cli.main()` this receives the `options` given on CLI, then does the transformation with these options and finally, it prints the result to the CLI. -**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) -**Access:** private +**Kind**: inner method of [jy-transform:jyt](#module_jy-transform_jyt) +**Access**: private | Param | Type | Description | | --- | --- | --- | | args | Array | The first mandatory argument is the input file (`args[0]`), the second (optional) argument is the output file (`args[1]`). | -| cliOptions | module:jy-transform:type-definitions~Options | The options provided via CLI. | +| cliOptions | module:jy-transform:type-definitions~TransformerOptions | The options provided via CLI. | -## jy-transform:constants ℗ +## jy-transform:constants Useful constants used for the module and its usage. -**Access:** private +**Access**: public -* [jy-transform:constants](#module_jy-transform_constants) ℗ +* [jy-transform:constants](#module_jy-transform_constants) * [~DEFAULT_OPTIONS](#module_jy-transform_constants..DEFAULT_OPTIONS) : object ℗ * [~UTF8](#module_jy-transform_constants..UTF8) : string ℗ * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string @@ -263,8 +291,8 @@ Useful constants used for the module and its usage. ### jy-transform:constants~DEFAULT_OPTIONS : object ℗ The default options. -**Kind**: inner namespace of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner namespace of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private **See** - [ORIGIN_DESCRIPTION](ORIGIN_DESCRIPTION) @@ -288,106 +316,106 @@ The default options. ### jy-transform:constants~UTF8 : string ℗ The 'utf8' constant. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~TYPE_YAML : string -The 'yaml' type constant. +The `'yaml'` type constant. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: public ### jy-transform:constants~TYPE_JSON : string -The 'json' type constant. +The `'json'` type constant. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: public ### jy-transform:constants~TYPE_JS : string -The 'js' type constant. +The `'js'` type constant. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** public +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: public ### jy-transform:constants~TYPE_MAP : Object ℗ A map for extensions to type. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~DEFAULT_INDENT : number ℗ The default file indention (4 SPACEs). -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~MIN_INDENT : number ℗ The minimum file indention (0 SPACE). -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~MAX_INDENT : number ℗ The maximum file indention (8 SPACEs). -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~DEFAULT_ORIGIN : string ℗ The default `origin` value: 'yaml'. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~DEFAULT_TARGET : string ℗ The default `origin` value: 'js'. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~DEFAULT_FORCE_FILE_OVERWRITE : boolean ℗ Whether to overwrite existing file or object on output. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~ORIGIN_DESCRIPTION : string ℗ The `origin` description value. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~TARGET_DESCRIPTION : string ℗ The `target` description value. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~DEST_DESCRIPTION : string ℗ The `dest` description value. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ### jy-transform:constants~DEFAULT_JS_IMPORTS_IDENTIFIER : string ℗ The `src` exports identifier value to read. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private **Example** ```js module.exports.foo = {...}; // here 'foo' is the identifier for an object to read from the source! @@ -397,8 +425,8 @@ module.exports.foo = {...}; // here 'foo' is the identifier for an object to rea ### jy-transform:constants~DEFAULT_JS_EXPORTS_IDENTIFIER : string ℗ The `dest` exports identifier value to write. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) -**Access:** private +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private ## jy-transform:debug-log ℗ @@ -406,7 +434,7 @@ The debug logger. Can be enabled via environment variables (set to `true`): - `JYT_DEBUG`: (only DEBUG logging via `console.log`) - `JYT_DEBUG`: (only ERROR logging via `console.error`) -**Access:** private +**Access**: private * [jy-transform:debug-log](#module_jy-transform_debug-log) ℗ * [~debug](#module_jy-transform_debug-log..debug) @@ -417,23 +445,23 @@ The debug logger. Can be enabled via environment variables (set to `true`): ### jy-transform:debug-log~debug DEBUG function. -**Kind**: inner constant of [jy-transform:debug-log](#module_jy-transform_debug-log) -**Access:** protected +**Kind**: inner constant of [jy-transform:debug-log](#module_jy-transform_debug-log) +**Access**: protected ### jy-transform:debug-log~error DEBUG function. -**Kind**: inner constant of [jy-transform:debug-log](#module_jy-transform_debug-log) -**Access:** protected +**Kind**: inner constant of [jy-transform:debug-log](#module_jy-transform_debug-log) +**Access**: protected -## jy-transform:reader +## jy-transform:reader ℗ This module provides the _read_ functionality for YAML, JS or JSON sources. -**Access:** public +**Access**: private -* [jy-transform:reader](#module_jy-transform_reader) +* [jy-transform:reader](#module_jy-transform_reader) ℗ * [~fsPromisified](#module_jy-transform_reader..fsPromisified) ℗ * [~readFromStream(readable, origin)](#module_jy-transform_reader..readFromStream) ⇒ Promise.<Object> ℗ @@ -442,16 +470,16 @@ This module provides the _read_ functionality for YAML, JS or JSON sources. ### jy-transform:reader~fsPromisified ℗ Promisified `fs` module. -**Kind**: inner constant of [jy-transform:reader](#module_jy-transform_reader) -**Access:** private +**Kind**: inner constant of [jy-transform:reader](#module_jy-transform_reader) +**Access**: private ### jy-transform:reader~readFromStream(readable, origin) ⇒ Promise.<Object> ℗ Reads from a passed stream and resolves by callback. -**Kind**: inner method of [jy-transform:reader](#module_jy-transform_reader) +**Kind**: inner method of [jy-transform:reader](#module_jy-transform_reader) **Returns**: Promise.<Object> - The read content as JS object representation. -**Access:** private +**Access**: private | Param | Type | Description | | --- | --- | --- | @@ -460,141 +488,24 @@ Reads from a passed stream and resolves by callback. -## jy-transform:transformer +## jy-transform:transformer ℗ This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. -**Access:** public - - -## jy-transform:type-definitions -The type definitions for this module. - -**Access:** public - -* [jy-transform:type-definitions](#module_jy-transform_type-definitions) - * [~ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) : object - * [~WriterOptions](#module_jy-transform_type-definitions..WriterOptions) : object - * [~TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) : object - * [~joi](#external_joi) ℗ - * [.ValidationError](#external_joi.ValidationError) ℗ - * [.Schema](#external_joi.Schema) ℗ - * [.Extension](#external_joi.Extension) ℗ - * [.validate](#external_joi.validate) : function ℗ - - - -### jy-transform:type-definitions~ReaderOptions : object -The configuration properties provided to the reader function. - -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public -**Properties** - -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| src | string | Stream.Readable | object | | The source (if `string` type is treated as a file path). | -| origin | string | "yaml" | The origin type. | -| imports | string | | The exports name for reading from JS source files or objects only. | - - - -### jy-transform:type-definitions~WriterOptions : object -The writer configuration properties provided to the writer function. - -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public -**Properties** - -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | -| target | string | "js" | The target type. | -| indent | number | 2 | The indention in files. | -| exports | string | | The exports name for usage in JS destination files only. | -| force | string | false | Force overwriting of existing output files on write phase. | - - - -### jy-transform:type-definitions~TransformerOptions : object -The configuration properties provided to the transformer function. - -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public -**Properties** - -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| src | string | Stream.Readable | object | | The source (if `string` type is treated as a file path). | -| origin | string | "yaml" | The origin type. | -| imports | string | | The exports name for reading from JS source files or objects only. | -| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | -| target | string | "js" | The target type. | -| exports | string | | The exports name for usage in JS destination files only. | -| indent | number | 2 | The indention in files. | -| force | string | false | Force overwriting of existing output files on write phase. | - - - -### jy-transform:type-definitions~joi ℗ -Hapi.js Joi. - -**Kind**: inner external of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** private -**See**: [Hapi Joi](https://github.com/hapijs/joi) - -* [~joi](#external_joi) ℗ - * [.ValidationError](#external_joi.ValidationError) ℗ - * [.Schema](#external_joi.Schema) ℗ - * [.Extension](#external_joi.Extension) ℗ - * [.validate](#external_joi.validate) : function ℗ - - - -#### joi.ValidationError ℗ -Joi validation error. - -**Kind**: static typedef of [joi](#external_joi) -**Access:** private -**See**: [Joi errors](hhttps://github.com/hapijs/joi/blob/v10.2.0/API.md#errors) - - -#### joi.Schema ℗ -The validation schema. Can be a [joi](#external_joi) type object or a plain object -where every key is assigned a [joi](#external_joi) type object. - -**Kind**: static typedef of [joi](#external_joi) -**Access:** private -**See**: [Joi API](https://github.com/hapijs/joi/blob/v10.2.2/API.md#joi) - - -#### joi.Extension ℗ -Hapi.js Joi schema extension. - -**Kind**: static typedef of [joi](#external_joi) -**Access:** private -**See**: [Hapi Joi Extension](https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension) - - -#### joi.validate : function ℗ -Joi `validate` method. - -**Kind**: static typedef of [joi](#external_joi) -**Access:** private -**See**: [Joi.validate](https://github.com/hapijs/joi/blob/master/API.md#validatevalue-schema-options-callback) +**Access**: private ## jy-transform:validation:joi-extensions-file-helper ℗ An (extended) Joi validation schema helper functions for the module options on FS validation. -**Access:** private +**Access**: private ### jy-transform:validation:joi-extensions-file-helper~isExistingFile(pathStr) ⇒ boolean Checks if given `pathStr` is an existing file after resolving `pathStr` relative to CWD. -**Kind**: inner method of [jy-transform:validation:joi-extensions-file-helper](#module_jy-transform_validation_joi-extensions-file-helper) +**Kind**: inner method of [jy-transform:validation:joi-extensions-file-helper](#module_jy-transform_validation_joi-extensions-file-helper) **Returns**: boolean - Value `true` if it is a file and exists, else `false`. -**Access:** protected +**Access**: protected | Param | Type | Description | | --- | --- | --- | @@ -605,7 +516,7 @@ Checks if given `pathStr` is an existing file after resolving `pathStr` relative ## jy-transform:validation:joi-extensions-identifier-helper ℗ An (extended) Joi validation schema helper function for the module options to validate ES6 identifiers. -**Access:** private +**Access**: private * [jy-transform:validation:joi-extensions-identifier-helper](#module_jy-transform_validation_joi-extensions-identifier-helper) ℗ * [~identifierRegExpECMAScript6](#module_jy-transform_validation_joi-extensions-identifier-helper..identifierRegExpECMAScript6) : RegExp ℗ @@ -617,16 +528,16 @@ An (extended) Joi validation schema helper function for the module options to va Created at [Generating a regular expression to match valid JavaScript identifiers] (https://mathiasbynens.be/demo/javascript-identifier-regex). -**Kind**: inner constant of [jy-transform:validation:joi-extensions-identifier-helper](#module_jy-transform_validation_joi-extensions-identifier-helper) -**Access:** private +**Kind**: inner constant of [jy-transform:validation:joi-extensions-identifier-helper](#module_jy-transform_validation_joi-extensions-identifier-helper) +**Access**: private ### jy-transform:validation:joi-extensions-identifier-helper~isValidEs6Identifier(identifier) ⇒ boolean This method checks if a given `identifier` is a valid ECMAScript 6 identifier. -**Kind**: inner method of [jy-transform:validation:joi-extensions-identifier-helper](#module_jy-transform_validation_joi-extensions-identifier-helper) +**Kind**: inner method of [jy-transform:validation:joi-extensions-identifier-helper](#module_jy-transform_validation_joi-extensions-identifier-helper) **Returns**: boolean - A `true` if valid, else `false`. -**Access:** protected +**Access**: protected | Param | Type | Description | | --- | --- | --- | @@ -643,24 +554,24 @@ console.log('valid = ' + isValidEs6Identifier(identifier)); ## jy-transform:validation:joi-extension ℗ The module exporting the [Extension](#external_joi.Extension)s for option validations. -**Access:** private +**Access**: private **See**: [Joi.extend(extension) method](https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension). -### jy-transform:validation:joi-extension~EXTENSIONS : [Extension](#external_joi.Extension) ℗ +### jy-transform:validation:joi-extension~EXTENSIONS : [Extension](#external_joi.Extension) ℗ The common [Schema](#external_joi.Schema) validation extensions: - `existingFile` - needs to be an absolute or relative path to an existing file. - `validEs6Identifier` - needs to be a valid ECMAScript 6 identifier. -**Kind**: inner constant of [jy-transform:validation:joi-extension](#module_jy-transform_validation_joi-extension) -**Access:** private +**Kind**: inner constant of [jy-transform:validation:joi-extension](#module_jy-transform_validation_joi-extension) +**Access**: private ## jy-transform:validation:options-schema-helper : Object ℗ Provides some helper functions used in [module:validation:options-schema](module:validation:options-schema) to resolve default values for origin and target depending on the `options.src` or `options.dest` value. -**Access:** private +**Access**: private **See**: [module:validation:options-schema](module:validation:options-schema) * [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) : Object ℗ @@ -673,9 +584,9 @@ values for origin and target depending on the `options.src` or `options.dest` va ### jy-transform:validation:options-schema-helper~inferOriginDefault ⇒ string Infers the _origin_ type value from current validation context. -**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) **Returns**: string - The target type. -**Access:** protected +**Access**: protected | Param | Type | Description | | --- | --- | --- | @@ -686,9 +597,9 @@ Infers the _origin_ type value from current validation context. ### jy-transform:validation:options-schema-helper~inferTargetDefault ⇒ string Infers the _target_ type value from current validation context. -**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) **Returns**: string - The target type. -**Access:** protected +**Access**: protected | Param | Type | Description | | --- | --- | --- | @@ -699,9 +610,9 @@ Infers the _target_ type value from current validation context. ### jy-transform:validation:options-schema-helper~getTypeFromFilePath(pathStr, defaultValue) ⇒ string ℗ Infer from path extension to a type using default value as fallback. -**Kind**: inner method of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Kind**: inner method of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) **Returns**: string - A type value. -**Access:** private +**Access**: private | Param | Type | Description | | --- | --- | --- | @@ -713,7 +624,7 @@ Infer from path extension to a type using default value as fallback. ## jy-transform:validation:options-schema : Object ℗ The module options schema used in [module:options-validator](module:options-validator). -**Access:** private +**Access**: private **See**: [module:options-validator](module:options-validator) * [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) : Object ℗ @@ -725,24 +636,24 @@ The module options schema used in [module:options-validator](module:options-vali ### jy-transform:validation:options-schema~readerOptionsSchema : JoiSchema ℗ The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [Reader](Reader) options. -**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) -**Access:** private +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access**: private ### jy-transform:validation:options-schema~writerOptionsSchema : JoiSchema ℗ The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [Writer](Writer) options. -**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) -**Access:** private +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access**: private -## jy-transform:writer +## jy-transform:writer ℗ This module provides the _write_ functionality to write JS objects from memory to a JSON/JS/YAML destination (file, object or [stream.Readable](stream.Readable)). -**Access:** public +**Access**: private -* [jy-transform:writer](#module_jy-transform_writer) +* [jy-transform:writer](#module_jy-transform_writer) ℗ * [~fsPromisified](#module_jy-transform_writer..fsPromisified) ℗ * [~writeToStream(object, dest, target)](#module_jy-transform_writer..writeToStream) ⇒ Promise.<string> ℗ @@ -751,20 +662,20 @@ destination (file, object or [stream.Readable](stream.Readable)). ### jy-transform:writer~fsPromisified ℗ Promisified `fs` module. -**Kind**: inner constant of [jy-transform:writer](#module_jy-transform_writer) -**Access:** private +**Kind**: inner constant of [jy-transform:writer](#module_jy-transform_writer) +**Access**: private ### jy-transform:writer~writeToStream(object, dest, target) ⇒ Promise.<string> ℗ Writes a string serialized data object to a stream. -**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) +**Kind**: inner method of [jy-transform:writer](#module_jy-transform_writer) **Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). **Throws**: - Error If serialized JS object could not be written due to any reason. -**Access:** private +**Access**: private **See** - [TYPE_YAML](TYPE_YAML) @@ -783,7 +694,7 @@ Writes a string serialized data object to a stream. ## jy-transform:unit:helper-constants : Object ℗ The test suite constants definitions. -**Access:** private +**Access**: private * [jy-transform:unit:helper-constants](#module_jy-transform_unit_helper-constants) : Object ℗ * [~TEST_SUITE_DESCRIPTION_UNIT](#module_jy-transform_unit_helper-constants..TEST_SUITE_DESCRIPTION_UNIT) : string @@ -794,21 +705,21 @@ The test suite constants definitions. ### jy-transform:unit:helper-constants~TEST_SUITE_DESCRIPTION_UNIT : string The unit test suite description for the plugin. -**Kind**: inner constant of [jy-transform:unit:helper-constants](#module_jy-transform_unit_helper-constants) -**Access:** public +**Kind**: inner constant of [jy-transform:unit:helper-constants](#module_jy-transform_unit_helper-constants) +**Access**: public ### jy-transform:unit:helper-constants~TEST_SUITE_DESCRIPTION_FUNCTIONAL : string The unit test suite description for the plugin. -**Kind**: inner constant of [jy-transform:unit:helper-constants](#module_jy-transform_unit_helper-constants) -**Access:** public +**Kind**: inner constant of [jy-transform:unit:helper-constants](#module_jy-transform_unit_helper-constants) +**Access**: public ## jy-transform:unit:logger : Object ℗ The test suite logger. -**Access:** private +**Access**: private * [jy-transform:unit:logger](#module_jy-transform_unit_logger) : Object ℗ * [~INDENT](#module_jy-transform_unit_logger..INDENT) : string ℗ @@ -825,28 +736,28 @@ The test suite logger. ### jy-transform:unit:logger~INDENT : string ℗ An indent of 0 SPACEs. -**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) -**Access:** private +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access**: private ### jy-transform:unit:logger~TEST_TMP_DIR : string ℗ A temporary test directory. -**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) -**Access:** private +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access**: private ### jy-transform:unit:logger~winstonFileOptions : Object ℗ Options for winston file logging. -**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) -**Access:** private +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access**: private #### winstonFileOptions.timestamp() ⇒ string Formats the timestamp as [Date](Date) ISO string prefixed by an indent. -**Kind**: static method of [winstonFileOptions](#module_jy-transform_unit_logger..winstonFileOptions) +**Kind**: static method of [winstonFileOptions](#module_jy-transform_unit_logger..winstonFileOptions) **Returns**: string - - The [Date](Date) ISO string. **See**: #INDENT @@ -854,14 +765,14 @@ Formats the timestamp as [Date](Date) ISO string prefixed by an indent. ### jy-transform:unit:logger~winstonConsoleOptions : Object ℗ Options for winston console logging. -**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) -**Access:** private +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access**: private #### winstonConsoleOptions.timestamp() ⇒ string Overwrites the timestamp by indent. -**Kind**: static method of [winstonConsoleOptions](#module_jy-transform_unit_logger..winstonConsoleOptions) +**Kind**: static method of [winstonConsoleOptions](#module_jy-transform_unit_logger..winstonConsoleOptions) **Returns**: string - - The indent only. **See**: #INDENT @@ -869,69 +780,75 @@ Overwrites the timestamp by indent. ### jy-transform:unit:logger~logger The winston logger. -**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) -**Access:** protected +**Kind**: inner constant of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Access**: protected ### jy-transform:unit:logger~formatter(options) ⇒ string ℗ This function formats the log string by given options to log. -**Kind**: inner method of [jy-transform:unit:logger](#module_jy-transform_unit_logger) +**Kind**: inner method of [jy-transform:unit:logger](#module_jy-transform_unit_logger) **Returns**: string - The log string. -**Access:** private +**Access**: private | Param | Type | Description | | --- | --- | --- | | options | Object | The formatter options. | + + +## jy-transform:unit-test:test-cli ℗ +This unit test suite checks the correct transformation behaviour of the CLI interface. + +**Access**: private ## jy-transform:test-unit:index ℗ This unit test module tests the correct exporting from _./index.js_. -**Access:** private +**Access**: private ## jy-transform:unit-test:test-reader ℗ -This unit test suite checks the validity and correctness of _./src/reader.js_ module. +This unit test suite checks the validity and correctness of the Reader module. -**Access:** private +**Access**: private ## jy-transform:unit-test:test-transformer ℗ -This unit test suite checks the correct transformation behaviour of [Transformer](Transformer) class. +This unit test suite checks the correct transformation behaviour of the Transformer module. -**Access:** private +**Access**: private ## jy-transform:unit-test:test-writer ℗ -This unit test suite checks the validity and correctness of _./src/js_ module. +This unit test suite checks the validity and correctness of Writer module. -**Access:** private +**Access**: private ## jy-transform:test-unit:test-joi-extension-file-helper ℗ This unit test module tests validation FS helper method. -**Access:** private +**Access**: private ## jy-transform:unit-test:test-joi-extensions-identifier-helper ℗ This unit test suite checks validity and correctness of ES6 identifiers. -**Access:** private +**Access**: private ## jy-transform:unit-test:test-options-schema-helper ℗ This unit test suite checks the validity and correctness of options schema helper methods. -**Access:** private +**Access**: private ## jy-transform:unit-test:test-options-schema ℗ This unit test suite checks the validity and correctness of options schema. -**Access:** private +**Access**: private * [jy-transform:unit-test:test-options-schema](#module_jy-transform_unit-test_test-options-schema) ℗ * [~expectOptionsValidationError(invalidOptions, schema)](#module_jy-transform_unit-test_test-options-schema..expectOptionsValidationError) ℗ @@ -942,12 +859,12 @@ This unit test suite checks the validity and correctness of options schema. ### jy-transform:unit-test:test-options-schema~expectOptionsValidationError(invalidOptions, schema) ℗ Expect a `ValidationError` for a given options function. -**Kind**: inner method of [jy-transform:unit-test:test-options-schema](#module_jy-transform_unit-test_test-options-schema) -**Access:** private +**Kind**: inner method of [jy-transform:unit-test:test-options-schema](#module_jy-transform_unit-test_test-options-schema) +**Access**: private | Param | Type | Description | | --- | --- | --- | -| invalidOptions | Options | The options which potentially produce the error. | +| invalidOptions | [ReaderOptions](#ReaderOptions) \| [WriterOptions](#WriterOptions) | The options which potentially produce the error. | | schema | Schema | The validation schema. | @@ -955,12 +872,12 @@ Expect a `ValidationError` for a given options function. ### jy-transform:unit-test:test-options-schema~expectOptionsValidationSuccess(validOptions, schema) ℗ Expect a validation success for a given options. -**Kind**: inner method of [jy-transform:unit-test:test-options-schema](#module_jy-transform_unit-test_test-options-schema) -**Access:** private +**Kind**: inner method of [jy-transform:unit-test:test-options-schema](#module_jy-transform_unit-test_test-options-schema) +**Access**: private | Param | Type | Description | | --- | --- | --- | -| validOptions | Options | The options which should be correct. | +| validOptions | [ReaderOptions](#ReaderOptions) \| [WriterOptions](#WriterOptions) | The options which should be correct. | | schema | Schema | The validation schema. | @@ -970,11 +887,11 @@ Reads the data from a given JS or JSON source. **Kind**: global variable **Returns**: Promise.<Object> - Contains the read JS object. -**Access:** private +**Access**: private | Param | Type | Description | | --- | --- | --- | -| options | [ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) | Contains the JS/JSON source reference to read from. | +| options | [ReaderOptions](#ReaderOptions) | Contains the JS/JSON source reference to read from. | @@ -985,24 +902,27 @@ exception on those. **Kind**: global variable **Returns**: Promise.<Object> - Contains the read JS object. -**Access:** private +**Access**: private | Param | Type | Description | | --- | --- | --- | -| options | [ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) | Contains the YAML source reference to read from. | +| options | [ReaderOptions](#ReaderOptions) | Contains the YAML source reference to read from. | -## read ⇒ Promise.<string> +## read ⇒ Promise Reads a particular content type from a source provided in the passed `options`. **Kind**: global variable -**Returns**: Promise.<string> - Resolves with JS object result. -**Access:** public +**Returns**: Promise - The result. +**Access**: public +**Resolve**: string Resolves with JS object result. +**Reject**: ValidationError If any `options` validation occurs. +**Reject**: Error If any write error occurs. | Param | Type | Description | | --- | --- | --- | -| options | [ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) | The read options. | +| options | [ReaderOptions](#ReaderOptions) | The read options. | **Example** ```js @@ -1032,25 +952,25 @@ read(options) ``` -## transform ⇒ Promise.<String> +## transform ⇒ Promise The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic: +an (optional) middleware function. It executes the transformation logic. + 1. Input (read) 2. Transform [ + Middleware] 3. Output (write). **Kind**: global variable -**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: - -- TypeError Will throw this error when the passed `middleware` is not type of `Function`. -- Error Will throw plain error when writing to _destination object_ failed due to any reason. - -**Access:** public +**Returns**: Promise - The result. +**Access**: public +**Resolve**: string With the transformation result as message (e.g. to be logged by caller). +**Reject**: TypeError Will throw this error when the passed `middleware` is not type of `Function`. +**Reject**: ValidationError If any `options` validation occurs. +**Reject**: Error Will throw any error if read, transform or write operation failed due to any reason. | Param | Type | Description | | --- | --- | --- | -| options | [TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) | The configuration for a transformation. | +| options | [TransformerOptions](#TransformerOptions) | The configuration for a transformation. | | [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` async function(object) ```

      **NOTE:** the Promise has to return the processed JS object. | **Example** @@ -1086,7 +1006,7 @@ Creates a potential named `'module.exports[.exportsTo]'` string. **Kind**: global variable **Returns**: Promise.<string> - Resolves with the exports string. -**Access:** private +**Access**: private | Param | Type | Description | | --- | --- | --- | @@ -1099,7 +1019,7 @@ Serialize a JS object to string. **Kind**: global variable **Returns**: Promise.<string> - - Promise resolve with the serialized JS content. -**Access:** private +**Access**: private **Todo** - [ ] [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> @@ -1119,7 +1039,7 @@ Serialize a JS object to JSON string. **Kind**: global variable **Returns**: string - The serialized JSON. -**Access:** private +**Access**: private | Param | Type | Description | | --- | --- | --- | @@ -1132,7 +1052,7 @@ Serialize a JS object to JSON string. Ensures that all dirs exists for file type `dest` and writes the JS object to file. **Kind**: global variable -**Access:** private +**Access**: private | Param | Type | Description | | --- | --- | --- | @@ -1152,7 +1072,7 @@ Writes a JS object to a YAML destination. - Error If YAML destination could not be written due to any reason. -**Access:** private +**Access**: private **See** - [MIN_INDENT](MIN_INDENT) @@ -1163,7 +1083,7 @@ Writes a JS object to a YAML destination. | Param | Type | Description | | --- | --- | --- | | object | Object | The JS object to write into YAML destination. | -| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write destination and indention. | +| options | [WriterOptions](#WriterOptions) | The write destination and indention. | @@ -1172,7 +1092,7 @@ Writes a JS object to a JSON destination. **Kind**: global variable **Returns**: Promise.<string> - Containing the write success message to handle by caller (e.g. for logging). -**Access:** private +**Access**: private **See** - [MIN_INDENT](MIN_INDENT) @@ -1183,7 +1103,7 @@ Writes a JS object to a JSON destination. | Param | Type | Description | | --- | --- | --- | | object | Object | The JS object to write into JSON destination. | -| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write destination and indention. | +| options | [WriterOptions](#WriterOptions) | The write destination and indention. | @@ -1192,7 +1112,7 @@ Writes a JS object to a JS destination. The object is prefixed by `module.export **Kind**: global variable **Returns**: Promise.<string> - - Containing the write success message to handle by caller (e.g. for logging). -**Access:** private +**Access**: private **See** - [MIN_INDENT](MIN_INDENT) @@ -1203,21 +1123,24 @@ Writes a JS object to a JS destination. The object is prefixed by `module.export | Param | Type | Description | | --- | --- | --- | | object | Object | The JSON to write into JS destination. | -| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write destination and indention. | +| options | [WriterOptions](#WriterOptions) | The write destination and indention. | -## write ⇒ Promise.<string> +## write ⇒ Promise Writes the passe JS object to a particular destination described by the passed `options`. **Kind**: global variable -**Returns**: Promise.<string> - Resolves with write success message. -**Access:** public +**Returns**: Promise - The result. +**Access**: public +**Resolve**: string With the write success message. +**Reject**: Error If any write error occurs. +**Reject**: ValidationError If any `options` validation occurs. | Param | Type | Description | | --- | --- | --- | | object | Object | The JS source object to write. | -| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write options. | +| options | [WriterOptions](#WriterOptions) | The write options. | **Example** ```js @@ -1260,3 +1183,103 @@ write(obj, options) .then(console.log) .catch(console.error); ``` + + +## ReaderOptions : object +The configuration properties provided to the `read` function. + +**Kind**: global typedef +**Access**: public +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| src | string \| Stream.Readable \| object | | The source (if `string` type is treated as a file path). | +| origin | string | "yaml" | The origin type. | +| imports | string | | The exports name for reading from JS source files or objects only. | + + + +## WriterOptions : object +The writer configuration properties provided to the `write` function. + +**Kind**: global typedef +**Access**: public +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| dest | string \| Stream.Writable \| object | | The destination (if `string` type is treated as a file path). | +| target | string | "js" | The target type. | +| indent | number | 2 | The indention value for pretty-print of output. | +| exports | string | | The exports name for usage in JS destination files only. | +| force | string | false | Force overwriting of existing output files on write phase. | + + + +## TransformerOptions : object +The configuration properties provided to the `transform` function. + +**Kind**: global typedef +**Access**: public +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| src | string \| Stream.Readable \| object | | The source (if `string` type is treated as a file path). | +| origin | string | "yaml" | The origin type. | +| imports | string | | The exports name for reading from JS source files or objects only. | +| dest | string \| Stream.Writable \| object | | The destination (if `string` type is treated as a file path). | +| target | string | "js" | The target type. | +| indent | number | 2 | The indention value for pretty-print of output. | +| exports | string | | The exports name for usage in JS destination files only. | +| force | string | false | Force overwriting of existing output files on write phase. | + + + +## joi ℗ +Hapi.js Joi. + +**Kind**: global external +**Access**: private +**See**: [Hapi Joi](https://github.com/hapijs/joi) + +* [joi](#external_joi) ℗ + * [.ValidationError](#external_joi.ValidationError) + * [.Schema](#external_joi.Schema) ℗ + * [.Extension](#external_joi.Extension) ℗ + * [.validate](#external_joi.validate) : function ℗ + + + +### joi.ValidationError +Joi validation error. + +**Kind**: static typedef of [joi](#external_joi) +**Access**: public +**See**: [Joi errors](hhttps://github.com/hapijs/joi/blob/v10.2.0/API.md#errors) + + +### joi.Schema ℗ +The validation schema. Can be a [joi](#external_joi) type object or a plain object +where every key is assigned a [joi](#external_joi) type object. + +**Kind**: static typedef of [joi](#external_joi) +**Access**: private +**See**: [Joi API](https://github.com/hapijs/joi/blob/v10.2.2/API.md#joi) + + +### joi.Extension ℗ +Hapi.js Joi schema extension. + +**Kind**: static typedef of [joi](#external_joi) +**Access**: private +**See**: [Hapi Joi Extension](https://github.com/hapijs/joi/blob/v10.2.2/API.md#extendextension) + + +### joi.validate : function ℗ +Joi `validate` method. + +**Kind**: static typedef of [joi](#external_joi) +**Access**: private +**See**: [Joi.validate](https://github.com/hapijs/joi/blob/master/API.md#validatevalue-schema-options-callback) diff --git a/API-PUBLIC.md b/API-PUBLIC.md index 3bfdcc4..65e5339 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -4,149 +4,107 @@ - [Modules](#modules) - [Members](#members) -- [jy-transform:reader](#jy-transformreader) -- [jy-transform:transformer](#jy-transformtransformer) -- [jy-transform:type-definitions](#jy-transformtype-definitions) -- [jy-transform:writer](#jy-transformwriter) -- [read ⇒ Promise.<string>](#read-%E2%87%92-codepromiseltstringgtcode) -- [transform ⇒ Promise.<String>](#transform-%E2%87%92-codepromiseltstringgtcode) -- [write ⇒ Promise.<string>](#write-%E2%87%92-codepromiseltstringgtcode) +- [Typedefs](#typedefs) +- [jy-transform:constants](#jy-transformconstants) +- [read ⇒ Promise](#read-%E2%87%92-codepromisecode) +- [transform ⇒ Promise](#transform-%E2%87%92-codepromisecode) +- [write ⇒ Promise](#write-%E2%87%92-codepromisecode) +- [ReaderOptions : object](#readeroptions--codeobjectcode) +- [WriterOptions : object](#writeroptions--codeobjectcode) +- [TransformerOptions : object](#transformeroptions--codeobjectcode) ## Modules

      -
      jy-transform:reader
      -

      This module provides the read functionality for YAML, JS or JSON sources.

      -
      -
      jy-transform:transformer
      -

      This module provides the transform functionality for YAML, JS or JSON source to destination mapping.

      -
      -
      jy-transform:type-definitions
      -

      The type definitions for this module.

      -
      -
      jy-transform:writer
      -

      This module provides the write functionality to write JS objects from memory to a JSON/JS/YAML -destination (file, object or stream.Readable).

      +
      jy-transform:constants
      +

      Useful constants used for the module and its usage.

      ## Members
      -
      readPromise.<string>
      +
      readPromise

      Reads a particular content type from a source provided in the passed options.

      -
      transformPromise.<String>
      +
      transformPromise

      The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic:

      +an (optional) middleware function. It executes the transformation logic.

      1. Input (read)
      2. Transform [ + Middleware]
      3. Output (write).
      -
      writePromise.<string>
      +
      writePromise

      Writes the passe JS object to a particular destination described by the passed options.

      - - -## jy-transform:reader -This module provides the _read_ functionality for YAML, JS or JSON sources. - -**Access:** public - - -## jy-transform:transformer -This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. - -**Access:** public - - -## jy-transform:type-definitions -The type definitions for this module. - -**Access:** public +## Typedefs -* [jy-transform:type-definitions](#module_jy-transform_type-definitions) - * [~ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) : object - * [~WriterOptions](#module_jy-transform_type-definitions..WriterOptions) : object - * [~TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) : object - - - -### jy-transform:type-definitions~ReaderOptions : object -The configuration properties provided to the reader function. - -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public -**Properties** +
      +
      ReaderOptions : object
      +

      The configuration properties provided to the read function.

      +
      +
      WriterOptions : object
      +

      The writer configuration properties provided to the write function.

      +
      +
      TransformerOptions : object
      +

      The configuration properties provided to the transform function.

      +
      +
      -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| src | string | Stream.Readable | object | | The source (if `string` type is treated as a file path). | -| origin | string | "yaml" | The origin type. | -| imports | string | | The exports name for reading from JS source files or objects only. | + - +## jy-transform:constants +Useful constants used for the module and its usage. -### jy-transform:type-definitions~WriterOptions : object -The writer configuration properties provided to the writer function. +**Access**: public -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public -**Properties** +* [jy-transform:constants](#module_jy-transform_constants) + * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string + * [~TYPE_JSON](#module_jy-transform_constants..TYPE_JSON) : string + * [~TYPE_JS](#module_jy-transform_constants..TYPE_JS) : string -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | -| target | string | "js" | The target type. | -| indent | number | 2 | The indention in files. | -| exports | string | | The exports name for usage in JS destination files only. | -| force | string | false | Force overwriting of existing output files on write phase. | + - +### jy-transform:constants~TYPE_YAML : string +The `'yaml'` type constant. -### jy-transform:type-definitions~TransformerOptions : object -The configuration properties provided to the transformer function. +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: public + -**Kind**: inner typedef of [jy-transform:type-definitions](#module_jy-transform_type-definitions) -**Access:** public -**Properties** +### jy-transform:constants~TYPE_JSON : string +The `'json'` type constant. -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| src | string | Stream.Readable | object | | The source (if `string` type is treated as a file path). | -| origin | string | "yaml" | The origin type. | -| imports | string | | The exports name for reading from JS source files or objects only. | -| dest | string | Stream.Writable | object | | The destination (if `string` type is treated as a file path). | -| target | string | "js" | The target type. | -| exports | string | | The exports name for usage in JS destination files only. | -| indent | number | 2 | The indention in files. | -| force | string | false | Force overwriting of existing output files on write phase. | +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: public + - +### jy-transform:constants~TYPE_JS : string +The `'js'` type constant. -## jy-transform:writer -This module provides the _write_ functionality to write JS objects from memory to a JSON/JS/YAML -destination (file, object or [stream.Readable](stream.Readable)). - -**Access:** public +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: public -## read ⇒ Promise.<string> +## read ⇒ Promise Reads a particular content type from a source provided in the passed `options`. **Kind**: global variable -**Returns**: Promise.<string> - Resolves with JS object result. -**Access:** public +**Returns**: Promise - The result. +**Access**: public +**Resolve**: string Resolves with JS object result. +**Reject**: ValidationError If any `options` validation occurs. +**Reject**: Error If any write error occurs. | Param | Type | Description | | --- | --- | --- | -| options | [ReaderOptions](#module_jy-transform_type-definitions..ReaderOptions) | The read options. | +| options | [ReaderOptions](#ReaderOptions) | The read options. | **Example** ```js @@ -176,25 +134,25 @@ read(options) ``` -## transform ⇒ Promise.<String> +## transform ⇒ Promise The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic: +an (optional) middleware function. It executes the transformation logic. + 1. Input (read) 2. Transform [ + Middleware] 3. Output (write). **Kind**: global variable -**Returns**: Promise.<String> - Containing the transformation result as message (e.g. to be logged by caller). -**Throws**: - -- TypeError Will throw this error when the passed `middleware` is not type of `Function`. -- Error Will throw plain error when writing to _destination object_ failed due to any reason. - -**Access:** public +**Returns**: Promise - The result. +**Access**: public +**Resolve**: string With the transformation result as message (e.g. to be logged by caller). +**Reject**: TypeError Will throw this error when the passed `middleware` is not type of `Function`. +**Reject**: ValidationError If any `options` validation occurs. +**Reject**: Error Will throw any error if read, transform or write operation failed due to any reason. | Param | Type | Description | | --- | --- | --- | -| options | [TransformerOptions](#module_jy-transform_type-definitions..TransformerOptions) | The configuration for a transformation. | +| options | [TransformerOptions](#TransformerOptions) | The configuration for a transformation. | | [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` async function(object) ```

      **NOTE:** the Promise has to return the processed JS object. | **Example** @@ -225,17 +183,20 @@ try { ``` -## write ⇒ Promise.<string> +## write ⇒ Promise Writes the passe JS object to a particular destination described by the passed `options`. **Kind**: global variable -**Returns**: Promise.<string> - Resolves with write success message. -**Access:** public +**Returns**: Promise - The result. +**Access**: public +**Resolve**: string With the write success message. +**Reject**: Error If any write error occurs. +**Reject**: ValidationError If any `options` validation occurs. | Param | Type | Description | | --- | --- | --- | | object | Object | The JS source object to write. | -| options | [WriterOptions](#module_jy-transform_type-definitions..WriterOptions) | The write options. | +| options | [WriterOptions](#WriterOptions) | The write options. | **Example** ```js @@ -278,3 +239,55 @@ write(obj, options) .then(console.log) .catch(console.error); ``` + + +## ReaderOptions : object +The configuration properties provided to the `read` function. + +**Kind**: global typedef +**Access**: public +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| src | string \| Stream.Readable \| object | | The source (if `string` type is treated as a file path). | +| origin | string | "yaml" | The origin type. | +| imports | string | | The exports name for reading from JS source files or objects only. | + + + +## WriterOptions : object +The writer configuration properties provided to the `write` function. + +**Kind**: global typedef +**Access**: public +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| dest | string \| Stream.Writable \| object | | The destination (if `string` type is treated as a file path). | +| target | string | "js" | The target type. | +| indent | number | 2 | The indention value for pretty-print of output. | +| exports | string | | The exports name for usage in JS destination files only. | +| force | string | false | Force overwriting of existing output files on write phase. | + + + +## TransformerOptions : object +The configuration properties provided to the `transform` function. + +**Kind**: global typedef +**Access**: public +**Properties** + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| src | string \| Stream.Readable \| object | | The source (if `string` type is treated as a file path). | +| origin | string | "yaml" | The origin type. | +| imports | string | | The exports name for reading from JS source files or objects only. | +| dest | string \| Stream.Writable \| object | | The destination (if `string` type is treated as a file path). | +| target | string | "js" | The target type. | +| indent | number | 2 | The indention value for pretty-print of output. | +| exports | string | | The exports name for usage in JS destination files only. | +| force | string | false | Force overwriting of existing output files on write phase. | + diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d03d41..6397f7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,76 +3,102 @@ #### v3.0.0 - New Features: - - The _middleware_ function must not need a Promise anymore, but is still recommended. + - The _middleware_ function must not need to be a Promise anymore, but it is still recommended. + - The `read` process returns a clone of the `options.src` when it is a JS object (so the origin + would never be changed). + - The minimum indent for YAML target types is validated for 2 now and throws a `ValidationError` + if < 2 (for others 0 is still valid). + +- Bugfix: + - If destination is not given on transformation process but the target is, then the destination file extension + is adapted to the proper type, e.g. `$ ./jyt inch.json -t yaml` results in a file _inch.yaml_ (formerly: + _inch.json_ with YAML content or _inch(1).json_ with YAML, the latter if `options.force` was `true`). - **CLI & API Changes (Backwards Incompatible!):** - - Removed support for Node.js < v4.0 - - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community + - Removed support for Node.js < v4.0. + - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community. + - An invalid indention setting (i.e. `indent` < 0 or `indent` > 8) raises a `ValidationError` now. - **API Changes Only (Backwards Incompatible!):** - - The exported constants `YAML`, `JS` and `JSON` (usable for `options.origin/target`) are renamed respectively to `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` - - Eased interface: - - Prototype approach removed from `Transformer`, `Reader` and `Writer`, turning it to internal modules with exports of _named_ functions to provide a simplified interface - - The formerly exported `Reader.readJs(...)/readYaml(...)` functions are not public anymore and replaced by a general `read(options)` function - - The formerly exported `Writer.writeJs(...)/writeJson(...)/writeYaml(...)` functions are not public anymore and replaced by a general `write(object, options)` function - - The formerly exported `middleware` is not publicly available anymore - - Reduced and named export of constants: `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` only - - The `options.imports/exports` are not allowed to be empty strings anymore (semantically senseless, just leave it out) - - The configuration property `options.dest` is required for `Transformer` and `Writer` when using the API - - Removal of `LogWrapper` (no more logger injection possible/needed) + - The exported constants `YAML`, `JS` and `JSON` (usable for `options.origin/target`) are renamed respectively + to `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON`. + - Provide a simplified interface: + - Prototype approach removed from `Transformer`, `Reader` and `Writer`, turning it to internal modules with + exports of _named_ functions: + - The formerly exported `Reader.readJs(...)/readYaml(...)` functions are not public anymore and replaced + by a general `read(options)` function. + - The formerly exported `Writer.writeJs(...)/writeJson(...)/writeYaml(...)` functions are not public + anymore and replaced by a general `write(object, options)` function. + - The formerly exported `middleware` (identity function) is not publicly available anymore. + - Reduced and named export of constants: `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` only. + - The `options.imports/exports` are not allowed to be empty strings anymore (semantically senseless, + just leave it out). + - The configuration property `options.dest` is required for `Writer` when using the API (but not from `Transformer` + if it can be inferred from `options.src` which is true for string or file stream sources). + - Removal of `LogWrapper` (no more logger injection possible). - Internal Changes & Improvements: - - Documentation restructured - - Removal of _development_ branch - - Usage of [babel](https://babeljs.io/) and therefore most modern language features - - Code base could be shrinked and readabilty was improved - - Usage of _native_ Promises instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html) - - Tests re-written in [Jest](https://facebook.github.io/jest) (could get rid of [assert](https://github.com/defunctzombie/commonjs-assert), - [mocha](https://mochajs.org/) and [istanbul](https://github.com/gotwarlost/istanbul)) - - Add travis build for Node.js v8.x - - Remove travis build for Node.js < v4.x - - Removal of `OptionsHandler` and `Validator` (replaced the validation by using [joi](https://github.com/hapijs/joi/tree/v10.5.0) now) + - Documentation restructured. + - Removal of _development_ branch. + - Usage of [babel](https://babeljs.io/) and therefore most modern language features. + - Update of dependencies and amount reduced. + - Code base could be shrinked and readabilty was improved. + - Usage of _native_ Promises instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html). + - Tests re-written in [Jest](https://facebook.github.io/jest) (could get rid of + [assert](https://github.com/defunctzombie/commonjs-assert), + [mocha](https://mochajs.org/) and [istanbul](https://github.com/gotwarlost/istanbul)). + - Add travis build for Node.js v8.x. + - Remove travis build for Node.js < v4.x. + - Removal of `OptionsHandler` and `Validator` (replaced the validation by using + [joi](https://github.com/hapijs/joi/tree/v10.5.0) now). #### v2.0.1 -- [[#39](https://github.com/deadratfink/jy-transform/issues/39)] Maintenance release - - Update dependencies to latest - - Add travis build for Node.js v7.x and v6.x - - Docs improved/corrected - - Add target pretest in `scripts` section to `rm` _./test/tmp_ folder +- [[#39](https://github.com/deadratfink/jy-transform/issues/39)] Maintenance release. + - Update dependencies to latest. + - Add travis build for Node.js v7.x and v6.x. + - Docs improved/corrected. + - Add target pretest in `scripts` section to `rm` _./test/tmp_ folder. #### v2.0.0 -- [[#33](https://github.com/deadratfink/jy-transform/issues/33)] Enhance `LogWrapper` with `TRACE` level (API) -- [[#32](https://github.com/deadratfink/jy-transform/issues/32)] Introduce input and output on CLI as ARGS instead of OPTIONS (non-backwards compatible change for CLI usage, _no_ impact on API level!) - - e.g. on CLI type in `$ jyt.js foo.js bar.yaml` instead of `$ jyt.js -s foo.js -d bar.yaml` -- [[#31](https://github.com/deadratfink/jy-transform/issues/31)] Bugfix: given `Object` source results in 'yaml' for origin (API) -- [Cleanup] Update dependencies +- [[#33](https://github.com/deadratfink/jy-transform/issues/33)] Enhance `LogWrapper` with `TRACE` level (API). +- [[#32](https://github.com/deadratfink/jy-transform/issues/32)] Introduce input and output on CLI as + ARGS instead of OPTIONS + (non-backwards compatible change for CLI usage, _no_ impact on API level!), e.g. on CLI type in + `$ jyt.js foo.js bar.yaml` instead of `$ jyt.js -s foo.js -d bar.yaml`. +- [[#31](https://github.com/deadratfink/jy-transform/issues/31)] Bugfix: given `Object` source results in + 'yaml' for origin (API). +- [Cleanup] Update dependencies. #### v1.0.2 -- [[#30](https://github.com/deadratfink/jy-transform/issues/30)] Fix README and externalize API reference to wiki -- [[#29](https://github.com/deadratfink/jy-transform/issues/29)] Fix Promise warning on write process +- [[#30](https://github.com/deadratfink/jy-transform/issues/30)] Fix README and externalize API reference to wiki. +- [[#29](https://github.com/deadratfink/jy-transform/issues/29)] Fix Promise warning on write process. #### v1.0.1 -Initial public release. This covers the basic implementation and tests. The following features and fixes and part of this release: - -- [[#27](https://github.com/deadratfink/jy-transform/issues/27)] Export variable for JS input -- [[#22](https://github.com/deadratfink/jy-transform/issues/22)] Integrate Coveralls -- [[#21](https://github.com/deadratfink/jy-transform/issues/21)] Check and fix CodeClimate issues -- [[#20](https://github.com/deadratfink/jy-transform/issues/20)] Cleanup test dir -- [[#19](https://github.com/deadratfink/jy-transform/issues/19)] File overwrite switch (`-f`, `-force`) -- [[#18](https://github.com/deadratfink/jy-transform/issues/18)] Read and Write from other sources than file path -- [[#16](https://github.com/deadratfink/jy-transform/issues/16)] ERROR: Error: Invalid target option found while creating destination file extension -- [[#15](https://github.com/deadratfink/jy-transform/issues/15)] Measure test code coverage and add a badge -- [[#12](https://github.com/deadratfink/jy-transform/issues/12)] Create middleware collection file to use by clients and internally -- [[#11](https://github.com/deadratfink/jy-transform/issues/11)] Check all Promises for optimization possibilities -- [[#10](https://github.com/deadratfink/jy-transform/issues/10)] Integrate project with Travis -- [[#9](https://github.com/deadratfink/jy-transform/issues/9)] Resolve origin and target from file extension whenever possible -- [[#8](https://github.com/deadratfink/jy-transform/issues/8)] Enable JS reading with `require(...)` -- [[#7](https://github.com/deadratfink/jy-transform/issues/7)] YAML indent is not set to `Constants.MIN_YAML_INDENT` when indent is set to 0 -- [[#6](https://github.com/deadratfink/jy-transform/issues/6)] Finish full JSDoc for all methods -- [[#5](https://github.com/deadratfink/jy-transform/issues/5)] Write unit tests -- [[#4](https://github.com/deadratfink/jy-transform/issues/4)] Export variable for JS output -- [[#3](https://github.com/deadratfink/jy-transform/issues/3)] Promise array as middleware solved with `Promise.all([...])` +Initial public release. This covers the basic implementation and tests. The following features and fixes and +part of this release: +- [[#27](https://github.com/deadratfink/jy-transform/issues/27)] Export variable for JS input. +- [[#22](https://github.com/deadratfink/jy-transform/issues/22)] Integrate Coveralls. +- [[#21](https://github.com/deadratfink/jy-transform/issues/21)] Check and fix CodeClimate issues. +- [[#20](https://github.com/deadratfink/jy-transform/issues/20)] Cleanup test dir. +- [[#19](https://github.com/deadratfink/jy-transform/issues/19)] File overwrite switch (`-f`, `-force`). +- [[#18](https://github.com/deadratfink/jy-transform/issues/18)] Read and Write from other sources than file path. +- [[#16](https://github.com/deadratfink/jy-transform/issues/16)] ERROR: Error: Invalid target option found while + creating destination file extension. +- [[#15](https://github.com/deadratfink/jy-transform/issues/15)] Measure test code coverage and add a badge. +- [[#12](https://github.com/deadratfink/jy-transform/issues/12)] Create middleware collection file to use by + clients and internally. +- [[#11](https://github.com/deadratfink/jy-transform/issues/11)] Check all Promises for optimization possibilities. +- [[#10](https://github.com/deadratfink/jy-transform/issues/10)] Integrate project with Travis. +- [[#9](https://github.com/deadratfink/jy-transform/issues/9)] Resolve origin and target from file extension + whenever possible. +- [[#8](https://github.com/deadratfink/jy-transform/issues/8)] Enable JS reading with `require(...)`. +- [[#7](https://github.com/deadratfink/jy-transform/issues/7)] YAML indent is not set to `Constants.MIN_YAML_INDENT` + when indent is set to 0. +- [[#6](https://github.com/deadratfink/jy-transform/issues/6)] Finish full JSDoc for all methods. +- [[#5](https://github.com/deadratfink/jy-transform/issues/5)] Write unit tests. +- [[#4](https://github.com/deadratfink/jy-transform/issues/4)] Export variable for JS output. +- [[#3](https://github.com/deadratfink/jy-transform/issues/3)] Promise array as middleware solved with `Promise.all([...])`. diff --git a/PACKAGE.md b/PACKAGE.md index 7c1134f..e5098b3 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -4,8 +4,6 @@ This project aims to read, write and transform YAML, JS or JSON objects into eac ## Installation -Download node at [nodejs.org](http://nodejs.org) and install it, if you haven't already. - ```sh npm install jy-transform --global ``` @@ -20,9 +18,8 @@ npm test ## Dependencies -- [bluebird](https://github.com/petkaantonov/bluebird): Full featured Promises/A+ implementation with exceptionally good performance +- [babel-cli](https://github.com/babel/babel/tree/master/packages): Babel command line. - [cli](https://github.com/node-js-libs/cli): A tool for rapidly building command line apps -- [cwd](https://github.com/jonschlinkert/cwd): Easily get the CWD (current working directory) of a project based on package.json, optionally starting from a given path. (node.js/javascript util) - [is-stream](https://github.com/sindresorhus/is-stream): Check if something is a Node.js stream - [joi](https://github.com/hapijs/joi): Object schema validation - [js-yaml](https://github.com/nodeca/js-yaml): YAML 1.2 parser and serializer @@ -33,17 +30,12 @@ npm test ## Dev Dependencies -- [babel-cli](https://github.com/babel/babel/tree/master/packages): Babel command line. -- [babel-core](https://github.com/babel/babel/tree/master/packages): Babel compiler core. - [babel-eslint](https://github.com/babel/babel-eslint): Custom parser for ESLint -- [babel-plugin-transform-builtin-extend](https://github.com/loganfsmyth/babel-plugin-transform-builtin-extend): A plugin for Babel 6 supports extending from builtin types based on static analysis. +- [babel-plugin-transform-flow-strip-types](https://github.com/babel/babel/tree/master/packages): Strip flow type annotations from your output code. - [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. -- [babel-preset-stage-0](https://github.com/babel/babel/tree/master/packages): Babel preset for stage 0 plugins -- [babel-watch](https://github.com/kmagiera/babel-watch): Reload your babel-node app on JS source file changes. And do it *fast*. -- [chalk](https://github.com/chalk/chalk): Terminal string styling done right. Much color. +- [chalk](https://github.com/chalk/chalk): Terminal string styling done right. Much color - [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects - [codecov](https://github.com/codecov/codecov-node): Uploading report to Codecov: https://codecov.io -- [combarnea-winston-console-formatter](https://github.com/combarnea/winston-console-formatter): Pretty print console formatter in yaml like style - [coveralls](https://github.com/nickmerwin/node-coveralls): takes json-cov output into stdin and POSTs to coveralls.io - [doctoc](https://github.com/thlorenz/doctoc): Generates TOC for markdown files of local git repo. - [eslint](https://github.com/eslint/eslint): An AST-based pattern checker for JavaScript. @@ -54,14 +46,10 @@ npm test - [eslint-plugin-jest-async](https://github.com/deadratfink/jy-transform.git): ESLint plugin to detect improper Jest test assertions for asynchronous (Promise-based) actions - [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc): JSDoc linting rules for ESLint. - [fs-extra](https://github.com/jprichardson/node-fs-extra): fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf. -- [istanbul](https://github.com/gotwarlost/istanbul): Yet another JS code coverage tool that computes statement, line, function and branch coverage with module loader hooks to transparently add coverage when running tests. Supports all JS coverage use cases including unit tests, server side functional tests - [jest](https://github.com/facebook/jest): Delightful JavaScript Testing. - [jsdoc-babel](https://github.com/ctumolosus/jsdoc-babel): A JSDoc plugin that transforms ES6 source files with Babel before they are processsed. - [jsdoc-parse](https://github.com/jsdoc2md/jsdoc-parse): Transforms jsdoc data into something more suitable for use as template input - [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown): Generates markdown API documentation from jsdoc annotated source code -- [mocha](https://github.com/mochajs/mocha): simple, flexible, fun test framework -- [mocha-lcov-reporter](https://github.com/StevenLooman/mocha-lcov-reporter): LCOV reporter for Mocha -- [object-path](https://github.com/mariocasciaro/object-path): Access deep object properties using a path - [package-json-to-readme](https://github.com/zeke/package-json-to-readme): Generate a README.md from package.json contents - [winston](https://github.com/winstonjs/winston): A multi-transport async logging library for Node.js - [winston-console-formatter](https://github.com/eugeny-dementev/winston-console-formatter): Pretty print console formatter in yaml like style @@ -70,4 +58,3 @@ npm test ## License SEE LICENSE IN [LICENSE.md](https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md) - diff --git a/README.md b/README.md index 6d79cfd..91fcb94 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,61 @@ +[![License][gh-license-image]][gh-license-url] [![Issue Stats][gh-issues-image]][gh-issues-url] [![Releases][gh-releases-image]][gh-releases-url] [![Tags][gh-tags-image]][gh-tags-url] [![Build Status][ci-image]][ci-url] [![Waffle][waffle-image]][waffle-url] [![Code Climate][cocl-image]][cocl-url] +[![codecov.io][cc-image-master]][cc-url-master] [![coveralls.io][ca-image-master]][ca-url-master] [![inch-ci.org][inch-image-master]][inch-url-master] [![Dependency Status][dep-image-master]][dep-url-master] [![devDependency Status][devdep-image-master]][devdep-url-master] + +[![NPM](https://nodei.co/npm/jy-transform.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/jy-transform/) + +[![NPM](https://nodei.co/npm-dl/jy-transform.png?height=3&months=9)](https://nodei.co/npm-dl/jy-transform/) + +[gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat-square +[gh-license-url]: https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md + +[gh-issues-image]: https://img.shields.io/github/issues/deadratfink/jy-transform.svg?style=flat-square +[gh-issues-url]: https://github.com/deadratfink/jy-transform/issues + +[gh-releases-image]: https://img.shields.io/github/release/deadratfink/jy-transform.svg?style=flat-square +[gh-releases-url]: https://github.com/deadratfink/jy-transform/releases + +[gh-tags-image]: https://img.shields.io/github/tag/deadratfink/jy-transform.svg?style=flat-square +[gh-tags-url]: https://github.com/deadratfink/jy-transform/tags + + +[ci-image]: https://img.shields.io/travis/deadratfink/jy-transform.svg?style=flat-square +[ci-url]: https://travis-ci.org/deadratfink/jy-transform/branches + +[is-pull-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/pr?style=flat-square +[is-issue-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/issue?style=flat-square +[is-url]: http://issuestats.com/github/deadratfink/jy-transform + +[waffle-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat-square +[waffle-url]: https://waffle.io/deadratfink/jy-transform + +[cocl-image]: https://img.shields.io/codeclimate/github/deadratfink/jy-transform.svg?style=flat-square +[cocl-url]: https://codeclimate.com/github/deadratfink/jy-transform + + +[cc-image-master]: https://img.shields.io/codecov/c/github/deadratfink/jy-transform/master.svg?style=flat-square +[cc-url-master]: https://codecov.io/github/deadratfink/jy-transform?branch=master + +[ca-image-master]: https://img.shields.io/coveralls/deadratfink/jy-transform/master.svg?style=flat-square +[ca-url-master]: https://coveralls.io/github/deadratfink/jy-transform?branch=master + + +[inch-image-master]: https://inch-ci.org/github/deadratfink/jy-transform.svg?branch=master&style=flat-square +[inch-url-master]: https://inch-ci.org/github/deadratfink/jy-transform?branch=master + +[dep-image-master]: https://img.shields.io/david/deadratfink/jy-transform/master.svg?style=flat-square +[dep-url-master]: https://david-dm.org/deadratfink/jy-transform/master + +[devdep-image-master]: https://img.shields.io/david/dev/deadratfink/jy-transform/master.svg?style=flat-square +[devdep-url-master]: https://david-dm.org/deadratfink/jy-transform/master#info=devDependencies + + + # jy-transform This project aims to read, write and transform YAML, JS or JSON objects into each other using CLI or API, while the source and destination resources can be files on CLI and additionally, objects or streams on API level. ## Installation -Download node at [nodejs.org](http://nodejs.org) and install it, if you haven't already. - ```sh npm install jy-transform --global ``` @@ -17,6 +67,9 @@ npm install jy-transform --global ## TOC - [API in a Minute](#api-in-a-minute) + - [Read, Transform & Write from Source to Destination](#read-transform--write-from-source-to-destination) + - [Read into JS object from particular Source (File, Stream or JS Object) only](#read-into-js-object-from-particular-source-file-stream-or-js-object-only) + - [Write JS object to particular Destination only](#write-js-object-to-particular-destination-only) - [Why This Module?](#why-this-module) - [Usage](#usage) - [Usage Types](#usage-types) @@ -33,11 +86,10 @@ npm install jy-transform --global ## API in a Minute -```javascript -import { transform, read, write } from 'jy-transform'; - +### Read, Transform & Write from Source to Destination -// --- transform from source to destination --- +```javascript +import { transform } from 'jy-transform'; const transformOptions = { src: 'foo/bar.yaml', @@ -57,9 +109,12 @@ try { } catch (err) { console.error(err.stack); } +``` +### Read into JS object from particular Source (File, Stream or JS Object) only -// --- read into JS object from particular source (file, stream or JS object) --- +```javascript +import { read } from 'jy-transform'; let object; @@ -69,9 +124,12 @@ try { } catch (err) { console.error(err.stack); } +``` +### Write JS object to particular Destination only -// --- write a JS object to particular destination --- +```javascript +import { write } from 'jy-transform'; try { const msg = await write(object, { dest: 'foo/bar.yaml' }); @@ -110,13 +168,10 @@ So, what are the typical use cases for this module? In terms of _transformation_ these consists of different phases: - Reading files (`Reader`) -- Transforming JSON objects (`Transformer`) -- Apply dedicated actions on the intermediate JSON objects (`Transformer` + `Middleware`) +- Transforming JSON objects (`Transformer`) or apply dedicated actions on the intermediate JSON objects (`Transformer` + `Middleware`) - Writing files (`Writer`) -#### Reading - -From: +#### Reading From - _*.yaml_ file - _*.js_ file @@ -156,9 +211,7 @@ while: As mentioned above a _middleware_ can apply particular actions on the intermediate JS object via injected functions. This is an optional part for [transformation](#transformation) phase. -#### Writing - -To: +#### Writing To - _*.yaml_ file - _*.js_ file @@ -489,7 +542,7 @@ async function transform(options, middleware) #### Options -The `options` object has to follow this key-values table: +The `options` object has to follow this key-values tables: ##### Reader Options @@ -509,8 +562,6 @@ The `options` object has to follow this key-values table: | `imports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | | `exports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | -**NOTE:** an invalid indention setting (1 > indent > 8) does not raise an error but a default of 2 SPACEs is applied instead. - ##### Transformer Options These are a combination of the [Reader](#reader-options) and [Writer](#writer-options) Options. @@ -658,8 +709,7 @@ wiki which describes the full API and provides more examples. ## Contributing -Pull requests and stars are always welcome. Anybody is invited to take part -into this project. For bugs and feature requests, please create an +Pull requests and stars are always welcome. For bugs and feature requests, please create an [issue](https://github.com/deadratfink/jy-transform/issues). See the wiki [Contributing](https://github.com/deadratfink/jy-transform/wiki/Contributing) section for more details about conventions. diff --git a/bin/create-readme.sh b/bin/create-readme.sh index c01422b..19e7f65 100755 --- a/bin/create-readme.sh +++ b/bin/create-readme.sh @@ -1,7 +1,6 @@ #!/bin/bash api="true" -package="true" changelog="true" license="true" makefile="true" @@ -14,10 +13,6 @@ while [[ $# -gt 1 ]]; do api="$2" shift # past argument ;; - -p|--package) - package="$2" - shift # past argument - ;; -c|--changelog) changelog="$2" shift # past argument @@ -42,37 +37,31 @@ done # PACKAGE.md ############################################################################### -if [ "$package" == "true" ]; then - printf "Create documentation (PACKAGE.md)\n" - touch PACKAGE.md - node node_modules/.bin/package-json-to-readme --no-footer package.json > PACKAGE.md - cat readme/LOGO.md >> README.md - cat readme/BADGES.md >> README.md - printf "\n\n" >> README.md - head -12 PACKAGE.md > README.md - printf "\n\n" >> README.md -else - # to ensure that the README.md is always empty at the beginning - printf "" > README.md -fi +printf "Create documentation (PACKAGE.md)\n" +touch PACKAGE.md +node node_modules/.bin/package-json-to-readme --no-footer package.json > PACKAGE.md ############################################################################### # README.md ############################################################################### +touch README.md +# cat readme/LOGO.md >> README.md +cat readme/BADGES.md > README.md +printf "\n\n" >> README.md +head -10 PACKAGE.md >> README.md +printf "\n\n" >> README.md + printf "\n\n\n" >> README.md cat readme/DOCUMENTATION.md >> README.md printf "\n\n" >> README.md -if [[ "$package" == "true" ]] || [[ "$api" == "true" ]] || [[ "$env" == "true" ]] || ([[ -f "$MAKE_FILE" ]] && [[ "$makefile" == "true" ]]); then - printf "## Further information" >> README.md - printf "\n\n" >> README.md -fi +printf "## Further information" >> README.md +printf "\n\n" >> README.md + +printf -- "- [Module Details](./PACKAGE.md)" >> README.md +printf "\n\n" >> README.md -if [ "$package" == "true" ]; then - printf -- "- [Module Details](./PACKAGE.md)" >> README.md - printf "\n\n" >> README.md -fi ############################################################################### # API-PUBLIC.md @@ -82,8 +71,8 @@ if [ "$api" == "true" ]; then printf "Create documentation (API-PUBLIC.md)\n" touch API-PUBLIC.md printf "\n\n\n" >> API-PUBLIC.md - node node_modules/.bin/jsdoc2md --no-cache --configure .jsdoc.json . > API-PUBLIC.md - node node_modules/.bin/doctoc API-PUBLIC.md --github --title "## TOC" --maxlevel 2 + node node_modules/.bin/jsdoc2md package.json index.js --no-cache --configure .jsdoc-public.json . > API-PUBLIC.md + node node_modules/.bin/doctoc API-PUBLIC.md --github --title "## TOC" --maxlevel 2 printf -- "- [Public Api Reference](./API-PUBLIC.md)" >> README.md printf "\n\n" >> README.md @@ -98,7 +87,7 @@ if [ "$api" == "true" ]; then touch API-PRIVATE.md printf "\n\n\n" >> API-PRIVATE.md node node_modules/.bin/jsdoc2md --no-cache --private --configure .jsdoc.json . > API-PRIVATE.md - node node_modules/.bin/doctoc API-PRIVATE.md --github --title "## TOC" --maxlevel 2 + node node_modules/.bin/doctoc API-PRIVATE.md --github --title "## TOC" --maxlevel 2 printf -- "- [Private Api Reference](./API-PRIVATE.md)" >> README.md printf "\n\n" >> README.md diff --git a/codecov.yml b/codecov.yml index ce34c4b..19eb643 100644 --- a/codecov.yml +++ b/codecov.yml @@ -67,10 +67,9 @@ coverage: # option: "X%" a static target percentage to hit branches: - master - - development - - bugfix/#* - - feature/#* - - hotfix/* + - bugfix/* + - feature/* + - refactor/* #threshold: null # allowed to drop X% and still result in a "success" commit status if_no_uploads: error # will post commit status of "error" if no coverage reports we uploaded # options: success, error, failure @@ -98,10 +97,9 @@ coverage: enabled: yes # must be yes|true to enable this status branches: - master - - development - - bugfix/#* - - feature/#* - - hotfix/* + - bugfix/* + - feature/* + - refactor/* if_no_uploads: error if_not_found: success if_ci_failed: error @@ -117,10 +115,9 @@ comment: layout: "header, diff, changes, sunburst, suggestions" branches: - master - - development - - bugfix/#* - - feature/#* - - hotfix/* + - bugfix/* + - feature/* + - refactor/* behavior: default # option: "default" posts once then update, posts new if delete # option: "once" post once then updates, if deleted do not post new # option: "new" delete old, post new diff --git a/jyt b/jyt index 0fa0360..e15fef3 100755 --- a/jyt +++ b/jyt @@ -1,4 +1,3 @@ -#!./node_modules/.bin/babel-node +#!/usr/bin/env node -// eslint-disable-next-line no-unused-vars -import jyt from './src/cli'; +require('./lib/cli'); diff --git a/package.json b/package.json index c1f9206..ca98689 100644 --- a/package.json +++ b/package.json @@ -20,66 +20,57 @@ "bugs": "https://github.com/deadratfink/jy-transform/issues", "private": false, "scripts": { - "build": "babel src -d lib", + "build": "NODE_ENV=production babel src --out-dir lib --copy-files --source-maps", "doc": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", "readme": "bin/create-readme.sh -a true -c false -m true -l false", "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", "pretest": "mkdir -pv test/tmp", - "test": "JYT_DEBUG=true JYT_ERROR=true jest --expand --no-cache --coverage --config=./.jestrc.js", + "test": "JYT_DEBUG=false JYT_ERROR=false jest --forceExit --expand --no-cache --coverage --config=./.jestrc.js", "eslint": "eslint ." }, "engines": { "node": ">=4.0.0" }, "dependencies": { - "bluebird": "^3.4.6", - "cli": "^1.0.1", - "cwd": "~0.10.0", - "is-stream": "^1.1.0", - "joi": "~10.5.0", - "js-yaml": "^3.6.1", - "json-stringify-safe": "^5.0.1", - "mkdirp-then": "^1.2.0", + "babel-cli": "~6.24.1", + "cli": " ~1.0.1", + "is-stream": " ~1.1.0", + "joi": "~10.6.0", + "js-yaml": " ~3.8.4", + "json-stringify-safe": " ~5.0.1", + "mkdirp-then": " ~1.2.0", "promisify-es6": "~1.0.2", - "serialize-js": "^1.1.0" + "serialize-js": " ~1.1.0" }, "devDependencies": { - "babel-cli": "~6.24.1", - "babel-core": "~6.24.1", - "babel-eslint": "~7.2.3", - "babel-plugin-transform-builtin-extend": "~1.1.2", - "babel-preset-env": "~1.4.0", - "babel-preset-stage-0": "~6.24.1", - "babel-watch": "~2.0.6", - "chalk": "~1.1.3", - "codeclimate-test-reporter": "^0.4.0", - "codecov": "^1.0.1", - "combarnea-winston-console-formatter": "~1.0.1", - "coveralls": "^2.11.14", - "doctoc": "^1.0.0", - "eslint": "~3.19.0", - "eslint-config-airbnb-base": "~10.0.1", - "eslint-plugin-filenames": "~1.2.0", - "eslint-plugin-import": "~2.2.0", - "eslint-plugin-jest": "~20.0.3", - "eslint-plugin-jest-async": "~1.0.3", - "eslint-plugin-jsdoc": "~2.3.1", - "fs-extra": "^1.0.0", - "istanbul": "^0.4.5", + "babel-eslint": " ~7.2.3", + "babel-plugin-transform-flow-strip-types": "~6.22.0", + "babel-preset-env": "~1.5.2", + "chalk": "~2.0.1", + "codeclimate-test-reporter": " ~0.5.0", + "codecov": " ~2.2.0", + "coveralls": " ~2.13.1", + "cwd": "~0.10.0", + "doctoc": " ~1.3.0", + "eslint": "~4.1.0", + "eslint-config-airbnb-base": "~11.2.0", + "eslint-plugin-filenames": "1.2.0", + "eslint-plugin-import": " ~2.6.1", + "eslint-plugin-jest": " ~20.0.3", + "eslint-plugin-jest-async": " ~1.0.3", + "eslint-plugin-jsdoc": " ~3.1.1", + "fs-extra": " ~3.0.1", "jest": "~20.0.4", - "jsdoc-babel": "^0.3.0", - "jsdoc-parse": "^2.0.5", - "jsdoc-to-markdown": "^2.0.1", - "mocha": "^3.1.2", - "mocha-lcov-reporter": "^1.2.0", - "object-path": "^0.11.2", - "package-json-to-readme": "^1.5.1", - "winston": "^2.3.0", + "jsdoc-babel": " ~0.3.0", + "jsdoc-parse": " ~3.0.0", + "jsdoc-to-markdown": " ~3.0.0", + "package-json-to-readme": " ~2.0.0", + "winston": " ~2.3.0", "winston-console-formatter": "~0.3.1" }, "preferGlobal": true, "bin": { - "jyt": "./jyt" + "jyt": "./src/cli.js" }, "main": "./index.js", "keywords": [ diff --git a/readme/API.md b/readme/API.md deleted file mode 100644 index fa33aca..0000000 --- a/readme/API.md +++ /dev/null @@ -1,1371 +0,0 @@ - - -### TOC - -- [Classes](#classes) -- [Typedefs](#typedefs) -- [Constants](#constants) -- [LogWrapper](#logwrapper) -- [Middleware](#middleware) -- [OptionsHandler](#optionshandler) -- [Reader](#reader) -- [Transformer](#transformer) -- [Validator](#validator) -- [Writer](#writer) -- [Options : object](#options--codeobjectcode) - - - -## Classes - -

      -
      Constants
      -

      Class which defines all constants usable in or with this module.

      -
      -
      LogWrapper
      -

      Class which defines a logger wrapper usable in this module. -

      - NOTE: This class is not to be intended to be called from - outside this module!

      -
      -
      Middleware
      -

      Class which defines middleware Promises usable in or with this module.

      -
      -
      OptionsHandler
      -

      Class which defines some useful methods to initialize and prepare the - transformation options used in this module. -

      - NOTE: This class is not to be intended to be called from - outside this module!

      -
      -
      Reader
      -

      This class provides utility methods usable to read YAML, JSON or JS - from a source (file, {object} or stream.Readable) to JS memory objects.

      -
      -
      Transformer
      -

      This class provides all methods usable to handle YAML, JSON and JS and - their transformations.

      -
      -
      Validator
      -

      This class validates JS identifier. -

      - NOTE: this class is intended for internal use only!

      -
      -
      Writer
      -

      This class provides utility methods usable to write JS objects - from memory to a JSON/JS/YAML destination - (file, object or stream.Readable).

      -
      -
      - -## Typedefs - -
      -
      Options : object
      -
      -
      - - - -## Constants -Class which defines all constants usable in or with this module. - -**Kind**: global class - -* [Constants](#Constants) - * [new Constants()](#new_Constants_new) - * [.DEFAULT_OPTIONS](#Constants+DEFAULT_OPTIONS) : object - * [.UTF8](#Constants+UTF8) : string - * [.YAML](#Constants+YAML) : string - * [.JSON](#Constants+JSON) : string - * [.JS](#Constants+JS) : string - * [.TYPES](#Constants+TYPES) : Array.<string> - * [.DEFAULT_INDENT](#Constants+DEFAULT_INDENT) : number - * [.MIN_INDENT](#Constants+MIN_INDENT) : number - * [.MAX_INDENT](#Constants+MAX_INDENT) : number - * [.DEFAULT_ORIGIN](#Constants+DEFAULT_ORIGIN) : string - * [.DEFAULT_TARGET](#Constants+DEFAULT_TARGET) : string - * [.DEFAULT_FORCE_FILE_OVERWRITE](#Constants+DEFAULT_FORCE_FILE_OVERWRITE) : boolean - * [.ORIGIN_DESCRIPTION](#Constants+ORIGIN_DESCRIPTION) : string - * [.TARGET_DESCRIPTION](#Constants+TARGET_DESCRIPTION) : string - * [.DEST_DESCRIPTION](#Constants+DEST_DESCRIPTION) : string - * [.DEFAULT_JS_IMPORTS_IDENTIFIER](#Constants+DEFAULT_JS_IMPORTS_IDENTIFIER) : string - * [.DEFAULT_JS_EXPORTS_IDENTIFIER](#Constants+DEFAULT_JS_EXPORTS_IDENTIFIER) : string - * [.YAML_TO_JS](#Constants+YAML_TO_JS) : string - * [.YAML_TO_JSON](#Constants+YAML_TO_JSON) : string - * [.JS_TO_YAML](#Constants+JS_TO_YAML) : string - * [.JSON_TO_YAML](#Constants+JSON_TO_YAML) : string - * [.JSON_TO_JS](#Constants+JSON_TO_JS) : string - * [.JS_TO_JSON](#Constants+JS_TO_JSON) : string - * [.YAML_TO_YAML](#Constants+YAML_TO_YAML) : string - * [.JSON_TO_JSON](#Constants+JSON_TO_JSON) : string - * [.JS_TO_JS](#Constants+JS_TO_JS) : string - * [.TRANSFORMATIONS](#Constants+TRANSFORMATIONS) : Array.<string> - - - -### new Constants() -Constructs the constants. - -**Returns**: [Constants](#Constants) - - The instance. - - -### constants.DEFAULT_OPTIONS : object -The default options. - -**Kind**: instance namespace of [Constants](#Constants) -**See** - -- [ORIGIN_DESCRIPTION](ORIGIN_DESCRIPTION) -- [TARGET_DESCRIPTION](TARGET_DESCRIPTION) -- [DEST_DESCRIPTION](DEST_DESCRIPTION) - -**Properties** - -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| origin | string | "yaml" | The default origin type. | -| target | string | "js" | The default target type. | -| dest | string | "relative_to_input_file" | The default dest description. | -| indent | number | 4 | The default indention for files. | -| force | boolean | false | Whether to overwrite existing file on output. | -| imports | string | | The exports name for reading from JS source file or objects only. | -| exports | string | | The exports name for usage in JS file or object only. | - - - -### constants.UTF8 : string -The 'utf8' constant. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.YAML : string -The 'yaml' type constant. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.JSON : string -The 'json' type constant. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.JS : string -The 'js' type constant. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.TYPES : Array.<string> -The type constants assembled in an array: `[ 'yaml', 'json', 'js' ]`. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.DEFAULT_INDENT : number -The default file indention (4 SPACEs). - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.MIN_INDENT : number -The minimum file indention (0 SPACE). - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.MAX_INDENT : number -The maximum file indention (8 SPACEs). - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.DEFAULT_ORIGIN : string -The default `origin` value: 'yaml'. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.DEFAULT_TARGET : string -The default `origin` value: 'js'. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.DEFAULT_FORCE_FILE_OVERWRITE : boolean -Whether to overwrite existing file or object on output. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.ORIGIN_DESCRIPTION : string -The `origin` description value. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.TARGET_DESCRIPTION : string -The `target` description value. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.DEST_DESCRIPTION : string -The `dest` description value. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.DEFAULT_JS_IMPORTS_IDENTIFIER : string -The `src` exports identifier value to read. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public -**Example** -```js -module.exports.foo = {...}; // here 'foo' is the identifier for an object to read from the source! -``` - - -### constants.DEFAULT_JS_EXPORTS_IDENTIFIER : string -The `dest` exports identifier value to write. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.YAML_TO_JS : string -The transformation direction YAML ⇒ JS. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.YAML_TO_JSON : string -The transformation direction YAML ⇒ JSON. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.JS_TO_YAML : string -The transformation direction JS ⇒ YAML. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.JSON_TO_YAML : string -The transformation direction JSON ⇒ YAML. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.JSON_TO_JS : string -The transformation direction JSON ⇒ JS. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.JS_TO_JSON : string -The transformation direction JS ⇒ JSON. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.YAML_TO_YAML : string -The transformation direction YAML ⇒ YAML. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.JSON_TO_JSON : string -The transformation direction JSON ⇒ JSON. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.JS_TO_JS : string -The transformation direction JS ⇒ JS. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -### constants.TRANSFORMATIONS : Array.<string> -The transformation directions. - -**Kind**: instance constant of [Constants](#Constants) -**Access:** public - - -## LogWrapper -Class which defines a `logger` wrapper usable in this module. -

      - **NOTE:** This class is not to be intended to be called from - outside this module! - -**Kind**: global class - -* [LogWrapper](#LogWrapper) - * [new LogWrapper([logger])](#new_LogWrapper_new) - * [.info(msg)](#LogWrapper+info) - * [.debug(msg)](#LogWrapper+debug) - * [.trace(msg)](#LogWrapper+trace) - * [.error(msg)](#LogWrapper+error) - * [.verboseOptions(options)](#LogWrapper+verboseOptions) ⇒ - - - -### new LogWrapper([logger]) -Constructs the `LogWrapper`. - -**Returns**: [LogWrapper](#LogWrapper) - - The instance. - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| [logger] | logger | cli | console | console | Logger object. | - -**Example** -```js -var logger = ...; -var logWrapper = new LogWrapper(logger); -``` - - -### logWrapper.info(msg) -Log the options with INFO level. - -**Kind**: instance method of [LogWrapper](#LogWrapper) -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| msg | string | The message to log. | - -**Example** -```js -var logger = ...; -var logWrapper = new LogWrapper(logger); -var msg = '...'; -logWrapper.info(msg); -``` - - -### logWrapper.debug(msg) -Log the options with DEBUG level (if logger supports it, else with INFO). - -**Kind**: instance method of [LogWrapper](#LogWrapper) -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| msg | string | The message to log. | - -**Example** -```js -var logger = ...; -var logWrapper = new LogWrapper(logger); -var msg = '...'; -logWrapper.debug(msg); -``` - - -### logWrapper.trace(msg) -Log the options with TRACE level (if logger supports it, else with DEBUG). - -**Kind**: instance method of [LogWrapper](#LogWrapper) -**Access:** public -**See**: [#debug](#debug) - -| Param | Type | Description | -| --- | --- | --- | -| msg | string | The message to log. | - -**Example** -```js -var logger = ...; -var logWrapper = new LogWrapper(logger); -var msg = '...'; -logWrapper.trace(msg); -``` - - -### logWrapper.error(msg) -Log the options with ERROR level. - -**Kind**: instance method of [LogWrapper](#LogWrapper) -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| msg | string | The message to log. | - -**Example** -```js -var logger = ...; -var logWrapper = new LogWrapper(logger); -var msg = '...'; -logWrapper.error(msg); -``` - - -### logWrapper.verboseOptions(options) ⇒ -Log the options with INFO level. - -**Kind**: instance method of [LogWrapper](#LogWrapper) -**Returns**: A Promise containing the passed `options` object. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The properties to log with INFO. | - -**Example** -```js -var logger = ...; -var logWrapper = new LogWrapper(logger); -var options = { - ... -}; -logWrapper.verboseOptions(options) - .then(function (options) { - ... - }); -``` - - -## Middleware -Class which defines middleware Promises usable in or with this module. - -**Kind**: global class - -* [Middleware](#Middleware) - * [new Middleware()](#new_Middleware_new) - * [.identityMiddleware](#Middleware+identityMiddleware) ⇒ Promise.<object> - * [.ensureMiddleware(middleware)](#Middleware+ensureMiddleware) ⇒ Promise - - - -### new Middleware() -Constructs the `Middleware`. - -**Returns**: [Middleware](#Middleware) - - The instance. -**Example** -```js -var middleware = require('./lib/middleware.js'); -``` - - -### middleware.identityMiddleware ⇒ Promise.<object> -Middleware Promise which reflects the identity of passed JSON: `f(object) → object`. - -**Kind**: instance property of [Middleware](#Middleware) -**Returns**: Promise.<object> - - A Promise resolving the passed `json` object. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| object | object | The object which is returned in Promise. | - -**Example** -```js -var middleware = require('./lib/middleware.js'); -var identityMiddleware = middleware.identityMiddleware; -transformer.transform(options, identityMiddleware) - .then(function(object) { - ... - }): -``` - - -### middleware.ensureMiddleware(middleware) ⇒ Promise -Ensure that the given middleware Promise is a function if set. -If not set a new JSON 'identity' Promise is returned which simply passes -a JSON object. - -**Kind**: instance method of [Middleware](#Middleware) -**Returns**: Promise - - The given middleware Promise or a new JSON 'identity' middleware Promise. -**Throws**: - -- TypeError - Will throw this error when the passed `middleware` - is not type of `Function`. - -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| middleware | function | This middleware Promise can be used to intercept the JSON object for altering he passed JSON, the function signature is: ``` function(object) ``` **NOTE:** the Promise has to return the processed JSON! | - -**Example** -```js -var middleware = require('./lib/middleware.js'); -var myMiddleware = function(object) { - //... -}; -transformer.transform(options, middleware.ensureMiddleware(myMiddleware)) - .then(function(object) { - //... - }): -``` - - -## OptionsHandler -Class which defines some useful methods to initialize and prepare the - transformation options used in this module. -

      - **NOTE:** This class is not to be intended to be called from - outside this module! - -**Kind**: global class - -* [OptionsHandler](#OptionsHandler) - * [new OptionsHandler([logger])](#new_OptionsHandler_new) - * [.assertOptions](#OptionsHandler+assertOptions) ⇒ Promise - * [.completeOptions(options)](#OptionsHandler+completeOptions) ⇒ Promise - * [.ensureSrc(options)](#OptionsHandler+ensureSrc) ⇒ Promise - * [.ensureDest(options)](#OptionsHandler+ensureDest) ⇒ Promise - * [.assertOrigin(options)](#OptionsHandler+assertOrigin) ⇒ Promise - * [.assertTarget(options)](#OptionsHandler+assertTarget) ⇒ Promise - * [.ensureIndent(options)](#OptionsHandler+ensureIndent) ⇒ Promise - * [.ensureOptions(options)](#OptionsHandler+ensureOptions) ⇒ Promise - * [.validateTransformation(options)](#OptionsHandler+validateTransformation) ⇒ Promise - - - -### new OptionsHandler([logger]) -Constructs the `OptionsHandler` with an (optional) logger. - -**Returns**: [OptionsHandler](#OptionsHandler) - The instance. - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| [logger] | logger | cli | console | console | Logger instance. | - -**Example** -```js -var OptionsHandler = require('./options-handler'); -var logger = ...; - -var optionsHandler = new OptionsHandler(logger); -``` - - -### optionsHandler.assertOptions ⇒ Promise -Asserts that the given `options` and (optionally) the given properties are -inside the options. If not, the Promise rejects with proper error message. - -**Kind**: instance property of [OptionsHandler](#OptionsHandler) -**Returns**: Promise - - Promise which contains the `options` as result. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | object | The objects which should be set. | -| [properties] | Array.<string> | Properties which should exist in `options`. | - -**Example** -```js -var options = {...}; - -assertOptions(options, ['src', 'origin']) - .then(function (assertedOptions) { - ... - }); -``` - - -### optionsHandler.completeOptions(options) ⇒ Promise -Completes the given `options` object by enriching from default values or using -type inference if something required is "missing" (a missing `options.src` cannot -be completed becaue this is mandatory). - -**Kind**: instance method of [OptionsHandler](#OptionsHandler) -**Returns**: Promise - - A Promise containing the passed `options` object. -**Throws**: - -- Error - If `options` or `options.src` not passed. - -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var options = {...}; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.completeOptions(options) - .then(function (copiedOptions) { - ... - }); -``` - - -### optionsHandler.ensureSrc(options) ⇒ Promise -Ensures that the given input source is valid. - -**Kind**: instance method of [OptionsHandler](#OptionsHandler) -**Returns**: Promise - - A Promise containing the passed `options` object. -**Throws**: - -- Error - If the `options.src` is not defined or the file represented by `options.src` does not exist. - -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var options = {...}; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.ensureSrc(options) - .then(function (ensuredOptions) { - ... - }); -``` - - -### optionsHandler.ensureDest(options) ⇒ Promise -This method ensures that destination file path is created if not set in -options. If not, then it creates the path relative to the source file using -its name and appending a proper extension depending on the `json` -property of `options` (if `true` then '.js', else '.json'). - -**Kind**: instance method of [OptionsHandler](#OptionsHandler) -**Returns**: Promise - - A Promise containing the passed `options` object. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var options = {...}; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.ensureDest(options) - .then(function (ensuredOptions) { - ... - }); -``` - - -### optionsHandler.assertOrigin(options) ⇒ Promise -Checks if the given origin is valid. - -**Kind**: instance method of [OptionsHandler](#OptionsHandler) -**Returns**: Promise - - A Promise containing the passed `options` object. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var options = {...}; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.assertOrigin(options) - .then(function (ensuredOptions) { - ... - }); -``` - - -### optionsHandler.assertTarget(options) ⇒ Promise -Checks if the given target is valid. - -**Kind**: instance method of [OptionsHandler](#OptionsHandler) -**Returns**: Promise - - A Promise containing the passed `options` object. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var options = {...}; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.assertTarget(options) - .then(function (ensuredOptions) { - ... - }); -``` - - -### optionsHandler.ensureIndent(options) ⇒ Promise -Checks if a valid indention value is given and corrects values if invalid (with default value: 4 SPACEs). - -**Kind**: instance method of [OptionsHandler](#OptionsHandler) -**Returns**: Promise - - A Promise containing the passed `options` object. -**Access:** public -**See** - -- [MIN_INDENT](#Constants+MIN_INDENT) -- [DEFAULT_INDENT](#Constants+DEFAULT_INDENT) -- [MAX_INDENT](#Constants+MAX_INDENT) - - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var options = {...}; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.ensureIndent(options) - .then(function (ensuredOptions) { - ... - }); -``` - - -### optionsHandler.ensureOptions(options) ⇒ Promise -This method ensures that the options object is set with all necessary and -correct values. The method does not alter the given object, but creates -and fills a new instance from the given values and/or default ones. - -**Kind**: instance method of [OptionsHandler](#OptionsHandler) -**Returns**: Promise - - A Promise containing a new and complete `options` object. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var options = {...}; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.ensureOptions(options) - .then(function (ensuredOptions) { - ... - }); -``` - - -### optionsHandler.validateTransformation(options) ⇒ Promise -This method validates the transformation process described by the given -options and provides the validate and enriched options and according name -to resolve a proper function. - -**Kind**: instance method of [OptionsHandler](#OptionsHandler) -**Returns**: Promise - - A Promise containing the passed `options` object and a 'transformation' string in an array. -**Access:** public -**See**: [transformations](transformations) - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The configuration for a transformation. | - -**Example** -```js -var OptionsHandler = require('./options-handler.js'); -var logger = ...; -var optionsHandler = new OptionsHandler(logger); - -optionsHandler.validateTransformation(options) - .spread(function (validatedOptions, transformation) { - ... - )): -``` - - -## Reader -This class provides utility methods usable to read YAML, JSON or JS - from a source (file, {object} or [stream.Readable](stream.Readable)) to JS memory objects. - -**Kind**: global class - -* [Reader](#Reader) - * [new Reader([logger])](#new_Reader_new) - * [.validator](#Reader+validator) : [Validator](#Validator) - * [.readJs(options)](#Reader+readJs) ⇒ Promise - * [.readYaml(options)](#Reader+readYaml) ⇒ Promise - - - -### new Reader([logger]) -Constructs the `Reader` with an (optional) logger. - -**Returns**: [Reader](#Reader) - The instance. - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| [logger] | logger | cli | console | console | Logger instance. | - -**Example** -```js -var Reader = require('jy-transform').Reader; -var logger = ...; - -var reader = new Reader(logger); -``` - - -### reader.validator : [Validator](#Validator) -The validator. - -**Kind**: instance property of [Reader](#Reader) - - -### reader.readJs(options) ⇒ Promise -Reads the data from a given JS or JSON source. - -**Kind**: instance method of [Reader](#Reader) -**Returns**: Promise - - Contains the read JS object. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | Contains the JS/JSON source reference to read from. | - -**Example** -```js -var Reader = require('jy-transform').Reader; -var logger = ...; -var reader = new Reader(logger); - -// --- from file path - -var options = { - src: 'foo.js' -}; - -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// --- from Readable - -options = { - src: fs.createReadStream('foo.js') -}; - -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// --- from object - -options = { - src: { - foo: 'bar' - } -}; - -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - - -### reader.readYaml(options) ⇒ Promise -Loads a single YAML source containing document and returns a JS object. - -*NOTE:* This function does not understand multi-document sources, it throws -exception on those. - -**Kind**: instance method of [Reader](#Reader) -**Returns**: Promise - - Contains the read JS object. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | Contains the YAML source reference to read from. | - -**Example** -```js -var Reader = require('jy-transform').Reader; -var logger = ...; -var reader = new Reader(logger); - -// --- from file path - -options = { - src: 'foo.yml' -}; - -reader.readYaml(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// --- from Readable - -options = { - src: fs.createReadStream('foo.yml') -}; - -reader.readJs(options) - .then(function (obj){ - logger.info(JSON.stringify(obj)); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - - -## Transformer -This class provides all methods usable to handle YAML, JSON and JS and - their transformations. - -**Kind**: global class - -* [Transformer](#Transformer) - * [new Transformer([logger])](#new_Transformer_new) - * _instance_ - * [.transform(options, [middleware])](#Transformer+transform) ⇒ Promise - * _inner_ - * [~ensureMiddleware](#Transformer..ensureMiddleware) - - - -### new Transformer([logger]) -Constructs the `Transformer` with options and an (optional) logger. - -**Returns**: [Transformer](#Transformer) - - The instance. - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| [logger] | logger | cli | console | console | Logger instance. | - -**Example** -```js -var logger = ...; -var Transformer = require('jy-transform'); -var transformer = new Transformer(logger); -``` - - -### transformer.transform(options, [middleware]) ⇒ Promise -The entry method for all transformation accepting a configuration object and -an (optional) middleware function. - -**Kind**: instance method of [Transformer](#Transformer) -**Returns**: Promise - - Containing the transformation result as message (e.g. - to be logged by caller). -**Throws**: - -- TypeError - Will throw this error when the passed `middleware` - is not type of `Function`. -- Error - Will throw plain error when writing to file failed due to any reason. - -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| options | [Options](#Options) | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` function(json) ``` **NOTE:** the Promise has to return the processed JSON! | - -**Example** -```js -var logger = ...; -var Transformer = require('jy-transform'); -var transformer = new Transformer(logger); -var Promise = require('bluebird'); -var options = {...}; -var middleware = function (json) { - json.myproperty = 'new value'; - return Promise.resolve(json); -}; - -transformer.transform(options, middleware) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - - -### Transformer~ensureMiddleware -Ensures that basic middleware is set. - -**Kind**: inner property of [Transformer](#Transformer) - - -## Validator -This class validates JS identifier. -

      - **NOTE:** this class is intended for internal use only! - -**Kind**: global class - -* [Validator](#Validator) - * [new Validator([logger])](#new_Validator_new) - * [.validateIdentifier(identifier)](#Validator+validateIdentifier) ⇒ boolean - - - -### new Validator([logger]) -Constructs the `Validator` with an (optional) logger. - -**Returns**: [Writer](#Writer) - The instance. - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| [logger] | logger | cli | console | console | Logger instance. | - -**Example** -```js -var Validator = require('./validator.js'); -var logger = ...; - -var validator = new Validator(logger); -``` - - -### validator.validateIdentifier(identifier) ⇒ boolean -This method checks if a given `identifier` is a valid ECMAScript 6 identifier. - -**Kind**: instance method of [Validator](#Validator) -**Returns**: boolean - - `true` if valid, else `false`. -**Access:** public - -| Param | Type | Description | -| --- | --- | --- | -| identifier | string | The identifier to check. | - -**Example** -```js -var Validator = require('./validator.js'); -var logger = ...; -var validator = new Validator(logger); -var identifier = 'foo'; - -logger.info('valid = ' + validator.validateIdentifier(identifier)); -``` - - -## Writer -This class provides utility methods usable to write JS objects - from memory to a JSON/JS/YAML destination - (file, object or [stream.Readable](stream.Readable)). - -**Kind**: global class - -* [Writer](#Writer) - * [new Writer([logger])](#new_Writer_new) - * [.validator](#Writer+validator) : [Validator](#Validator) - * [.writeYaml(object, options)](#Writer+writeYaml) ⇒ Promise - * [.writeJson(object, options)](#Writer+writeJson) ⇒ Promise - * [.writeJs(object, options)](#Writer+writeJs) ⇒ Promise - - - -### new Writer([logger]) -Constructs the `Writer` with an (optional) logger. - -**Returns**: [Writer](#Writer) - The instance. - -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| [logger] | logger | cli | console | console | Logger instance. | - -**Example** -```js -var Writer = require('jy-transform').Writer; -var logger = ...; - -var writer = new Writer(logger); -``` - - -### writer.validator : [Validator](#Validator) -The validator. - -**Kind**: instance property of [Writer](#Writer) - - -### writer.writeYaml(object, options) ⇒ Promise -Writes a JS object to a YAML destination. - -**Kind**: instance method of [Writer](#Writer) -**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). -**Throws**: - -- Error - If YAML destination could not be written due to any reason. - -**Access:** public -**See** - -- [MIN_INDENT](#Constants+MIN_INDENT) -- [DEFAULT_INDENT](#Constants+DEFAULT_INDENT) -- [MAX_INDENT](#Constants+MAX_INDENT) - - -| Param | Type | Description | -| --- | --- | --- | -| object | object | The JS object to write into YAML destination. | -| options | [Options](#Options) | The write destination and indention. | - -**Example** -```js -var Writer = require('jy-transform').Writer; -var logger = ...; -var writer = new Writer(logger); - -// ---- write obj to file - -var obj = {...}, -var options = { - dest: 'result.yml', - indent: 2 -} - -writer.writeYaml(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to Writable - -options = { - dest: fs.createWriteStream('result.yml'), - indent: 4 -} - -writer.writeYaml(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - - -### writer.writeJson(object, options) ⇒ Promise -Writes a JS object to a JSON destination. - -**Kind**: instance method of [Writer](#Writer) -**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). -**Access:** public -**See** - -- [MIN_INDENT](#Constants+MIN_INDENT) -- [DEFAULT_INDENT](#Constants+DEFAULT_INDENT) -- [MAX_INDENT](#Constants+MAX_INDENT) - - -| Param | Type | Description | -| --- | --- | --- | -| object | object | The JS object to write into JSON destination. | -| options | [Options](#Options) | The write destination and indention. | - -**Example** -```js -var Writer = require('jy-transform').Writer; -var logger = ...; -var writer = new Writer(logger); - -// ---- write obj to file - -var obj = {...}; -var options = { - dest: 'result.json', - indent: 2 -} - -writer.writeJson(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to Writable - -options = { - dest: fs.createWriteStream('result.json'), - indent: 4 -} - -writer.writeJson(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - -// ---- write obj to object - -options = { - dest: {}, - indent: 4 -} - -writer.writeJson(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - - -### writer.writeJs(object, options) ⇒ Promise -Writes a JS object to a JS destination. The object is prefixed by `module.exports = `. - -**Kind**: instance method of [Writer](#Writer) -**Returns**: Promise - - Containing the write success message to handle by caller (e.g. for logging). -**Access:** public -**See** - -- [MIN_INDENT](#Constants+MIN_INDENT) -- [DEFAULT_INDENT](#Constants+DEFAULT_INDENT) -- [MAX_INDENT](#Constants+MAX_INDENT) - - -| Param | Type | Description | -| --- | --- | --- | -| object | object | The JSON to write into JS destination. | -| options | [Options](#Options) | The write destination and indention. | - -**Example** -```js -var Writer = require('jy-transform').Writer; -var logger = ...; -var writer = new Writer(logger); - -// ---- write obj to file - -var obj = {...}; -var options = { - dest: 'result.js', - indent: 2 -} - -writer.writeJs(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to Writable - -options = { - dest: fs.createWriteStream('result.json'), - indent: 4 -} - -writer.writeJs(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); - - -// ---- write obj to object - -options = { - dest: {}, - indent: 2 -} - -writer.writeJs(obj, options) - .then(function (msg){ - logger.info(msg); - }) - .catch(function (err) { - logger.error(err.stack); - }); -``` - - -## Options : object -**Kind**: global typedef -**Properties** - -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| origin | string | "yaml" | The origin type. | -| target | string | "js" | The target type. | -| src | string | Readable | object | | The source (`string` type is treated as a file path). | -| dest | string | Writable | object | | The destination (`string` type is treated as a file path). | -| indent | number | 4 | The indention in files. | -| imports | string | | The exports name for reading from JS source file or objects only. | -| exports | string | | The exports name for usage in JS destination files only. | - diff --git a/readme/BADGES.md b/readme/BADGES.md index 6f08584..e03edb8 100644 --- a/readme/BADGES.md +++ b/readme/BADGES.md @@ -25,7 +25,7 @@ [is-issue-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/issue?style=flat-square [is-url]: http://issuestats.com/github/deadratfink/jy-transform -[waffle-image]: https://badge.waffle.io/deadratfink/jy-transform.png?label=ready&title=Ready&style=flat-square +[waffle-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat-square [waffle-url]: https://waffle.io/deadratfink/jy-transform [cocl-image]: https://img.shields.io/codeclimate/github/deadratfink/jy-transform.svg?style=flat-square diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index e05a5b0..a92113e 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -1,10 +1,9 @@ ## API in a Minute -```javascript -import { transform, read, write } from 'jy-transform'; - +### Read, Transform & Write from Source to Destination -// --- transform from source to destination --- +```javascript +import { transform } from 'jy-transform'; const transformOptions = { src: 'foo/bar.yaml', @@ -24,21 +23,25 @@ try { } catch (err) { console.error(err.stack); } +``` +### Read into JS object from particular Source (File, Stream or JS Object) only -// --- read into JS object from particular source (file, stream or JS object) --- - -let object; +```javascript +import { read } from 'jy-transform'; try { - object = await read({ src: 'foo/bar.yaml' }); // here: read from file + const object = await read({ src: 'foo/bar.yaml' }); // here: read from file console.log(JSON.stringify(object)); } catch (err) { console.error(err.stack); } +``` +### Write JS object to particular Destination only -// --- write a JS object to particular destination --- +```javascript +import { write } from 'jy-transform'; try { const msg = await write(object, { dest: 'foo/bar.yaml' }); @@ -59,8 +62,7 @@ types into each other format. ## Usage The module can be used on CLI or as API (the latter is fully -[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)) -based). +[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) based). ### Usage Types @@ -77,13 +79,10 @@ So, what are the typical use cases for this module? In terms of _transformation_ these consists of different phases: - Reading files (`Reader`) -- Transforming JSON objects (`Transformer`) -- Apply dedicated actions on the intermediate JSON objects (`Transformer` + `Middleware`) +- Transforming JSON objects (`Transformer`) or apply dedicated actions on the intermediate JSON objects (`Transformer` + `Middleware`) - Writing files (`Writer`) -#### Reading - -From: +#### Reading From - _*.yaml_ file - _*.js_ file @@ -123,9 +122,7 @@ while: As mentioned above a _middleware_ can apply particular actions on the intermediate JS object via injected functions. This is an optional part for [transformation](#transformation) phase. -#### Writing - -To: +#### Writing To - _*.yaml_ file - _*.js_ file @@ -456,7 +453,7 @@ async function transform(options, middleware) #### Options -The `options` object has to follow this key-values table: +The `options` object has to follow this key-values tables: ##### Reader Options @@ -476,8 +473,6 @@ The `options` object has to follow this key-values table: | `imports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | | `exports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | -**NOTE:** an invalid indention setting (1 > indent > 8) does not raise an error but a default of 2 SPACEs is applied instead. - ##### Transformer Options These are a combination of the [Reader](#reader-options) and [Writer](#writer-options) Options. @@ -625,8 +620,7 @@ wiki which describes the full API and provides more examples. ## Contributing -Pull requests and stars are always welcome. Anybody is invited to take part -into this project. For bugs and feature requests, please create an +Pull requests and stars are always welcome. For bugs and feature requests, please create an [issue](https://github.com/deadratfink/jy-transform/issues). See the wiki [Contributing](https://github.com/deadratfink/jy-transform/wiki/Contributing) section for more details about conventions. diff --git a/src/cli.js b/src/cli.js index 6f6d9e2..4459d74 100755 --- a/src/cli.js +++ b/src/cli.js @@ -1,6 +1,17 @@ import path from 'path'; import cli from 'cli'; -import { DEFAULT_INDENT, DEFAULT_OPTIONS, TYPE_JS, TYPE_JSON, TYPE_YAML } from './constants'; +import Package from '../package.json'; +import { + DEFAULT_INDENT, + DEFAULT_FORCE_FILE_OVERWRITE, + DEFAULT_JS_IMPORTS_IDENTIFIER, + DEFAULT_JS_EXPORTS_IDENTIFIER, + ORIGIN_DESCRIPTION, + TARGET_DESCRIPTION, + TYPE_JS, + TYPE_JSON, + TYPE_YAML +} from './constants'; import { transform } from './transformer'; /** @@ -19,7 +30,7 @@ import { transform } from './transformer'; * @type {string} * @private */ -const usage = path.basename(__filename) + ' INPUT-FILE [OUTPUT-FILE] [OPTIONS]'; +const usage = Object.keys(Package.bin)[0] + ' INPUT-FILE [OUTPUT-FILE] [OPTIONS]'; /** * The path to package.json. @@ -39,27 +50,22 @@ const packagePath = path.join(__dirname, '../package.json'); * @private */ const options = { - origin: [ - 'o', 'The origin type of INPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML - + ' ]', 'string', DEFAULT_OPTIONS.origin], - target: [ - 't', 'The target type of OUTPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML - + ' ]', 'string', DEFAULT_OPTIONS.target], + origin: ['o', 'The origin type of INPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + + ' ]', 'string', ORIGIN_DESCRIPTION], + target: ['t', 'The target type of OUTPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + + ' ]', 'string', TARGET_DESCRIPTION], indent: ['i', 'The indention for pretty-print: 1 - 8', 'int', DEFAULT_INDENT], - force: [ - 'f', 'Force overwriting of existing output files on write phase: when files are not overwritten (which' + + force: ['f', 'Force overwriting of existing output files on write phase: when files are not overwritten (which' + ' is default), then the next transformation with same output file name gets a consecutive number on the base' + - ' file name, e.g. in case of foo.yaml it would be foo(1).yaml'], - imports: [ - 'm', 'Define a \'module.exports[.identifier] = \' identifier (to read from JS _source_ file only, must' + - ' be a valid JS identifier!)', 'string', DEFAULT_OPTIONS.imports], - exports: [ - 'x', 'Define a \'module.exports[.identifier] = \' identifier (for usage in JS destination file only,' + - ' must be a valid JS identifier!)', 'string', DEFAULT_OPTIONS.exports] + ' file name, e.g. in case of foo.yaml it would be foo(1).yaml', 'boolean', DEFAULT_FORCE_FILE_OVERWRITE], + imports: ['m', 'Define a \'module.exports[.identifier] = \' identifier for object (to read from JS source file ' + + 'only, must be a valid JS identifier!)', 'string', DEFAULT_JS_IMPORTS_IDENTIFIER], + exports: ['x', 'Define a \'module.exports[.identifier] = \' identifier for object (to write in JS destination file ' + + 'only, must be a valid JS identifier!)', 'string', DEFAULT_JS_EXPORTS_IDENTIFIER] }; /** - * Prints the error to console and exit with 1. + * Prints the error to console and exits process with 1. * * @param {string|Error} err - The error to print. * @private @@ -81,7 +87,7 @@ function error(err) { * * @param {Array} args - The first mandatory argument is the input file (`args[0]`), the second (optional) * argument is the output file (`args[1]`). - * @param {module:jy-transform:type-definitions~Options} cliOptions - The options provided via CLI. + * @param {module:jy-transform:type-definitions~TransformerOptions} cliOptions - The options provided via CLI. * @private */ function main(args, cliOptions) { @@ -100,6 +106,17 @@ function main(args, cliOptions) { cli.debug('output file not specified, using default'); } + // We have to set to undefined if the values are not given on CLI or else Joi will fail + // because it will receive the description created in options object above! + if (cliOptions.origin === ORIGIN_DESCRIPTION) { + cliOptions.origin = undefined; + } + if (cliOptions.target === TARGET_DESCRIPTION) { + cliOptions.target = undefined; + } + + cli.debug('Passed options:\n' + JSON.stringify(cliOptions, null, 4)); + // transform with options return transform(cliOptions) diff --git a/src/constants.js b/src/constants.js index 9dcd9dc..5ed1626 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,12 +1,11 @@ /** * @module jy-transform:constants * @description Useful constants used for the module and its usage. - * @private + * @public */ /** * The 'utf8' constant. - * * @type {string} * @constant * @private @@ -14,8 +13,7 @@ export const UTF8 = 'utf8'; /** - * The 'yaml' type constant. - * + * The `'yaml'` type constant. * @type {string} * @constant * @public @@ -23,7 +21,7 @@ export const UTF8 = 'utf8'; export const TYPE_YAML = 'yaml'; /** - * The 'json' type constant. + * The `'json'` type constant. * @type {string} * @constant * @public @@ -31,7 +29,7 @@ export const TYPE_YAML = 'yaml'; export const TYPE_JSON = 'json'; /** - * The 'js' type constant. + * The `'js'` type constant. * @type {string} * @constant * @public @@ -44,7 +42,7 @@ export const TYPE_JS = 'js'; * @type {{yml: string, yaml: string, js: string, json: string}} * @private */ -export const TYPE_MAP = { +export const EXT_TO_TYPE_MAP = { yml: TYPE_YAML, yaml: TYPE_YAML, js: TYPE_JS, @@ -60,13 +58,21 @@ export const TYPE_MAP = { export const DEFAULT_INDENT = 2; /** - * The minimum file indention (0 SPACE). + * The minimum file indention (0 SPACE) fo JS and JSON types. * @type {number} * @constant * @private */ export const MIN_INDENT = 0; +/** + * The minimum file indention (0 SPACE) for YAML types. + * @type {number} + * @constant + * @private + */ +export const MIN_YAML_INDENT = 2; + /** * The maximum file indention (8 SPACEs). * @type {number} @@ -142,29 +148,3 @@ export const DEFAULT_JS_IMPORTS_IDENTIFIER = undefined; * @constant */ export const DEFAULT_JS_EXPORTS_IDENTIFIER = undefined; - -/** - * The default options. - * @constant - * @namespace - * @property {string} origin=yaml - The default origin type. - * @property {string} target=js - The default target type. - * @property {string} dest=relative_to_input_file - The default dest description. - * @property {number} indent=4 - The default indention for files. - * @property {boolean} force=false - Whether to overwrite existing file on output. - * @property {string} imports=undefined - The exports name for reading from JS source file or objects only. - * @property {string} exports=undefined - The exports name for usage in JS file or object only. - * @see {@link ORIGIN_DESCRIPTION} - * @see {@link TARGET_DESCRIPTION} - * @see {@link DEST_DESCRIPTION} - * @private - */ -export const DEFAULT_OPTIONS = { - origin: ORIGIN_DESCRIPTION, - target: TARGET_DESCRIPTION, - dest: DEST_DESCRIPTION, - indent: DEFAULT_INDENT, - force: DEFAULT_FORCE_FILE_OVERWRITE, - imports: DEFAULT_JS_IMPORTS_IDENTIFIER, - exports: DEFAULT_JS_EXPORTS_IDENTIFIER, -}; diff --git a/src/reader.js b/src/reader.js index 121e66e..368114a 100644 --- a/src/reader.js +++ b/src/reader.js @@ -18,7 +18,7 @@ import { /** * @module jy-transform:reader * @description This module provides the _read_ functionality for YAML, JS or JSON sources. - * @public + * @private */ /** @@ -44,14 +44,14 @@ function readFromStream(readable, origin) { .on('end', () => { const buffer = Buffer.concat(buffers); try { - logger.debug(origin + ' reading from Readable'); + // logger.debug(origin + ' reading from Readable'); TODO remove if (origin === TYPE_JSON || origin === TYPE_JS) { resolve(JSON.parse(buffer.toString(UTF8))); } else { // HINT: commented (see below): if (origin === YAML) { resolve(jsYaml.safeLoad(buffer.toString(UTF8))); } } catch (err) { // probably a SyntaxError for JSON or a YAMLException - logger.error('Unexpected error: ' + err.stack); + // logger.error('Unexpected error: ' + err.stack); TODO remove readable.emit('error', err); // send to .on('error',... } }); @@ -61,8 +61,7 @@ function readFromStream(readable, origin) { /** * Reads the data from a given JS or JSON source. * - * @param {module:jy-transform:type-definitions~ReaderOptions} options - Contains the JS/JSON source reference - * to read from. + * @param {ReaderOptions} options - Contains the JS/JSON source reference to read from. * @returns {Promise.} Contains the read JS object. * @private */ @@ -73,8 +72,8 @@ async function readJs(options) { if (options.imports) { // eslint-disable-next-line import/no-dynamic-require, global-require const object = require(resolvedPath)[options.imports]; - logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(object, null, 4)); - if (!object) { + // logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(object, null, 4)); TODO remove + if (!object) { // TODO check this as part of config validation? throw new Error('an identifier string \'' + options.imports + '\' was specified for JS object' + ' but could not find this object, pls ensure that file ' + options.src + ' contains it.'); } else { @@ -85,22 +84,22 @@ async function readJs(options) { return require(resolvedPath); // reads both: JS and JSON! } } catch (err) { // probably a SyntaxError - logger.error('Unexpected error: ' + err.stack); + // logger.error('Unexpected error: ' + err.stack); TODO remove throw err; } } else if (isStream.readable(options.src)) { - return await readFromStream(options.src, TYPE_JSON); // reads both: JS or JSON! + return readFromStream(options.src, TYPE_JSON); // reads both: JS or JSON! } else if (options.imports) { // options.src is JS object here! const subObject = options.src[options.imports]; - logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(subObject, null, 4)); + // logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(subObject, null, 4)); TODO remove if (!subObject) { throw new Error('an identifier string \'' + options.imports + '\' was specified for JS object ' + 'but could not find this object, pls ensure that object source contains it.'); } else { return Object.assign({}, subObject); // clone, do not alter original object! } - } else { // // options.src is JS object here! - return Object.assign({}, options.src); // clone, do not alter original object! + } else { // options.src is JS object here! + return Object.assign({}, options.src); // clone, do not alter original object! } } @@ -109,8 +108,7 @@ async function readJs(options) { * *NOTE:* this function does not understand multi-document sources, it throws * exception on those. * - * @param {module:jy-transform:type-definitions~ReaderOptions} options - Contains the YAML source reference - * to read from. + * @param {ReaderOptions} options - Contains the YAML source reference to read from. * @returns {Promise.} Contains the read JS object. * @private */ @@ -118,16 +116,16 @@ async function readYaml(options) { if (typeof options.src === 'string') { // load source from YAML file const yaml = await fsPromisified.readFile(options.src, UTF8); - logger.debug('YAML loaded from file ' + options.src); + // logger.debug('YAML loaded from file ' + options.src); TODO remove try { return jsYaml.safeLoad(yaml); } catch (err) { // probably a YAMLException - logger.error('Unexpected error: ' + err.stack); + // logger.error('Unexpected error: ' + err.stack); TODO remove throw err; } } // as validation has passed already, this can only be stream here - return await readFromStream(options.src, TYPE_YAML); + return readFromStream(options.src, TYPE_YAML); } // /** @@ -151,8 +149,11 @@ async function readYaml(options) { /** * Reads a particular content type from a source provided in the passed `options`. * - * @param {module:jy-transform:type-definitions~ReaderOptions} options - The read options. - * @returns {Promise.} Resolves with JS object result. + * @param {ReaderOptions} options - The read options. + * @returns {Promise} The result. + * @resolve {string} Resolves with JS object result. + * @reject {ValidationError} If any `options` validation occurs. + * @reject {Error} If any write error occurs. * @public * @example * import { read } from 'jy-transform'; @@ -184,9 +185,9 @@ export async function read(options) { switch (assertedOptions.origin) { case TYPE_JS: case TYPE_JSON: - return await readJs(assertedOptions); + return readJs(assertedOptions); default: - return await readYaml(assertedOptions); + return readYaml(assertedOptions); } } diff --git a/src/transformer.js b/src/transformer.js index fc32a88..663a81c 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -1,13 +1,25 @@ import logger from 'cli'; +import path from 'path'; +import fs from 'fs'; +import isStream from 'is-stream'; import { read } from './reader'; import { write } from './writer'; +import { EXT_TO_TYPE_MAP } from './constants'; /** * @module jy-transform:transformer * @description This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. - * @public + * @private */ +/** + * Applies the `middleware` function to `object` if is passed. + * + * @param {Object} object - The object to alter by passed `middleware` function. + * @param {Function} [middleware] - The function to alter `object`. + * @returns {Object} The passed `object` which could be altered by optional `middleware` function. + * @private + */ const callMiddlewareIfExists = async (object, middleware) => { if (middleware !== undefined && typeof middleware !== 'function') { throw new TypeError('The provided middleware is not a Function type'); @@ -18,28 +30,91 @@ const callMiddlewareIfExists = async (object, middleware) => { return middleware(object); }; +/** + * Turns the destination file name into a name containing a consecutive + * number if it exists. It iterates over the files until it finds a file + * name which does not exist. + * + * @param {string} dest - The destination file. + * @returns {string} - A consecutive file name or the original one if + * `dest` file does not exist. + * @private + */ +function getConsecutiveDestName(dest) { + let tmpDest = dest; + let i = 0; + const destDirName = path.dirname(tmpDest); + const ext = path.extname(tmpDest); + const basename = path.basename(tmpDest, ext); + while (fs.existsSync(tmpDest)) { + tmpDest = path.join(destDirName, basename + '(' + (i += 1) + ')' + ext); + } + return tmpDest; +} + +/** + * Checks if passed `object` is a file stream instance. + * + * @param {*} object - The object to check. + * @returns {boolean} A `true` if passed `object` is a file stream instance, else `false`. + * @private + */ +function isFileStream(object) { + return isStream(object) && object.path; +} + +/** + * Returns the passes `dest` value or an adapted destination path (the latter if `target` is defined an differs from + * destinations path extension). + * + * @param {string} dest - The destination path. + * @param {string} [target] - The target file type of destination. + * @returns {string} The `dest` value or an adapted destination path. + * @private + */ +function adaptTargetPathType(dest, target) { + if (target) { + const tmpDest = dest; + const destDirName = path.dirname(tmpDest); + const ext = path.extname(tmpDest); + const basename = path.basename(tmpDest, ext); + let destType = ext; + if (ext.charAt(0) === '.') { + destType = ext.substr(1); + } + if (EXT_TO_TYPE_MAP[destType] !== target) { + destType = target; + } + return path.join(destDirName, basename + '.' + destType); + } + return dest; +} + // //////////////////////////////////////////////////////////////////////////// // PUBLIC API // //////////////////////////////////////////////////////////////////////////// /** * The entry method for all transformation accepting a configuration object and - * an (optional) middleware function. It executes the transformation logic: + * an (optional) middleware function. It executes the transformation logic. + * * 1. Input (read) * 2. Transform [ + Middleware] * 3. Output (write). * - * @param {module:jy-transform:type-definitions~TransformerOptions} options - The configuration for a transformation. - * @param {Function} [middleware] - This middleware Promise can be used to + * @param {TransformerOptions} options - The configuration for a transformation. + * @param {Function} [middleware] - This middleware Promise can be used to * intercept the JSON object for altering the passed JSON, the function signature is: * ``` * async function(object) * ``` *

      * **NOTE:** the Promise has to return the processed JS object. - * @returns {Promise.} Containing the transformation result as message (e.g. to be logged by caller). - * @throws {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. - * @throws {Error} Will throw plain error when writing to _destination object_ failed due to any reason. + * @returns {Promise} The result. + * @resolve {string} With the transformation result as message (e.g. to be logged by caller). + * @reject {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. + * @reject {ValidationError} If any `options` validation occurs. + * @reject {Error} Will throw any error if read, transform or write operation failed due to any reason. * @public * @example * import { transform } from 'jy-transform'; @@ -67,10 +142,22 @@ const callMiddlewareIfExists = async (object, middleware) => { * }; */ export async function transform(options, middleware) { - logger.debug('transform'); + // logger.debug('transform'); TODO remove let object = await read(options); object = await callMiddlewareIfExists(object, middleware); - return await write(object, options); + + // Here we have to check the options.dest, if not set we allow to use the source (string as + // file path or File Writable with path) to be used as destination (and even allow to overwrite)! + if (!options.dest && (typeof options.src === 'string' || isFileStream(options.src))) { + if (options.force) { + // Overwrite the source if target is set and does not differ! + options.dest = adaptTargetPathType((options.src.path || options.src), options.target); + } else { + options.dest = getConsecutiveDestName(adaptTargetPathType((options.src.path || options.src), options.target)); + } + } + + return write(object, options); } export default { diff --git a/src/type-definitions.js b/src/type-definitions.js index 7e891fe..5f58538 100644 --- a/src/type-definitions.js +++ b/src/type-definitions.js @@ -1,9 +1,3 @@ -/** - * @module jy-transform:type-definitions - * @description The type definitions for this module. - * @public - */ - // ///////////////////////////////////////////////////////////////////////////// // EXTERNAL DEFINITIONS // ///////////////////////////////////////////////////////////////////////////// @@ -20,7 +14,7 @@ * @typedef ValidationError * @memberof external:joi * @see {@link hhttps://github.com/hapijs/joi/blob/v10.2.0/API.md#errors Joi errors} - * @private + * @public */ /** @@ -53,7 +47,7 @@ // ///////////////////////////////////////////////////////////////////////////// /** - * The configuration properties provided to the reader function. + * The configuration properties provided to the `read` function. * @typedef {object} ReaderOptions * @property {(string|Stream.Readable|object)} src - The source (if `string` type is treated as a file path). * @property {string} [origin=yaml] - The origin type. @@ -62,18 +56,18 @@ */ /** - * The writer configuration properties provided to the writer function. + * The writer configuration properties provided to the `write` function. * @typedef {object} WriterOptions - * @property {(string|Stream.Writable|object)} [dest] - The destination (if `string` type is treated as a file path). - * @property {string} [target=js] - The target type. - * @property {number} [indent=2] - The indention in files. - * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. - * @property {string} [force=false] - Force overwriting of existing output files on write phase. + * @property {(string|Stream.Writable|object)} dest - The destination (if `string` type is treated as a file path). + * @property {string} [target=js] - The target type. + * @property {number} [indent=2] - The indention value for pretty-print of output. + * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. + * @property {string} [force=false] - Force overwriting of existing output files on write phase. * @public */ /** - * The configuration properties provided to the transformer function. + * The configuration properties provided to the `transform` function. * @typedef {object} TransformerOptions * @property {(string|Stream.Readable|object)} src - The source (if `string` type is treated as a file path). * @property {string} [origin=yaml] - The origin type. @@ -81,8 +75,8 @@ * or objects only. * @property {(string|Stream.Writable|object)} [dest] - The destination (if `string` type is treated as a file path). * @property {string} [target=js] - The target type. + * @property {number} [indent=2] - The indention value for pretty-print of output. * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. - * @property {number} [indent=2] - The indention in files. * @property {string} [force=false] - Force overwriting of existing output files on write phase. * @public */ diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-helper.js index e3271d4..04bde7f 100644 --- a/src/validation/options-schema-helper.js +++ b/src/validation/options-schema-helper.js @@ -1,8 +1,7 @@ import path from 'path'; -import logger from 'cli'; import isStream from 'is-stream'; import { - TYPE_MAP, + EXT_TO_TYPE_MAP, DEFAULT_ORIGIN, DEFAULT_TARGET, } from '../constants'; @@ -31,7 +30,7 @@ const getTypeFromFilePath = (pathStr, defaultValue) => { if (ext.charAt(0) === '.') { ext = ext.substr(1); } - type = TYPE_MAP[ext]; + type = EXT_TO_TYPE_MAP[ext]; } if (!type) { type = defaultValue; diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js index 05f2c9d..457177b 100644 --- a/src/validation/options-schema.js +++ b/src/validation/options-schema.js @@ -11,6 +11,7 @@ import { DEFAULT_FORCE_FILE_OVERWRITE, DEFAULT_INDENT, MIN_INDENT, + MIN_YAML_INDENT, MAX_INDENT, } from '../constants'; @@ -39,7 +40,7 @@ export const readerOptionsSchema = Joi.object().keys({ Joi.object().type(Object), ) .required() - .description('The input source (if string type it is treated as a file path).'), + .description('The input source (if string type is treated as a file path).'), origin: Joi .when('src', { is: Joi.object().type(Stream.Readable), @@ -85,7 +86,7 @@ export const writerOptionsSchema = Joi.object().keys({ Joi.object().type(Object), ) .required() - .description('The output destination (if string type it is treated as a file path).'), + .description('The output destination (if string type is treated as a file path).'), target: Joi .when('dest', { is: Joi.object().type(Stream.Writable), @@ -113,12 +114,22 @@ export const writerOptionsSchema = Joi.object().keys({ .validEs6Identifier() .description('The name of property to export while writing a JS object to a JS output destination.'), indent: Joi - .number() - .integer() - .min(MIN_INDENT) - .max(MAX_INDENT) - .default(DEFAULT_INDENT) - .description('The indention for pretty-print.'), + .when('target', { + is: TYPE_YAML, + then: Joi + .number() + .integer() + .min(MIN_YAML_INDENT) // Must be 2 for YAML type! + .max(MAX_INDENT) + .default(DEFAULT_INDENT), + otherwise: Joi + .number() + .integer() + .min(MIN_INDENT) + .max(MAX_INDENT) + .default(DEFAULT_INDENT), + }) + .description('The indention value for pretty-print of output.'), force: Joi .boolean() .default(DEFAULT_FORCE_FILE_OVERWRITE) diff --git a/src/writer.js b/src/writer.js index 524b113..51285c3 100644 --- a/src/writer.js +++ b/src/writer.js @@ -21,7 +21,7 @@ import { writerOptionsSchema } from './validation/options-schema'; * @module jy-transform:writer * @description This module provides the _write_ functionality to write JS objects from memory to a JSON/JS/YAML * destination (file, object or {@link stream.Readable}). - * @public + * @private */ /** @@ -112,13 +112,13 @@ function getConsecutiveDestName(dest) { */ async function mkdirAndWrite(object, dest, target, forceOverwrite) { const destDir = path.dirname(dest); - logger.debug('Destination dir: ' + destDir); + // logger.debug('Destination dir: ' + destDir); TODO remove await mkdirp(destDir); - logger.debug('Destination dir ' + destDir + ' successfully written'); + // logger.debug('Destination dir ' + destDir + ' successfully written'); TODO remove let finalDestination = dest; if (forceOverwrite === undefined || forceOverwrite === false) { finalDestination = getConsecutiveDestName(dest); - logger.debug('Setting was: do not overwrite, using destination ' + finalDestination + '.'); + // logger.debug('Setting was: do not overwrite, using destination ' + finalDestination + '.'); TODO remove } await fsPromisified.writeFile(finalDestination, object, UTF8); return 'Writing \'' + target + '\' file \'' + finalDestination + '\' successful.'; @@ -140,7 +140,7 @@ async function mkdirAndWrite(object, dest, target, forceOverwrite) { */ function writeToFile(object, dest, target, forceOverwrite) { return new Promise((resolve, reject) => { - return fsPromisified.stat(dest) + fsPromisified.stat(dest) .then((stats) => { if (stats.isDirectory()) { reject(new Error('Destination file is a directory, pls specify a valid file resource!')); @@ -184,8 +184,8 @@ function writeToStream(object, dest, target) { /** * Writes a JS object to a YAML destination. * - * @param {Object} object - The JS object to write into YAML destination. - * @param {module:jy-transform:type-definitions~WriterOptions} options - The write destination and indention. + * @param {Object} object - The JS object to write into YAML destination. + * @param {WriterOptions} options - The write destination and indention. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} @@ -203,9 +203,9 @@ async function writeYaml(object, options) { } if (typeof options.dest === 'string') { // file - return await writeToFile(yaml, options.dest, TYPE_YAML, options.force); + return writeToFile(yaml, options.dest, TYPE_YAML, options.force); } else if (isStream.writable(options.dest)) { // stream - return await writeToStream(yaml, options.dest, TYPE_YAML); + return writeToStream(yaml, options.dest, TYPE_YAML); } // object options.dest = yaml; @@ -215,8 +215,8 @@ async function writeYaml(object, options) { /** * Writes a JS object to a JSON destination. * - * @param {Object} object - The JS object to write into JSON destination. - * @param {module:jy-transform:type-definitions~WriterOptions} options - The write destination and indention. + * @param {Object} object - The JS object to write into JSON destination. + * @param {WriterOptions} options - The write destination and indention. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} @@ -226,9 +226,9 @@ async function writeYaml(object, options) { async function writeJson(object, options) { const jsonString = await serializeJsToJsonString(object, options.indent); if (typeof options.dest === 'string') { // file - return await writeToFile(jsonString, options.dest, TYPE_JSON, options.force); + return writeToFile(jsonString, options.dest, TYPE_JSON, options.force); } else if (isStream.writable(options.dest)) { // stream - return await writeToStream(jsonString, options.dest, TYPE_JSON); + return writeToStream(jsonString, options.dest, TYPE_JSON); } // object options.dest = jsonString; @@ -238,8 +238,8 @@ async function writeJson(object, options) { /** * Writes a JS object to a JS destination. The object is prefixed by `module.exports[.${options.exports}] = `. * - * @param {Object} object - The JSON to write into JS destination. - * @param {module:jy-transform:type-definitions~WriterOptions} options - The write destination and indention. + * @param {Object} object - The JSON to write into JS destination. + * @param {WriterOptions} options - The write destination and indention. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} @@ -249,9 +249,9 @@ async function writeJson(object, options) { async function writeJs(object, options) { const data = await serializeJsToString(object, options.indent, options.exports); if (typeof options.dest === 'string') { // file - return await writeToFile(data, options.dest, TYPE_JS, options.force); + return writeToFile(data, options.dest, TYPE_JS, options.force); } else if (isStream.writable(options.dest)) { // stream - return await writeToStream(data, options.dest, TYPE_JS); + return writeToStream(data, options.dest, TYPE_JS); } // object let msg; @@ -268,9 +268,12 @@ async function writeJs(object, options) { /** * Writes the passe JS object to a particular destination described by the passed `options`. * - * @param {Object} object - The JS source object to write. - * @param {module:jy-transform:type-definitions~WriterOptions} options - The write options. - * @returns {Promise.} Resolves with write success message. + * @param {Object} object - The JS source object to write. + * @param {WriterOptions} options - The write options. + * @returns {Promise} The result. + * @resolve {string} With the write success message. + * @reject {Error} If any write error occurs. + * @reject {ValidationError} If any `options` validation occurs. * @public * @example * import { write } from 'jy-transform'; @@ -313,6 +316,7 @@ async function writeJs(object, options) { * .catch(console.error); */ export async function write(object, options) { + console.log('OPTIONS ON WRITE ===> ' + JSON.stringify(options, null, 4)) const validatedOptions = await Joi.validate(options, writerOptionsSchema); // HINT: we have to use the original options object because the caller must not loose the reference to options.dest, @@ -322,11 +326,11 @@ export async function write(object, options) { validatedOptions.dest = options.dest; switch (validatedOptions.target) { case TYPE_JSON: - return await writeJson(object, options); + return writeJson(object, options); case TYPE_YAML: - return await writeYaml(object, options); + return writeYaml(object, options); default: - return await writeJs(object, options); + return writeJs(object, options); } } diff --git a/test/logger.js b/test/logger.js index b15c174..87f1563 100644 --- a/test/logger.js +++ b/test/logger.js @@ -1,10 +1,4 @@ import winston from 'winston'; -// import fsExtra from 'fs-extra'; // TODO clean up this file! -// import promisify from 'promisify-es6'; -// // import toYAML from 'combarnea-winston-console-formatter'; -// -// const ensureDir = promisify(fsExtra.ensureDir); -// const emptyDir = promisify(fsExtra.emptyDir); /** * @module jy-transform:unit:logger @@ -91,17 +85,6 @@ const winstonConsoleOptions = { level: 'info' }; -// //////////////////////////////////////////////////////////////////////////// -// PREPARE TMP FOLDER -// //////////////////////////////////////////////////////////////////////////// - -// (async () => { -// await ensureDir(TEST_TMP_DIR); -// await emptyDir(TEST_TMP_DIR); -// })(); -// fsExtra.ensureDirSync(TEST_TMP_DIR); -// fsExtra.emptyDirSync(TEST_TMP_DIR); - // //////////////////////////////////////////////////////////////////////////// // PROTECTED EXPORTS // //////////////////////////////////////////////////////////////////////////// @@ -119,12 +102,6 @@ export const logger = new (winston.Logger)({ exitOnError: false }); -// logger.add(winston.transports.Console, toYAML.config({ -// // colors, // colorizer colors array -// noMeta: false, // boolean - not to print metadata, if true metadata will not be printed -// noStack: false // boolean - not to print stack trace, if true stack trace will not be printed -// })); - logger.info('Test-logger initialized, writing to ', winstonFileOptions.filename); export default { diff --git a/test/unit/test-cli.js b/test/unit/test-cli.js new file mode 100644 index 0000000..fe8f586 --- /dev/null +++ b/test/unit/test-cli.js @@ -0,0 +1,104 @@ +import { exec, execFile, spawn } from 'child_process'; + +import jsYaml from 'js-yaml'; +import promisify from 'promisify-es6'; +import fsExtra from 'fs-extra'; +import fs from 'fs'; +import cwd from 'cwd'; +import path from 'path'; +import { logger } from '../logger'; + +import { UTF8 } from '../../src/constants'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + + +const fsPromised = promisify(fs); +const execPromised = promisify(exec); + +/** + * @module jy-transform:unit-test:test-cli + * @description This unit test suite checks the correct transformation behaviour of the CLI interface. + * @private + */ + +const execJyt = async (args) => { + try { + console.log('CWD: ' + cwd()); + const command = './jyt ' + args.join(' '); + console.log('executing command: ' + command); + const result = await execPromised(command, { cwd: cwd(), encoding: 'utf8' }); + console.log(`stdout: ${JSON.stringify(result, null, 4)}`); + console.log(`stdout: ${result.stdout}`); + console.log(`stderr: ${result.stderr}`); + } catch (err) { + console.log(`stdout: ${err.stdout}`); + console.log(`stderr: ${err.stderr}`); + console.log(`exit code: ${err.code}`); + throw err; + } + + // const childProcess = exec('babel-node', args, { cwd: cwd(), encoding: 'utf8' }, (error, stdout, stderr) => { + // if (error) { + // console.error(`exec error: ${error}`); + // return; + // } + // console.log(`stdout: ${stdout}`); + // console.log(`stderr: ${stderr}`); + // }); + // console.log('childProcess.pid = ' + childProcess.pid); + // + // if (command.error !== null) { + // console.log('exec error: ', command.error); + // } + // command.stdout.pipe(process.stdout); + // return callback(command.output.toString()); +}; + +// function execJyt(args, callback) { +// var command = spawn(cmd, args); +// command +// .on('error', function( err ){ throw err }) +// var result = ''; +// command.stdout.on('data', function(data) { +// result += data.toString(); +// }); +// //command.stderr.on('data', function(data) { +// //result += data.toString(); +// //}); +// command.on('close', function(code) { +// return callback(result); +// }); +// +// if (command.error !== null) { +// console.log('exec error: ' + JSON.stringify(command.error, null, 4)); +// } +// } + +// run("ls", undefined, function(result) { console.log(result) }); + +// run("babel-node ./jyt", ['./inch.json', '-t yaml'], function(result) { console.log(result) }); + +//exec('./jyt inch.json inch.yml -i 1') + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { + const TEST_DATA_DIR = './test/data'; + const SRC_YAML = TEST_DATA_DIR + '/test-file.yaml'; + const EXPECTED_VALUE = 'bar'; + + /** + * Temporary base dir for writer test output. + * @type {string} + * @constant + * @private + */ + const CLI_TEST_BASE_DIR = './test/tmp/cli'; + + beforeAll(() => { + fsExtra.ensureDirSync(CLI_TEST_BASE_DIR); + fsExtra.emptyDirSync(CLI_TEST_BASE_DIR); + }); + + it('cli', async () => { + await execJyt(['./inch.json', CLI_TEST_BASE_DIR + '/inch.yaml', '-i 2', '-t yaml']); + }); +}); diff --git a/test/unit/test-reader.js b/test/unit/test-reader.js index b8ff935..e232f20 100644 --- a/test/unit/test-reader.js +++ b/test/unit/test-reader.js @@ -10,7 +10,7 @@ import { /** * @module jy-transform:unit-test:test-reader - * @description This unit test suite checks the validity and correctness of _./src/reader.js_ module. + * @description This unit test suite checks the validity and correctness of the Reader module. * @private */ @@ -67,12 +67,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { return expectReaderError(options, { name: 'ValidationError', isJoi: true }); }); - it('should read JS successfully', async () => - await expectReaderSuccess({ src: { myproperty: 'value' } }, 'myproperty', 'value') + it('should read JS successfully', () => + expectReaderSuccess({ src: { myproperty: 'value' } }, 'myproperty', 'value') ); - it('should read JS from file successfully', async () => - await expectReaderSuccess({ src: './test/data/test-data.js' }, 'myproperty', 'old value') + it('should read JS from file successfully', () => + expectReaderSuccess({ src: './test/data/test-data.js' }, 'myproperty', 'old value') ); it('should read JS from JS object successfully and both object references are different', async () => { @@ -134,8 +134,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { return expectReaderErrorByType(options, Error); }); - it('should read JSON from file successfully', async () => - await expectReaderSuccess({ src: './test/data/test-data.json' }, 'myproperty', 'old value') + it('should read JSON from file successfully', () => + expectReaderSuccess({ src: './test/data/test-data.json' }, 'myproperty', 'old value') ); it('should read JS from object successfully', async () => { @@ -195,96 +195,95 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { return expectReaderErrorByType(options, Error); }); - it('should read JSON from stream successfully', async () => - await expectReaderSuccess({ + it('should read JSON from stream successfully', () => + expectReaderSuccess({ origin: TYPE_JSON, src: fs.createReadStream('./test/data/test-data.json') }, 'myproperty', 'old value') ); - it('should read corrupted JSON from file path and fail by SyntaxError', () => { - return expectReaderErrorByType({ + it('should read corrupted JSON from file path and fail by SyntaxError', () => + expectReaderErrorByType({ origin: TYPE_JSON, src: './test/data/test-data-corrupted.json' - }, SyntaxError); - }); + }, SyntaxError) + ); - it('should read invalid JSON from file path and fail by SyntaxError', () => { - return expectReaderErrorByType({ + it('should read invalid JSON from file path and fail by SyntaxError', () => + expectReaderErrorByType({ origin: TYPE_JSON, src: './test/data/test-data-wrong-syntax.json' - }, SyntaxError); - }); + }, SyntaxError) + ); - it('should read corrupted JSON from stream and fail by SyntaxError', () => { - const options = { + it('should read corrupted JSON from stream and fail by SyntaxError', () => + expectReaderErrorByType({ origin: TYPE_JSON, src: fs.createReadStream('./test/data/test-data-corrupted.json'), - }; - return expectReaderErrorByType(options, SyntaxError); - }); + }, SyntaxError) + ); - it('should read invalid JSON from stream and fail by SyntaxError', () => { - return expectReaderErrorByType({ + it('should read invalid JSON from stream and fail by SyntaxError', () => + expectReaderErrorByType({ origin: TYPE_JSON, src: fs.createReadStream('./test/data/test-data-wrong-syntax.json') - }, SyntaxError); - }); + }, SyntaxError) + ); - it('should fail JS(ON) read by missing options', () => { - return expectReaderError(null, { name: 'ValidationError', isJoi: true }); - }); + it('should fail JS(ON) read by missing options', () => + expectReaderError(null, { name: 'ValidationError', isJoi: true }) + ); - it('should fail JS(ON) read by missing options.src', () => { - return expectReaderError({}, { name: 'ValidationError', isJoi: true }); - }); + it('should fail JS(ON) read by missing options.src', () => + expectReaderError({}, { name: 'ValidationError', isJoi: true }) + ); }); describe('Testing reading from YAML', () => { - it('should read YAML from file successfully', async () => - await expectReaderSuccess({ src: './test/data/test-data.yaml' }, 'myproperty', 'old value') + it('should read YAML from file successfully', () => + expectReaderSuccess({ src: './test/data/test-data.yaml' }, 'myproperty', 'old value') ); - it('should read JS from object successfully', async () => // TODO - await expectReaderSuccess({ src: { test: 'value' } }, 'test', 'value') + it('should read JS from object successfully', () => // TODO + expectReaderSuccess({ src: { test: 'value' } }, 'test', 'value') ); - it('should read YAML from stream successfully', async () => - await expectReaderSuccess({ + it('should read YAML from stream successfully', () => + expectReaderSuccess({ origin: TYPE_YAML, src: fs.createReadStream('./test/data/test-data.yaml'), }, 'myproperty', 'old value') ); - it('should read invalid YAML from file path and fail by YAMLException', () => { - return expectReaderErrorByType({ src: './test/data/test-data-wrong-syntax.yaml' }, YAMLException); - }); + it('should read invalid YAML from file path and fail by YAMLException', () => + expectReaderErrorByType({ src: './test/data/test-data-wrong-syntax.yaml' }, YAMLException) + ); - it('should read invalid YAML from stream and fail by YAMLException', () => { - return expectReaderErrorByType({ + it('should read invalid YAML from stream and fail by YAMLException', () => + expectReaderErrorByType({ origin: TYPE_YAML, src: fs.createReadStream('./test/data/test-data-wrong-syntax.yaml'), - }, YAMLException); - }); + }, YAMLException) + ); - it('should fail reading YAML by providing empty JS object as options.src', () => { - return expectReaderError({ src: {}, origin: TYPE_YAML }, { name: 'ValidationError', isJoi: true }); - }); + it('should fail reading YAML by providing empty JS object as options.src', () => + expectReaderError({ src: {}, origin: TYPE_YAML }, { name: 'ValidationError', isJoi: true }) + ); - it('should fail YAML reading by missing input options', () => { - return expectReaderError(null, { name: 'ValidationError', isJoi: true }); - }); + it('should fail YAML reading by missing input options', () => + expectReaderError(null, { name: 'ValidationError', isJoi: true }) + ); - it('should fail reading YAML by missing options.src', () => { - return expectReaderError({}, { name: 'ValidationError', isJoi: true }); - }); + it('should fail reading YAML by missing options.src', () => + expectReaderError({}, { name: 'ValidationError', isJoi: true }) + ); - it('should fail reading YAML from configured directory source', async () => - await expectReaderError({ src: './test/data' }, { name: 'ValidationError', isJoi: true }) + it('should fail reading YAML from configured directory source', () => + expectReaderError({ src: './test/data' }, { name: 'ValidationError', isJoi: true }) ); - it('should fail reading YAML from non-existing file', async () => - await expectReaderError({ src: './test/data/non-existing.yml' }, { + it('should fail reading YAML from non-existing file', () => + expectReaderError({ src: './test/data/non-existing.yml' }, { name: 'ValidationError', isJoi: true }) diff --git a/test/unit/test-transformer.js b/test/unit/test-transformer.js index 034184f..d7d5bd8 100644 --- a/test/unit/test-transformer.js +++ b/test/unit/test-transformer.js @@ -12,7 +12,7 @@ const fsPromised = promisify(fs); /** * @module jy-transform:unit-test:test-transformer - * @description This unit test suite checks the correct transformation behaviour of {@link Transformer} class. + * @description This unit test suite checks the correct transformation behaviour of the Transformer module. * @private */ @@ -52,9 +52,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { * * @param {Object} json - To transform. */ - const middlewareFunc = (json) => { + const middlewareFunc = async (json) => { json.foo = EXPECTED_VALUE; - return Promise.resolve(json); + return json; }; /** @@ -80,30 +80,17 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { * * @param {Object} options - The transformation options. * @param {Function} middleware - The transformation middleware. - * @param {Function} done - Test finished callback. */ - function assertYamlTransformSuccess(options, middleware, done) { - return transform(options, middleware) - .then((msg) => { - logger.info(msg); - const stats = fsExtra.statSync(options.dest); - expect(stats.isFile()).toBeTruthy(); - return fsPromised.readFile(options.dest, UTF8) - .then((yaml) => { - try { - const json = jsYaml.safeLoad(yaml); - expect(json.foo).toBe(EXPECTED_VALUE); - return done(); - } catch (err) { // probably a YAMLException - logger.error(err.stack); - return done(err); - } - }); - }) - .catch((err) => { - logger.error(err.stack); - done(err); - }); + async function assertYamlTransformSuccess(options, middleware) { + expect.assertions(3); + const msg = await transform(options, middleware); + logger.info(msg); + expect(msg).toEqual(expect.any(String)); + const stats = fsExtra.statSync(options.dest); + expect(stats.isFile()).toBeTruthy(); + const yaml = await fsPromised.readFile(options.dest, UTF8); + const object = jsYaml.safeLoad(yaml); + expect(object.foo).toBe(EXPECTED_VALUE); } describe('Testing transform with middleware', () => { @@ -305,13 +292,13 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { const SRC = './test/data/test-file.json'; const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-json-yaml.yaml'; - it('should store ' + SRC + ' file to ' + DEST, (done) => { + it('should store ' + SRC + ' file to ' + DEST, async () => { expect.assertions(2); const options = { src: path.resolve(SRC), dest: path.resolve(DEST), }; - assertYamlTransformSuccess(options, middlewareFunc, done); + await assertYamlTransformSuccess(options, middlewareFunc); }); }); diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js index 79667ab..26b2b5b 100644 --- a/test/unit/test-writer.js +++ b/test/unit/test-writer.js @@ -16,7 +16,7 @@ const fsPromised = promisify(fs); /** * @module jy-transform:unit-test:test-writer - * @description This unit test suite checks the validity and correctness of _./src/js_ module. + * @description This unit test suite checks the validity and correctness of Writer module. * @private */ diff --git a/test/unit/validation/test-joi-extensions-file-helper.js b/test/unit/validation/test-joi-extensions-file-helper.js index 02995d3..11b8e63 100644 --- a/test/unit/validation/test-joi-extensions-file-helper.js +++ b/test/unit/validation/test-joi-extensions-file-helper.js @@ -10,19 +10,19 @@ import { isExistingFile } from '../../../src/validation/joi-extensions-file-help describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-file-helper - ', () => { describe('Method isExistingFile(pathStr) ', () => { it('should return true on relative path string with existing file', () => - expect(isExistingFile('test/unit/validation/test-joi-extensions-file-helper.js')).toBeTruthy() + expect(isExistingFile('test/unit/validation/test-joi-extensions-file-helper.js')).toBe(true) ); it('should return true on relative path string with existing file starting with \'./\'', () => - expect(isExistingFile('./test/unit/validation/test-joi-extensions-file-helper.js')).toBeTruthy() + expect(isExistingFile('./test/unit/validation/test-joi-extensions-file-helper.js')).toBe(true) ); it('should return false on incorrect path string with non-existing file', () => - expect(isExistingFile('/foo/bar/non-exist.html')).toBeFalsy() + expect(isExistingFile('/foo/bar/non-exist.html')).toBe(false) ); it('should return false on existing directory path string', () => - expect(isExistingFile('./test')).toBeFalsy() + expect(isExistingFile('./test')).toBe(false) ); }); }); diff --git a/test/unit/validation/test-joi-extensions-identifier-helper.js b/test/unit/validation/test-joi-extensions-identifier-helper.js index df18fca..c9b76d2 100644 --- a/test/unit/validation/test-joi-extensions-identifier-helper.js +++ b/test/unit/validation/test-joi-extensions-identifier-helper.js @@ -10,16 +10,16 @@ import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-identifier-helper - ', () => { const nonStringIdentifier = {}; it('should validate non-string identifier \'' + JSON.toString(nonStringIdentifier) + '\' to false', () => - expect(isValidEs6Identifier(nonStringIdentifier)).toBeFalsy() + expect(isValidEs6Identifier(nonStringIdentifier)).toBe(false) ); const invalidIdentifier = '#3/-'; it('should validate invalid identifier \'' + invalidIdentifier + '\' to false', () => - expect(isValidEs6Identifier(invalidIdentifier)).toBeFalsy() + expect(isValidEs6Identifier(invalidIdentifier)).toBe(false) ); const validIdentifier = 'bar'; it('should validate \'' + validIdentifier + '\' identifier to true', () => - expect(isValidEs6Identifier(validIdentifier)).toBeTruthy() + expect(isValidEs6Identifier(validIdentifier)).toBe(true) ); }); diff --git a/test/unit/validation/test-options-schema-helper.js b/test/unit/validation/test-options-schema-helper.js index 6c699ad..fdcf53c 100644 --- a/test/unit/validation/test-options-schema-helper.js +++ b/test/unit/validation/test-options-schema-helper.js @@ -18,7 +18,6 @@ import { * @private */ -// TODO write all tests describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { describe('Function inferOriginDefault', () => { it('should infer the correct origin from relative path string with existing file having a known file type', () => @@ -77,7 +76,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { })).toBe(TYPE_YAML) ); - it('should infer the default target from relative path string with existing file having an unknown file type', () => + it('should infer the default target from relative path string with existing file having an' + + 'unknown stream file type', () => expect(inferTargetDefault({ dest: fs.createWriteStream('test/data/writable-test-dummy.txt'), })).toBe(DEFAULT_TARGET) diff --git a/test/unit/validation/test-options-schema.js b/test/unit/validation/test-options-schema.js index 8782cb0..9782ab7 100644 --- a/test/unit/validation/test-options-schema.js +++ b/test/unit/validation/test-options-schema.js @@ -30,8 +30,8 @@ import Joi from '../../../src/validation/joi-extensions'; /** * Expect a `ValidationError` for a given options function. * - * @param {Options} invalidOptions - The options which potentially produce the error. - * @param {Schema} schema - The validation schema. + * @param {ReaderOptions|WriterOptions} invalidOptions - The options which potentially produce the error. + * @param {Schema} schema - The validation schema. * @private */ function expectOptionsValidationError(invalidOptions, schema) { @@ -45,8 +45,8 @@ function expectOptionsValidationError(invalidOptions, schema) { /** * Expect a validation success for a given options. * - * @param {Options} validOptions - The options which should be correct. - * @param {Schema} schema - The validation schema. + * @param {ReaderOptions|WriterOptions} validOptions - The options which should be correct. + * @param {Schema} schema - The validation schema. * @private */ function expectOptionsValidationSuccess(validOptions, schema) { @@ -56,12 +56,12 @@ function expectOptionsValidationSuccess(validOptions, schema) { describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { describe('Testing readerOptionsSchema validation', () => { - it('should reject when options is missing (null)', async () => - await expectOptionsValidationError(null, readerOptionsSchema) + it('should reject when options is missing (null)', () => + expectOptionsValidationError(null, readerOptionsSchema) ); - it('should reject when options is missing (undefined)', async () => - await expectOptionsValidationError(undefined, readerOptionsSchema) + it('should reject when options is missing (undefined)', () => + expectOptionsValidationError(undefined, readerOptionsSchema) ); it('should set all defaults', async () => { @@ -78,7 +78,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { it('should infer options.origin from file type', async () => { expect.assertions(1); const options = { - src: './test/data/test-data.js', // non default type + src: './test/data/test-data.js', // non default type }; const validatedOptions = await Joi.validate(options, readerOptionsSchema); expect(validatedOptions.origin).toBe(TYPE_JS); @@ -88,7 +88,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { expect.assertions(1); const options = { origin: TYPE_JS, - src: new Stream.Readable(), // no inference possible + src: new Stream.Readable(), // no inference possible }; const validatedOptions = await Joi.validate(options, readerOptionsSchema); expect(validatedOptions.origin).toBe(TYPE_JS); @@ -106,19 +106,19 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { }); describe('Testing options.src schema validation', () => { - it('should reject when options.src is an existing directory path string', async () => - await expectOptionsValidationError({ + it('should reject when options.src is an existing directory path string', () => + expectOptionsValidationError({ src: './test', dest: 'some-file', }, readerOptionsSchema) ); - it('should reject when options.src is undefined', async () => - await expectOptionsValidationError({ dest: 'some-file' }, readerOptionsSchema) + it('should reject when options.src is undefined', () => + expectOptionsValidationError({ dest: 'some-file' }, readerOptionsSchema) ); - it('should reject when options.src is null', async () => - await expectOptionsValidationError({ + it('should reject when options.src is null', () => + expectOptionsValidationError({ src: null, dest: 'some-file', }, readerOptionsSchema) @@ -148,40 +148,40 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); }); - it('should resolve when options.origin has valid target ' + TYPE_JS, async () => { - await expectOptionsValidationSuccess({ + it('should resolve when options.origin has valid target ' + TYPE_JS, () => { + expectOptionsValidationSuccess({ src: './test/data/test-data', dest: 'some-file', origin: TYPE_JS, }, readerOptionsSchema); }); - it('should resolve when options.origin has valid target ' + TYPE_JSON, async () => { - await expectOptionsValidationSuccess({ + it('should resolve when options.origin has valid target ' + TYPE_JSON, () => { + expectOptionsValidationSuccess({ src: './test/data/test-data-json', dest: 'some-file', origin: TYPE_JSON, }, readerOptionsSchema); }); - it('should resolve when options.origin has valid target ' + TYPE_YAML, async () => { - await expectOptionsValidationSuccess({ + it('should resolve when options.origin has valid target ' + TYPE_YAML, () => { + expectOptionsValidationSuccess({ src: './test/data/test-data-yaml', dest: 'some-file', origin: TYPE_YAML, }, readerOptionsSchema); }); - it('should reject when options.origin is null', async () => - await expectOptionsValidationError({ + it('should reject when options.origin is null', () => + expectOptionsValidationError({ src: './test/data/test-data-yaml', dest: 'some-file', origin: null, }, readerOptionsSchema) ); - it('should reject when options.origin is not allowed value', async () => - await expectOptionsValidationError({ + it('should reject when options.origin is not allowed value', () => + expectOptionsValidationError({ src: './test/data/test-data-yaml', dest: 'some-file', origin: 'not-allowed', @@ -191,8 +191,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { describe('Testing options.imports schema validation', () => { const nonStringIdentifier = {}; - it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', async () => - await expectOptionsValidationError({ + it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', imports: nonStringIdentifier, @@ -200,8 +200,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { ); const invalidIdentifier = '#3/-'; - it('should reject invalid identifier \'' + invalidIdentifier + '\'', async () => - await expectOptionsValidationError({ + it('should reject invalid identifier \'' + invalidIdentifier + '\'', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', imports: invalidIdentifier, @@ -209,8 +209,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { ); const validIdentifier = 'bar'; - it('should accept valid \'' + validIdentifier + '\' identifier', async () => - await expectOptionsValidationSuccess({ + it('should accept valid \'' + validIdentifier + '\' identifier', () => + expectOptionsValidationSuccess({ src: './test/data/test-data.js', dest: 'some-file', imports: validIdentifier, @@ -220,12 +220,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { }); describe('Testing writerOptionsSchema validation', () => { - it('should reject when options is missing (null)', async () => - await expectOptionsValidationError(null, writerOptionsSchema) + it('should reject when options is missing (null)', () => + expectOptionsValidationError(null, writerOptionsSchema) ); - it('should reject when options is missing (undefined)', async () => - await expectOptionsValidationError(undefined, writerOptionsSchema) + it('should reject when options is missing (undefined)', () => + expectOptionsValidationError(undefined, writerOptionsSchema) ); it('should set all defaults', async () => { @@ -243,7 +243,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { it('should infer options.target from file type', async () => { expect.assertions(1); const options = { - dest: 'some-file.yml', // non default type + dest: 'some-file.yml', // non default type }; const validatedOptions = await Joi.validate(options, writerOptionsSchema); expect(validatedOptions.target).toBe(TYPE_YAML); @@ -253,7 +253,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { expect.assertions(1); const options = { target: TYPE_YAML, - dest: new Stream.Writable(), // no inference possible + dest: new Stream.Writable(), // no inference possible }; const validatedOptions = await Joi.validate(options, writerOptionsSchema); expect(validatedOptions.target).toBe(TYPE_YAML); @@ -269,12 +269,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { }); describe('Testing options.dest schema validation', () => { - it('should reject when options.dest is undefined', async () => - await expectOptionsValidationError({ src: './test/data/test-data.js' }, writerOptionsSchema) + it('should reject when options.dest is undefined', () => + expectOptionsValidationError({ src: './test/data/test-data.js' }, writerOptionsSchema) ); - it('should reject when options.dest is null', async () => - await expectOptionsValidationError({ + it('should reject when options.dest is null', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: null, }, writerOptionsSchema) @@ -320,40 +320,40 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { expect(validatedOptions.target).toBe(DEFAULT_TARGET); }); - it('should resolve when options.target has valid target ' + TYPE_JS, async () => { - await expectOptionsValidationSuccess({ + it('should resolve when options.target has valid target ' + TYPE_JS, () => { + expectOptionsValidationSuccess({ src: './test/data/test-data.js', dest: 'some-file', target: TYPE_JS, }, writerOptionsSchema); }); - it('should resolve when options.target has valid target ' + TYPE_JSON, async () => { - await expectOptionsValidationSuccess({ + it('should resolve when options.target has valid target ' + TYPE_JSON, () => { + expectOptionsValidationSuccess({ src: './test/data/test-data.json', dest: 'some-file', target: TYPE_JS, }, writerOptionsSchema); }); - it('should resolve when options.target has valid target ' + TYPE_YAML, async () => { - await expectOptionsValidationSuccess({ + it('should resolve when options.target has valid target ' + TYPE_YAML, () => { + expectOptionsValidationSuccess({ src: './test/data/test-data.yaml', dest: 'some-file', target: TYPE_YAML, }, writerOptionsSchema); }); - it('should reject when options.target is null', async () => - await expectOptionsValidationError({ + it('should reject when options.target is null', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', target: null, }, writerOptionsSchema) ); - it('should reject when options.target is not allowed value', async () => - await expectOptionsValidationError({ + it('should reject when options.target is not allowed value', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', target: 'not-allowed', @@ -363,8 +363,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { describe('Testing options.exports schema validation', () => { const nonStringIdentifier = {}; - it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', async () => - await expectOptionsValidationError({ + it('should reject non-string identifier \'' + stringify(nonStringIdentifier) + '\'', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', exports: nonStringIdentifier, @@ -372,8 +372,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { ); const invalidIdentifier = '#3/-'; - it('should reject invalid identifier \'' + invalidIdentifier + '\'', async () => - await expectOptionsValidationError({ + it('should reject invalid identifier \'' + invalidIdentifier + '\'', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', exports: invalidIdentifier, @@ -381,8 +381,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { ); const validIdentifier = 'bar'; - it('should accept valid \'' + validIdentifier + '\' identifier', async () => - await expectOptionsValidationSuccess({ + it('should accept valid \'' + validIdentifier + '\' identifier', () => + expectOptionsValidationSuccess({ src: './test/data/test-data.js', dest: 'some-file', exports: validIdentifier, @@ -392,24 +392,24 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { describe('Testing options.force schema validation', () => { const notBoolean = {}; - it('should reject non-boolean value \'' + stringify(notBoolean) + '\'', async () => - await expectOptionsValidationError({ + it('should reject non-boolean value \'' + stringify(notBoolean) + '\'', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', force: notBoolean, }, writerOptionsSchema) ); - it('should accept valid value \'false\'', async () => - await expectOptionsValidationSuccess({ + it('should accept valid value \'false\'', () => + expectOptionsValidationSuccess({ src: './test/data/test-data.js', dest: 'some-file', force: false, }, writerOptionsSchema) ); - it('should accept valid value \'true\'', async () => - await expectOptionsValidationSuccess({ + it('should accept valid value \'true\'', () => + expectOptionsValidationSuccess({ src: './test/data/test-data.js', dest: 'some-file', force: true, @@ -419,40 +419,40 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { describe('Testing options.indent schema validation', () => { const notInteger = 0.5; - it('should reject non-integer value \'' + stringify(notInteger) + '\'', async () => - await expectOptionsValidationError({ + it('should reject non-integer value \'' + stringify(notInteger) + '\'', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', indent: notInteger, }, writerOptionsSchema) ); - it('should accept valid \'' + MIN_INDENT + '\'', async () => - await expectOptionsValidationSuccess({ + it('should accept valid \'' + MIN_INDENT + '\'', () => + expectOptionsValidationSuccess({ src: './test/data/test-data.js', dest: 'some-file', indent: MIN_INDENT, }, writerOptionsSchema) ); - it('should accept valid \'' + MAX_INDENT + '\'', async () => - await expectOptionsValidationSuccess({ + it('should accept valid \'' + MAX_INDENT + '\'', () => + expectOptionsValidationSuccess({ src: './test/data/test-data.js', dest: 'some-file', indent: MAX_INDENT, }, writerOptionsSchema) ); - it('should reject < \'' + MIN_INDENT + '\'', async () => - await expectOptionsValidationError({ + it('should reject < \'' + MIN_INDENT + '\'', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', indent: MIN_INDENT - 1, }, writerOptionsSchema) ); - it('should reject > \'' + MAX_INDENT + '\'', async () => - await expectOptionsValidationError({ + it('should reject > \'' + MAX_INDENT + '\'', () => + expectOptionsValidationError({ src: './test/data/test-data.js', dest: 'some-file', indent: MAX_INDENT + 1, From 25ac68a5dde36320a4c6a9cce367e091ff85c071 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Wed, 12 Jul 2017 08:56:43 +0200 Subject: [PATCH 16/58] Tests green again, but not 100%, aligned configuration for transform, docs corrected --- .bithoundrc | 22 ++ .jestrc.js | 4 +- .travis.yml | 11 +- API-PRIVATE.md | 300 +++++++++++--------- API-PUBLIC.md | 116 ++------ CHANGELOG.md | 125 +++++--- CODEOWNERS | 7 + MAKE.md | 3 +- Makefile | 11 +- PACKAGE.md | 2 + README.md | 108 ++++--- package.json | 4 +- readme/BADGES.md | 49 +++- readme/DOCUMENTATION.md | 56 ++-- src/cli.js | 10 +- src/reader.js | 20 +- src/transformer.js | 144 ++-------- src/type-definitions.js | 47 +-- src/validation/options-schema-helper.js | 56 ++++ src/validation/options-schema.js | 103 ++++++- src/writer.js | 29 +- test/unit/test-transformer.js | 77 +++-- test/unit/validation/test-options-schema.js | 110 +++---- 23 files changed, 813 insertions(+), 601 deletions(-) create mode 100644 .bithoundrc create mode 100644 CODEOWNERS diff --git a/.bithoundrc b/.bithoundrc new file mode 100644 index 0000000..5e0d2eb --- /dev/null +++ b/.bithoundrc @@ -0,0 +1,22 @@ +{ + "critics": { + "lint": {"engine": "eslint"} + }, + "dependencies": { + "unused-ignores": [ + "grunt-*", + "bower", + "eslint" + ] + } + "ignore": [ + "**/bin/**", + "**/coverage/**", + "**/lib/**", + "**/node_modules/**", + "**/readme/**" + ], + "test": [ + "**/test/unit/**" + ] +} diff --git a/.jestrc.js b/.jestrc.js index c4e1b71..c8c231b 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -23,10 +23,10 @@ module.exports = { // '!**/test/unit/test-cli.js', // '**/test/unit/validation/test-joi-extensions-file-helper.js', // '**/test/unit/validation/test-joi-extensions-identifier-helper.js', - // '**/test/unit/test-transformer.js', + //'**/test/unit/test-transformer.js', // '**/test/unit/test-index.js', //'**/test/unit/test-reader.js', - '**/test/unit/test-writer.js', + // '**/test/unit/test-writer.js', //'**/test/unit/validation/test-options-schema.js', '**/test/unit/**/*.js', //'**/test/unit/validation/test-options-schema-helper.js', diff --git a/.travis.yml b/.travis.yml index 53479f5..eb52e4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,12 +10,17 @@ os: - linux - osx +script: +- make test +- npm install bithound --save-dev +- bithound check git@github.com:deadratfink/jy-transform.git + after_success: - ./node_modules/codecov/bin/codecov -e TRAVIS_NODE_VERSION - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js --verbose - ./node_modules/codeclimate-test-reporter/bin/codeclimate.js < ./coverage/lcov.info branches: - only: # whitelist - - master - - /^(bugfix|feature|refactor)\/(#\d)?.*$/ + only: # whitelist + - master + - /^(bugfix|feature|refactor)\/(#\d)?.*$/ diff --git a/API-PRIVATE.md b/API-PRIVATE.md index a548c4d..413d717 100644 --- a/API-PRIVATE.md +++ b/API-PRIVATE.md @@ -31,7 +31,6 @@ - [readJs ⇒ Promise.<Object> ℗](#readjs-%E2%87%92-codepromiseltobjectgtcode-%E2%84%97) - [readYaml ⇒ Promise.<Object> ℗](#readyaml-%E2%87%92-codepromiseltobjectgtcode-%E2%84%97) - [read ⇒ Promise](#read-%E2%87%92-codepromisecode) -- [transform ⇒ Promise](#transform-%E2%87%92-codepromisecode) - [createExportsString ⇒ Promise.<string> ℗](#createexportsstring-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [serializeJsToString ⇒ Promise.<string> ℗](#serializejstostring-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [serializeJsToJsonString ⇒ string ℗](#serializejstojsonstring-%E2%87%92-codestringcode-%E2%84%97) @@ -40,9 +39,9 @@ - [writeJson ⇒ Promise.<string> ℗](#writejson-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [writeJs ⇒ Promise.<string> ℗](#writejs-%E2%87%92-codepromiseltstringgtcode-%E2%84%97) - [write ⇒ Promise](#write-%E2%87%92-codepromisecode) -- [ReaderOptions : object](#readeroptions--codeobjectcode) -- [WriterOptions : object](#writeroptions--codeobjectcode) -- [TransformerOptions : object](#transformeroptions--codeobjectcode) +- [ReadOptions : object](#readoptions--codeobjectcode) +- [WriteOptions : object](#writeoptions--codeobjectcode) +- [TransformOptions : object](#transformoptions--codeobjectcode) - [joi ℗](#joi-%E2%84%97) @@ -138,15 +137,6 @@ exception on those.

      readPromise

      Reads a particular content type from a source provided in the passed options.

      -
      transformPromise
      -

      The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic.

      -
        -
      1. Input (read)
      2. -
      3. Transform [ + Middleware]
      4. -
      5. Output (write).
      6. -
      -
      createExportsStringPromise.<string>

      Creates a potential named 'module.exports[.exportsTo]' string.

      @@ -176,13 +166,13 @@ an (optional) middleware function. It executes the transformation logic.

      ## Typedefs
      -
      ReaderOptions : object
      +
      ReadOptions : object

      The configuration properties provided to the read function.

      -
      WriterOptions : object
      -

      The writer configuration properties provided to the write function.

      +
      WriteOptions : object
      +

      The configuration properties provided to the write function.

      -
      TransformerOptions : object
      +
      TransformOptions : object

      The configuration properties provided to the transform function.

      @@ -205,7 +195,7 @@ The command line interface. * [jy-transform:jyt](#module_jy-transform_jyt) ℗ * [~usage](#module_jy-transform_jyt..usage) : string ℗ * [~packagePath](#module_jy-transform_jyt..packagePath) : string ℗ - * [~options](#module_jy-transform_jyt..options) : Object ℗ + * [~cliOptionsSchema](#module_jy-transform_jyt..cliOptionsSchema) : Object ℗ * [~error(err)](#module_jy-transform_jyt..error) ℗ * [~main(args, cliOptions)](#module_jy-transform_jyt..main) ℗ @@ -223,9 +213,9 @@ The path to package.json. **Kind**: inner constant of [jy-transform:jyt](#module_jy-transform_jyt) **Access**: private - + -### jy-transform:jyt~options : Object ℗ +### jy-transform:jyt~cliOptionsSchema : Object ℗ The options description for parsing the command line input, must be an object with opts defined like: ``` long_tag: [short_tag, description, value_type, default_value]; @@ -258,7 +248,7 @@ prints the result to the CLI. | Param | Type | Description | | --- | --- | --- | | args | Array | The first mandatory argument is the input file (`args[0]`), the second (optional) argument is the output file (`args[1]`). | -| cliOptions | module:jy-transform:type-definitions~TransformerOptions | The options provided via CLI. | +| cliOptions | module:jy-transform:type-definitions~TransformOptions | The options provided via CLI. | @@ -268,14 +258,14 @@ Useful constants used for the module and its usage. **Access**: public * [jy-transform:constants](#module_jy-transform_constants) - * [~DEFAULT_OPTIONS](#module_jy-transform_constants..DEFAULT_OPTIONS) : object ℗ * [~UTF8](#module_jy-transform_constants..UTF8) : string ℗ * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string * [~TYPE_JSON](#module_jy-transform_constants..TYPE_JSON) : string * [~TYPE_JS](#module_jy-transform_constants..TYPE_JS) : string - * [~TYPE_MAP](#module_jy-transform_constants..TYPE_MAP) : Object ℗ + * [~EXT_TO_TYPE_MAP](#module_jy-transform_constants..EXT_TO_TYPE_MAP) : Object ℗ * [~DEFAULT_INDENT](#module_jy-transform_constants..DEFAULT_INDENT) : number ℗ * [~MIN_INDENT](#module_jy-transform_constants..MIN_INDENT) : number ℗ + * [~MIN_YAML_INDENT](#module_jy-transform_constants..MIN_YAML_INDENT) : number ℗ * [~MAX_INDENT](#module_jy-transform_constants..MAX_INDENT) : number ℗ * [~DEFAULT_ORIGIN](#module_jy-transform_constants..DEFAULT_ORIGIN) : string ℗ * [~DEFAULT_TARGET](#module_jy-transform_constants..DEFAULT_TARGET) : string ℗ @@ -286,31 +276,6 @@ Useful constants used for the module and its usage. * [~DEFAULT_JS_IMPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_IMPORTS_IDENTIFIER) : string ℗ * [~DEFAULT_JS_EXPORTS_IDENTIFIER](#module_jy-transform_constants..DEFAULT_JS_EXPORTS_IDENTIFIER) : string ℗ - - -### jy-transform:constants~DEFAULT_OPTIONS : object ℗ -The default options. - -**Kind**: inner namespace of [jy-transform:constants](#module_jy-transform_constants) -**Access**: private -**See** - -- [ORIGIN_DESCRIPTION](ORIGIN_DESCRIPTION) -- [TARGET_DESCRIPTION](TARGET_DESCRIPTION) -- [DEST_DESCRIPTION](DEST_DESCRIPTION) - -**Properties** - -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| origin | string | "yaml" | The default origin type. | -| target | string | "js" | The default target type. | -| dest | string | "relative_to_input_file" | The default dest description. | -| indent | number | 4 | The default indention for files. | -| force | boolean | false | Whether to overwrite existing file on output. | -| imports | string | | The exports name for reading from JS source file or objects only. | -| exports | string | | The exports name for usage in JS file or object only. | - ### jy-transform:constants~UTF8 : string ℗ @@ -339,9 +304,9 @@ The `'js'` type constant. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) **Access**: public - + -### jy-transform:constants~TYPE_MAP : Object ℗ +### jy-transform:constants~EXT_TO_TYPE_MAP : Object ℗ A map for extensions to type. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) @@ -356,7 +321,14 @@ The default file indention (4 SPACEs). ### jy-transform:constants~MIN_INDENT : number ℗ -The minimum file indention (0 SPACE). +The minimum file indention (0 SPACE) fo JS and JSON types. + +**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Access**: private + + +### jy-transform:constants~MIN_YAML_INDENT : number ℗ +The minimum file indention (0 SPACE) for YAML types. **Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) **Access**: private @@ -492,6 +464,57 @@ Reads from a passed stream and resolves by callback. This module provides the _transform_ functionality for YAML, JS or JSON source to destination mapping. **Access**: private + + +### jy-transform:transformer~transform ⇒ Promise +The entry method for all transformations accepting a configuration object and +an (optional) middleware function. It executes the transformation logic. + +1. Input (read) +2. Transform [ + Middleware] +3. Output (write). + +**Kind**: inner property of [jy-transform:transformer](#module_jy-transform_transformer) +**Returns**: Promise - The transformation result. +**Access**: public +**Resolve**: string With the transformation result as message (e.g. to be logged by caller). +**Reject**: TypeError Will throw this error when the passed `middleware` is not type of `Function`. +**Reject**: ValidationError If any `options` validation occurs. +**Reject**: Error Will throw any error if read, transform or write operation failed due to any reason. + +| Param | Type | Description | +| --- | --- | --- | +| options | [TransformOptions](#TransformOptions) | The configuration for a transformation. | + +**Example** +```js +import { transform } from 'jy-transform'; +const options = { + src: 'foo/bar.yaml', // From YAML file... + transform: async (object) => { // ...with exchanging value... + object.foo = 'new value'; + return object; + }, + target: 'foo/bar.json', // ...to a new JSON file. + indent: 4, +}; + +// ---- Promise style: + +transform(options) + .then(console.log) + .catch(console.error); + + +// ---- async/await style: + +try { + const msg = await transform(options); + console.log(msg); +} catch (err) { + console.error(err.stack); +}; +``` ## jy-transform:validation:joi-extensions-file-helper ℗ @@ -577,6 +600,9 @@ values for origin and target depending on the `options.src` or `options.dest` va * [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) : Object ℗ * [~inferOriginDefault](#module_jy-transform_validation_options-schema-helper..inferOriginDefault) ⇒ string * [~inferTargetDefault](#module_jy-transform_validation_options-schema-helper..inferTargetDefault) ⇒ string + * [~isFileStream](#module_jy-transform_validation_options-schema-helper..isFileStream) ⇒ boolean ℗ + * [~adaptTargetPathType](#module_jy-transform_validation_options-schema-helper..adaptTargetPathType) ⇒ string ℗ + * [~inferDestDefaultFromSrc](#module_jy-transform_validation_options-schema-helper..inferDestDefaultFromSrc) ⇒ string \| undefined * [~getTypeFromFilePath(pathStr, defaultValue)](#module_jy-transform_validation_options-schema-helper..getTypeFromFilePath) ⇒ string @@ -605,6 +631,48 @@ Infers the _target_ type value from current validation context. | --- | --- | --- | | context | Object | The validation context. | + + +### jy-transform:validation:options-schema-helper~isFileStream ⇒ boolean ℗ +Checks if passed `object` is a file stream instance. + +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Returns**: boolean - A `true` if passed `object` is a file stream instance, else `false`. +**Access**: private + +| Param | Type | Description | +| --- | --- | --- | +| object | \* | The object to check. | + + + +### jy-transform:validation:options-schema-helper~adaptTargetPathType ⇒ string ℗ +Returns the passes `dest` value or an adapted destination path (the latter if `target` is defined an differs from +destinations path extension). + +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Returns**: string - The `dest` value or an adapted destination path. +**Access**: private + +| Param | Type | Description | +| --- | --- | --- | +| dest | string | The destination path. | +| [target] | string | The target file type of destination. | + + + +### jy-transform:validation:options-schema-helper~inferDestDefaultFromSrc ⇒ string \| undefined +This function is used to infer a _default_ value in case `options.dest` is not defined. +Checks if `context.src` is either a string or a file stream where can get the file path from. +If this detection process cannot be fulfilled then the function returns `undefined`. + +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Returns**: string \| undefined - The adapted `dest` path if possible, or `undefined`. + +| Param | Type | Description | +| --- | --- | --- | +| context | Object | The validation context. | + ### jy-transform:validation:options-schema-helper~getTypeFromFilePath(pathStr, defaultValue) ⇒ string ℗ @@ -628,20 +696,28 @@ The module options schema used in [module:options-validator](module:options-vali **See**: [module:options-validator](module:options-validator) * [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) : Object ℗ - * [~readerOptionsSchema](#module_jy-transform_validation_options-schema..readerOptionsSchema) : JoiSchema ℗ - * [~writerOptionsSchema](#module_jy-transform_validation_options-schema..writerOptionsSchema) : JoiSchema ℗ + * [~readOptionsSchema](#module_jy-transform_validation_options-schema..readOptionsSchema) : JoiSchema ℗ + * [~writeOptionsSchema](#module_jy-transform_validation_options-schema..writeOptionsSchema) : JoiSchema ℗ + * [~transformOptionsSchema](#module_jy-transform_validation_options-schema..transformOptionsSchema) : JoiSchema ℗ + + - +### jy-transform:validation:options-schema~readOptionsSchema : JoiSchema ℗ +The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [ReadOptions](#ReadOptions). + +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access**: private + -### jy-transform:validation:options-schema~readerOptionsSchema : JoiSchema ℗ -The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [Reader](Reader) options. +### jy-transform:validation:options-schema~writeOptionsSchema : JoiSchema ℗ +The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [WriteOptions](#WriteOptions). **Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) **Access**: private - + -### jy-transform:validation:options-schema~writerOptionsSchema : JoiSchema ℗ -The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [Writer](Writer) options. +### jy-transform:validation:options-schema~transformOptionsSchema : JoiSchema ℗ +The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [TransformOptions](#TransformOptions). **Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) **Access**: private @@ -864,7 +940,7 @@ Expect a `ValidationError` for a given options function. | Param | Type | Description | | --- | --- | --- | -| invalidOptions | [ReaderOptions](#ReaderOptions) \| [WriterOptions](#WriterOptions) | The options which potentially produce the error. | +| invalidOptions | [ReadOptions](#ReadOptions) \| [WriteOptions](#WriteOptions) | The options which potentially produce the error. | | schema | Schema | The validation schema. | @@ -877,7 +953,7 @@ Expect a validation success for a given options. | Param | Type | Description | | --- | --- | --- | -| validOptions | [ReaderOptions](#ReaderOptions) \| [WriterOptions](#WriterOptions) | The options which should be correct. | +| validOptions | [ReadOptions](#ReadOptions) \| [WriteOptions](#WriteOptions) | The options which should be correct. | | schema | Schema | The validation schema. | @@ -891,7 +967,7 @@ Reads the data from a given JS or JSON source. | Param | Type | Description | | --- | --- | --- | -| options | [ReaderOptions](#ReaderOptions) | Contains the JS/JSON source reference to read from. | +| options | [ReadOptions](#ReadOptions) | Contains the JS/JSON source reference to read from. | @@ -906,7 +982,7 @@ exception on those. | Param | Type | Description | | --- | --- | --- | -| options | [ReaderOptions](#ReaderOptions) | Contains the YAML source reference to read from. | +| options | [ReadOptions](#ReadOptions) | Contains the YAML source reference to read from. | @@ -922,7 +998,7 @@ Reads a particular content type from a source provided in the passed `options`. | Param | Type | Description | | --- | --- | --- | -| options | [ReaderOptions](#ReaderOptions) | The read options. | +| options | [ReadOptions](#ReadOptions) | The read options. | **Example** ```js @@ -950,55 +1026,6 @@ read(options) .then(obj => console.log(JSON.stringify(obj))) .catch(console.error); ``` - - -## transform ⇒ Promise -The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic. - -1. Input (read) -2. Transform [ + Middleware] -3. Output (write). - -**Kind**: global variable -**Returns**: Promise - The result. -**Access**: public -**Resolve**: string With the transformation result as message (e.g. to be logged by caller). -**Reject**: TypeError Will throw this error when the passed `middleware` is not type of `Function`. -**Reject**: ValidationError If any `options` validation occurs. -**Reject**: Error Will throw any error if read, transform or write operation failed due to any reason. - -| Param | Type | Description | -| --- | --- | --- | -| options | [TransformerOptions](#TransformerOptions) | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` async function(object) ```

      **NOTE:** the Promise has to return the processed JS object. | - -**Example** -```js -import { transform } from 'jy-transform'; -const options = {...}; - -const middleware = async (object) { - object.myproperty = 'new value'; - return object; -}; - -// ---- Promise style: - -transform(options, middleware) - .then(console.log) - .catch(console.error); - - -// ---- async/await style: - -try { - const msg = await transform(options, middleware); - console.log(msg); -} catch (err) { - console.error(err.stack); -}; -``` ## createExportsString ⇒ Promise.<string> ℗ @@ -1083,7 +1110,7 @@ Writes a JS object to a YAML destination. | Param | Type | Description | | --- | --- | --- | | object | Object | The JS object to write into YAML destination. | -| options | [WriterOptions](#WriterOptions) | The write destination and indention. | +| options | [WriteOptions](#WriteOptions) | The write destination and indention. | @@ -1103,7 +1130,7 @@ Writes a JS object to a JSON destination. | Param | Type | Description | | --- | --- | --- | | object | Object | The JS object to write into JSON destination. | -| options | [WriterOptions](#WriterOptions) | The write destination and indention. | +| options | [WriteOptions](#WriteOptions) | The write destination and indention. | @@ -1123,7 +1150,7 @@ Writes a JS object to a JS destination. The object is prefixed by `module.export | Param | Type | Description | | --- | --- | --- | | object | Object | The JSON to write into JS destination. | -| options | [WriterOptions](#WriterOptions) | The write destination and indention. | +| options | [WriteOptions](#WriteOptions) | The write destination and indention. | @@ -1140,7 +1167,7 @@ Writes the passe JS object to a particular destination described by the passed ` | Param | Type | Description | | --- | --- | --- | | object | Object | The JS source object to write. | -| options | [WriterOptions](#WriterOptions) | The write options. | +| options | [WriteOptions](#WriteOptions) | The write options. | **Example** ```js @@ -1183,9 +1210,9 @@ write(obj, options) .then(console.log) .catch(console.error); ``` - + -## ReaderOptions : object +## ReadOptions : object The configuration properties provided to the `read` function. **Kind**: global typedef @@ -1194,14 +1221,14 @@ The configuration properties provided to the `read` function. | Name | Type | Default | Description | | --- | --- | --- | --- | -| src | string \| Stream.Readable \| object | | The source (if `string` type is treated as a file path). | -| origin | string | "yaml" | The origin type. | +| src | string \| Stream.Readable \| object | | The source (if `string` type it is treated as a file path). | +| origin | string | "yaml" | The source origin type. | | imports | string | | The exports name for reading from JS source files or objects only. | - + -## WriterOptions : object -The writer configuration properties provided to the `write` function. +## WriteOptions : object +The configuration properties provided to the `write` function. **Kind**: global typedef **Access**: public @@ -1209,15 +1236,15 @@ The writer configuration properties provided to the `write` function. | Name | Type | Default | Description | | --- | --- | --- | --- | -| dest | string \| Stream.Writable \| object | | The destination (if `string` type is treated as a file path). | -| target | string | "js" | The target type. | -| indent | number | 2 | The indention value for pretty-print of output. | +| dest | string \| Stream.Writable \| object | | The destination (if `string` type it is treated as a file path). | +| target | string | "js" | The destination target type. | +| indent | number | 2 | The indentation value for pretty-print of output. | | exports | string | | The exports name for usage in JS destination files only. | | force | string | false | Force overwriting of existing output files on write phase. | - + -## TransformerOptions : object +## TransformOptions : object The configuration properties provided to the `transform` function. **Kind**: global typedef @@ -1226,13 +1253,14 @@ The configuration properties provided to the `transform` function. | Name | Type | Default | Description | | --- | --- | --- | --- | -| src | string \| Stream.Readable \| object | | The source (if `string` type is treated as a file path). | -| origin | string | "yaml" | The origin type. | -| imports | string | | The exports name for reading from JS source files or objects only. | -| dest | string \| Stream.Writable \| object | | The destination (if `string` type is treated as a file path). | -| target | string | "js" | The target type. | -| indent | number | 2 | The indention value for pretty-print of output. | -| exports | string | | The exports name for usage in JS destination files only. | +| src | string \| Stream.Readable \| object | | The _read_ source (if `string` type it is treated as a file path). | +| origin | string | "yaml" | The _read_ source origin type. | +| imports | string | | The _read_ exports name for reading from JS source files or objects only. | +| transform | function | | The option is a _transformation_ function with the following signature:

      ``` [async|Promise] function(object) ``` | +| dest | string \| Stream.Writable \| object | | The _write_ destination (if `string` type it is treated as a file path). This property could be optional in case we infer a value from `src` which is then either a string or a file stream where can get the file path from. If this detection process cannot be fulfilled then the property is `undefined` and the transform process will fail with a `ValidationError` on write phase. | +| target | string | "js" | The _write_ target type. | +| indent | number | 2 | The _write_ indentation value for pretty-print of output. | +| exports | string | | The _write_ exports name for usage in JS destination files only. | | force | string | false | Force overwriting of existing output files on write phase. | diff --git a/API-PUBLIC.md b/API-PUBLIC.md index 65e5339..44da18e 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -7,11 +7,10 @@ - [Typedefs](#typedefs) - [jy-transform:constants](#jy-transformconstants) - [read ⇒ Promise](#read-%E2%87%92-codepromisecode) -- [transform ⇒ Promise](#transform-%E2%87%92-codepromisecode) - [write ⇒ Promise](#write-%E2%87%92-codepromisecode) -- [ReaderOptions : object](#readeroptions--codeobjectcode) -- [WriterOptions : object](#writeroptions--codeobjectcode) -- [TransformerOptions : object](#transformeroptions--codeobjectcode) +- [ReadOptions : object](#readoptions--codeobjectcode) +- [WriteOptions : object](#writeoptions--codeobjectcode) +- [TransformOptions : object](#transformoptions--codeobjectcode) @@ -29,15 +28,6 @@

      readPromise

      Reads a particular content type from a source provided in the passed options.

      -
      transformPromise
      -

      The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic.

      -
        -
      1. Input (read)
      2. -
      3. Transform [ + Middleware]
      4. -
      5. Output (write).
      6. -
      -
      writePromise

      Writes the passe JS object to a particular destination described by the passed options.

      @@ -46,13 +36,13 @@ an (optional) middleware function. It executes the transformation logic.

      ## Typedefs
      -
      ReaderOptions : object
      +
      ReadOptions : object

      The configuration properties provided to the read function.

      -
      WriterOptions : object
      -

      The writer configuration properties provided to the write function.

      +
      WriteOptions : object
      +

      The configuration properties provided to the write function.

      -
      TransformerOptions : object
      +
      TransformOptions : object

      The configuration properties provided to the transform function.

      @@ -104,7 +94,7 @@ Reads a particular content type from a source provided in the passed `options`. | Param | Type | Description | | --- | --- | --- | -| options | [ReaderOptions](#ReaderOptions) | The read options. | +| options | [ReadOptions](#ReadOptions) | The read options. | **Example** ```js @@ -132,55 +122,6 @@ read(options) .then(obj => console.log(JSON.stringify(obj))) .catch(console.error); ``` - - -## transform ⇒ Promise -The entry method for all transformation accepting a configuration object and -an (optional) middleware function. It executes the transformation logic. - -1. Input (read) -2. Transform [ + Middleware] -3. Output (write). - -**Kind**: global variable -**Returns**: Promise - The result. -**Access**: public -**Resolve**: string With the transformation result as message (e.g. to be logged by caller). -**Reject**: TypeError Will throw this error when the passed `middleware` is not type of `Function`. -**Reject**: ValidationError If any `options` validation occurs. -**Reject**: Error Will throw any error if read, transform or write operation failed due to any reason. - -| Param | Type | Description | -| --- | --- | --- | -| options | [TransformerOptions](#TransformerOptions) | The configuration for a transformation. | -| [middleware] | function | This middleware Promise can be used to intercept the JSON object for altering the passed JSON, the function signature is: ``` async function(object) ```

      **NOTE:** the Promise has to return the processed JS object. | - -**Example** -```js -import { transform } from 'jy-transform'; -const options = {...}; - -const middleware = async (object) { - object.myproperty = 'new value'; - return object; -}; - -// ---- Promise style: - -transform(options, middleware) - .then(console.log) - .catch(console.error); - - -// ---- async/await style: - -try { - const msg = await transform(options, middleware); - console.log(msg); -} catch (err) { - console.error(err.stack); -}; -``` ## write ⇒ Promise @@ -196,7 +137,7 @@ Writes the passe JS object to a particular destination described by the passed ` | Param | Type | Description | | --- | --- | --- | | object | Object | The JS source object to write. | -| options | [WriterOptions](#WriterOptions) | The write options. | +| options | [WriteOptions](#WriteOptions) | The write options. | **Example** ```js @@ -239,9 +180,9 @@ write(obj, options) .then(console.log) .catch(console.error); ``` - + -## ReaderOptions : object +## ReadOptions : object The configuration properties provided to the `read` function. **Kind**: global typedef @@ -250,14 +191,14 @@ The configuration properties provided to the `read` function. | Name | Type | Default | Description | | --- | --- | --- | --- | -| src | string \| Stream.Readable \| object | | The source (if `string` type is treated as a file path). | -| origin | string | "yaml" | The origin type. | +| src | string \| Stream.Readable \| object | | The source (if `string` type it is treated as a file path). | +| origin | string | "yaml" | The source origin type. | | imports | string | | The exports name for reading from JS source files or objects only. | - + -## WriterOptions : object -The writer configuration properties provided to the `write` function. +## WriteOptions : object +The configuration properties provided to the `write` function. **Kind**: global typedef **Access**: public @@ -265,15 +206,15 @@ The writer configuration properties provided to the `write` function. | Name | Type | Default | Description | | --- | --- | --- | --- | -| dest | string \| Stream.Writable \| object | | The destination (if `string` type is treated as a file path). | -| target | string | "js" | The target type. | -| indent | number | 2 | The indention value for pretty-print of output. | +| dest | string \| Stream.Writable \| object | | The destination (if `string` type it is treated as a file path). | +| target | string | "js" | The destination target type. | +| indent | number | 2 | The indentation value for pretty-print of output. | | exports | string | | The exports name for usage in JS destination files only. | | force | string | false | Force overwriting of existing output files on write phase. | - + -## TransformerOptions : object +## TransformOptions : object The configuration properties provided to the `transform` function. **Kind**: global typedef @@ -282,12 +223,13 @@ The configuration properties provided to the `transform` function. | Name | Type | Default | Description | | --- | --- | --- | --- | -| src | string \| Stream.Readable \| object | | The source (if `string` type is treated as a file path). | -| origin | string | "yaml" | The origin type. | -| imports | string | | The exports name for reading from JS source files or objects only. | -| dest | string \| Stream.Writable \| object | | The destination (if `string` type is treated as a file path). | -| target | string | "js" | The target type. | -| indent | number | 2 | The indention value for pretty-print of output. | -| exports | string | | The exports name for usage in JS destination files only. | +| src | string \| Stream.Readable \| object | | The _read_ source (if `string` type it is treated as a file path). | +| origin | string | "yaml" | The _read_ source origin type. | +| imports | string | | The _read_ exports name for reading from JS source files or objects only. | +| transform | function | | The option is a _transformation_ function with the following signature:

      ``` [async|Promise] function(object) ``` | +| dest | string \| Stream.Writable \| object | | The _write_ destination (if `string` type it is treated as a file path). This property could be optional in case we infer a value from `src` which is then either a string or a file stream where can get the file path from. If this detection process cannot be fulfilled then the property is `undefined` and the transform process will fail with a `ValidationError` on write phase. | +| target | string | "js" | The _write_ target type. | +| indent | number | 2 | The _write_ indentation value for pretty-print of output. | +| exports | string | | The _write_ exports name for usage in JS destination files only. | | force | string | false | Force overwriting of existing output files on write phase. | diff --git a/CHANGELOG.md b/CHANGELOG.md index 6397f7d..f96ac71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,58 +1,89 @@ -## Changelog +# Changelog -#### v3.0.0 +## v3.0.0 -- New Features: - - The _middleware_ function must not need to be a Promise anymore, but it is still recommended. - - The `read` process returns a clone of the `options.src` when it is a JS object (so the origin - would never be changed). - - The minimum indent for YAML target types is validated for 2 now and throws a `ValidationError` - if < 2 (for others 0 is still valid). +### Note +This release has undergone a huge refactoring which includes some new features, cleanups in many places +including a new public interface, bugfixes and many internal improvements. Of course, this resulted in some +APIs and CLI backwards compatibilities (these are marked in the descriptions below). So, please read the following +changelog entries carefully and see also the following documents for more information about how to use the +new interface: +- [README.md](https://github.com/deadratfink/jy-transform/blob/master/README.md) +- [API-PUBLIC.md](https://github.com/deadratfink/jy-transform/blob/master/API-PUBLIC.md) -- Bugfix: - - If destination is not given on transformation process but the target is, then the destination file extension - is adapted to the proper type, e.g. `$ ./jyt inch.json -t yaml` results in a file _inch.yaml_ (formerly: - _inch.json_ with YAML content or _inch(1).json_ with YAML, the latter if `options.force` was `true`). +### New Features: +- [[#59](https://github.com/deadratfink/jy-transform/issues/59)] Support single-quotes options for JS output. + - **CLI & API Backwards Incompatible Change!** + - This is the default now. +- [[#55](https://github.com/deadratfink/jy-transform/issues/55)] The `read` process returns a clone of the + `options.src` when it is a JS object (so the origin would never be changed). +- The `options.transform` function (formerly aka _middleware_ function) must not need to be a Promise anymore, but it is still recommended. -- **CLI & API Changes (Backwards Incompatible!):** - - Removed support for Node.js < v4.0. - - Default `options.indent` is 2 (instead of 4) now which seems to be more common in the JS/Node.js community. - - An invalid indention setting (i.e. `indent` < 0 or `indent` > 8) raises a `ValidationError` now. +### Bugfixes: +- [[#57](https://github.com/deadratfink/jy-transform/issues/57)] The minimum indent for YAML target types is + validated for 2 now and throws a `ValidationError` if < 2 (for others 0 is still valid). +- [[#56](https://github.com/deadratfink/jy-transform/issues/56)] If _destination_ is not given on transformation + process but the _target_ is, then the destination's file extension + is adapted to the proper type, e.g. `$ ./jyt inch.json -t yaml` results in a file _inch.yaml_ (formerly: + _inch.json_ with YAML content or respectively _inch(1).json_ with YAML, the latter if `options.force` was `true`). -- **API Changes Only (Backwards Incompatible!):** +### Public Interface Changes & Improvements: +- [[#61](https://github.com/deadratfink/jy-transform/issues/61)] Invalid indention setting should raise an error: + - **CLI & API Backwards Incompatible Change!** + - An invalid indention setting (i.e. `indent` < 0 or `indent` > 8) raises a `ValidationError` now instead + of using default. +- [[#60](https://github.com/deadratfink/jy-transform/issues/60)] Default `[write.]options.indent` is 2 (instead of 4): + - **CLI & API Backwards Incompatible Change!** + - This seems to be more common in the JS/Node.js community. +- [[#57](https://github.com/deadratfink/jy-transform/issues/57)] Provide a simplified interface: + - **API Backwards Incompatible Changes!** - The exported constants `YAML`, `JS` and `JSON` (usable for `options.origin/target`) are renamed respectively to `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON`. - - Provide a simplified interface: - - Prototype approach removed from `Transformer`, `Reader` and `Writer`, turning it to internal modules with - exports of _named_ functions: - - The formerly exported `Reader.readJs(...)/readYaml(...)` functions are not public anymore and replaced - by a general `read(options)` function. - - The formerly exported `Writer.writeJs(...)/writeJson(...)/writeYaml(...)` functions are not public - anymore and replaced by a general `write(object, options)` function. - - The formerly exported `middleware` (identity function) is not publicly available anymore. - - Reduced and named export of constants: `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` only. - - The `options.imports/exports` are not allowed to be empty strings anymore (semantically senseless, - just leave it out). - - The configuration property `options.dest` is required for `Writer` when using the API (but not from `Transformer` - if it can be inferred from `options.src` which is true for string or file stream sources). + - Prototype approach removed from `Transformer`, `Reader` and `Writer`, turning it to internal modules which + exports _named_ functions instead: + - The formerly exported `Reader.readJs(...)/readYaml(...)` functions are not public anymore and replaced + by a general `read(options)` function. + - The formerly exported `Writer.writeJs(...)/writeJson(...)/writeYaml(...)` functions are not public + anymore and replaced by a general `write(object, options)` function. + - The formerly exported `Transformer.transform(options, middleware)` functions + does not take the `middleware` parameter anymore (it is added to options: `options.transform`). + - The formerly exported `middleware` (identity function) is not publicly available anymore. + - Reduced and named export of constants: `TYPE_YAML`, `TYPE_JS` and `TYPE_JSON` only. - Removal of `LogWrapper` (no more logger injection possible). - -- Internal Changes & Improvements: - - Documentation restructured. - - Removal of _development_ branch. - - Usage of [babel](https://babeljs.io/) and therefore most modern language features. - - Update of dependencies and amount reduced. - - Code base could be shrinked and readabilty was improved. + - The `options.imports/exports` are not allowed to be empty strings anymore (semantically senseless, just leave it out). + - Of course, the configuration property `options.dest` is required for _write_ process when using the API (but not from _transformer_ + if it can be inferred from `options.src` but which is true for string or file stream sources only). + +### Internal Changes & Improvements: +- [[#54](https://github.com/deadratfink/jy-transform/issues/54)] General dependency check and update: + - Latest versions. - Usage of _native_ Promises instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html). - - Tests re-written in [Jest](https://facebook.github.io/jest) (could get rid of - [assert](https://github.com/defunctzombie/commonjs-assert), - [mocha](https://mochajs.org/) and [istanbul](https://github.com/gotwarlost/istanbul)). + - Test dependencies reduced. +- [[#53](https://github.com/deadratfink/jy-transform/issues/53)] Update supported node versions: + - **CLI & API Backwards Incompatible Change!** - Add travis build for Node.js v8.x. - - Remove travis build for Node.js < v4.x. - - Removal of `OptionsHandler` and `Validator` (replaced the validation by using - [joi](https://github.com/hapijs/joi/tree/v10.5.0) now). + - Remove travis build for Node.js < v4.x. +- [[#52](https://github.com/deadratfink/jy-transform/issues/52)] Leverage modern ES6 features: + - Integrated by [babel](https://babeljs.io/). + - Update of dependencies and amount reduced. + - Code base could be shrinked and readabilty was improved. +- [[#51](https://github.com/deadratfink/jy-transform/issues/51)] Removal of _development_ branch. +- [[#50](https://github.com/deadratfink/jy-transform/issues/50)] Update/upgrade ESLint +- [[#49](https://github.com/deadratfink/jy-transform/issues/49)] Tests re-written in + [Jest](https://facebook.github.io/jest), could get rid of "complex" test setup + ([assert](https://github.com/defunctzombie/commonjs-assert), [mocha](https://mochajs.org/) and + [istanbul](https://github.com/gotwarlost/istanbul)). +- [[#48](https://github.com/deadratfink/jy-transform/issues/48)] Using [Joi](https://github.com/hapijs/joi) + for consistent options validation: + - Removal of `OptionsHandler` and `Validator` +- [[#47](https://github.com/deadratfink/jy-transform/issues/47)] Integration of + [bithound.io](https://www.bithound.io/github/deadratfink/jy-transform) +- [[#46](https://github.com/deadratfink/jy-transform/issues/46)] Use Make as an abstraction to npm scripts +- [[#45](https://github.com/deadratfink/jy-transform/issues/45)] [Node Security Plattform] integrated. +- [[#43](https://github.com/deadratfink/jy-transform/issues/43)] Documentation restructured. + -#### v2.0.1 +### v2.0.1 - [[#39](https://github.com/deadratfink/jy-transform/issues/39)] Maintenance release. - Update dependencies to latest. @@ -60,7 +91,7 @@ - Docs improved/corrected. - Add target pretest in `scripts` section to `rm` _./test/tmp_ folder. -#### v2.0.0 +### v2.0.0 - [[#33](https://github.com/deadratfink/jy-transform/issues/33)] Enhance `LogWrapper` with `TRACE` level (API). - [[#32](https://github.com/deadratfink/jy-transform/issues/32)] Introduce input and output on CLI as @@ -71,12 +102,12 @@ 'yaml' for origin (API). - [Cleanup] Update dependencies. -#### v1.0.2 +### v1.0.2 - [[#30](https://github.com/deadratfink/jy-transform/issues/30)] Fix README and externalize API reference to wiki. - [[#29](https://github.com/deadratfink/jy-transform/issues/29)] Fix Promise warning on write process. -#### v1.0.1 +### v1.0.1 Initial public release. This covers the basic implementation and tests. The following features and fixes and part of this release: diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..d166473 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,7 @@ +# See: https://github.com/blog/2392-introducing-code-owners +# +# Lines starting with '#' are comments. +# Each line is a file pattern followed by one or more owners. + +# These owners will be the default owners for everything in the repo. +* @deadratfink diff --git a/MAKE.md b/MAKE.md index 5e11af3..c5c37a9 100644 --- a/MAKE.md +++ b/MAKE.md @@ -6,6 +6,7 @@ Target Call | Description | Dependencies `$ make eslint` | Runs ESLint. | `$ make help` | Prints the help about targets. | `$ make install` | Installs all modules | +`$ make nsp` | Runs an [Node Security Plattform](https://nodesecurity.io/opensource) check. | `$ make publish` | Publishes module to NPM registry. | `test readme` `$ make readme` | Creates all the documentation parts of the project | -`$ make test` | Runs the test suite and ESLint. | +`$ make test` | Runs the test suite, ESLint and a [Node Security Plattform](https://nodesecurity.io/opensource) check. | diff --git a/Makefile b/Makefile index 9ce77a7..b0444ee 100644 --- a/Makefile +++ b/Makefile @@ -16,10 +16,17 @@ clean: ## Removes generated files in folders ./node_modules, ./lib and ./coverag rm -rf node_modules rm -rf coverage -test: ## Runs the test suite and ESLint. - @printf "Running test suite and ESLint...\n" +test: ## Runs the test suite, ESLint and a [Node Security Plattform](https://nodesecurity.io/opensource) check. + @printf "Running test suite, ESLint and NSP...\n" npm test npm run eslint + npm run nsp + +nsp: ## Runs an [Node Security Plattform](https://nodesecurity.io/opensource) check. + @printf "Running NSP check...\n" + npm run nsp + +bithound bithound check git@github.com:deadratfink/jy-transform.git readme: ## Creates all the documentation parts of the project: _README.md_, _MAKE.md_, _PACKAGE.md_ and _API.md_ (the latter based on [JSDoc](http://usejsdoc.org/)). @printf "Create documentation...\n" diff --git a/PACKAGE.md b/PACKAGE.md index e5098b3..d741837 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -37,6 +37,7 @@ npm test - [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects - [codecov](https://github.com/codecov/codecov-node): Uploading report to Codecov: https://codecov.io - [coveralls](https://github.com/nickmerwin/node-coveralls): takes json-cov output into stdin and POSTs to coveralls.io +- [cwd](https://github.com/jonschlinkert/cwd): Easily get the CWD (current working directory) of a project based on package.json, optionally starting from a given path. (node.js/javascript util) - [doctoc](https://github.com/thlorenz/doctoc): Generates TOC for markdown files of local git repo. - [eslint](https://github.com/eslint/eslint): An AST-based pattern checker for JavaScript. - [eslint-config-airbnb-base](https://github.com/airbnb/javascript): Airbnb's base JS ESLint config, following our styleguide @@ -50,6 +51,7 @@ npm test - [jsdoc-babel](https://github.com/ctumolosus/jsdoc-babel): A JSDoc plugin that transforms ES6 source files with Babel before they are processsed. - [jsdoc-parse](https://github.com/jsdoc2md/jsdoc-parse): Transforms jsdoc data into something more suitable for use as template input - [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown): Generates markdown API documentation from jsdoc annotated source code +- [nsp](https://github.com/nodesecurity/nsp): The Node Security (nodesecurity.io) command line interface - [package-json-to-readme](https://github.com/zeke/package-json-to-readme): Generate a README.md from package.json contents - [winston](https://github.com/winstonjs/winston): A multi-transport async logging library for Node.js - [winston-console-formatter](https://github.com/eugeny-dementev/winston-console-formatter): Pretty print console formatter in yaml like style diff --git a/README.md b/README.md index 91fcb94..a1dc1d6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,27 @@ -[![License][gh-license-image]][gh-license-url] [![Issue Stats][gh-issues-image]][gh-issues-url] [![Releases][gh-releases-image]][gh-releases-url] [![Tags][gh-tags-image]][gh-tags-url] [![Build Status][ci-image]][ci-url] [![Waffle][waffle-image]][waffle-url] [![Code Climate][cocl-image]][cocl-url] -[![codecov.io][cc-image-master]][cc-url-master] [![coveralls.io][ca-image-master]][ca-url-master] [![inch-ci.org][inch-image-master]][inch-url-master] [![Dependency Status][dep-image-master]][dep-url-master] [![devDependency Status][devdep-image-master]][devdep-url-master] - -[![NPM](https://nodei.co/npm/jy-transform.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/jy-transform/) - -[![NPM](https://nodei.co/npm-dl/jy-transform.png?height=3&months=9)](https://nodei.co/npm-dl/jy-transform/) +[![Node version][node-version-image]][node-version-url] +[![License][gh-license-image]][gh-license-url] +[![Issue Stats][gh-issues-image]][gh-issues-url] +[![Releases][gh-releases-image]][gh-releases-url] +[![Tags][gh-tags-image]][gh-tags-url] +[![Build Status][ci-image]][ci-url] +[![Waffle][waffle-ready-image]][waffle-url] +[![Waffle][waffle-waffle-in-progress-image]][waffle-url] +[![Code Climate][cocl-image]][cocl-url] +[![codecov.io][cc-image-master]][cc-url-master] +[![coveralls.io][ca-image-master]][ca-url-master] +[![inch-ci.org][inch-image-master]][inch-url-master] +[![bitHound Code][bithound-code-image]][bithound-url] +[![bitHound Dependencies][bitHound-dependencies-image]][bitHound-dependencies] +[![bitHound Dev Dependencies][bitHound-dev-dependencies-image]][bitHound-dependencies] +[![NSP Status][nsp-image-master]][nsp-url-master] + + + + +[![NPM][npm-image]][npm-url] +[![NPM][npm-downloads-image]][npm-url] [gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat-square [gh-license-url]: https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md @@ -25,7 +43,8 @@ [is-issue-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/issue?style=flat-square [is-url]: http://issuestats.com/github/deadratfink/jy-transform -[waffle-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat-square +[waffle-ready-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat-square +[waffle-waffle-in-progress-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=in%20progress&title=Waffle%20In%20Progress&style=flat-square [waffle-url]: https://waffle.io/deadratfink/jy-transform [cocl-image]: https://img.shields.io/codeclimate/github/deadratfink/jy-transform.svg?style=flat-square @@ -48,6 +67,22 @@ [devdep-image-master]: https://img.shields.io/david/dev/deadratfink/jy-transform/master.svg?style=flat-square [devdep-url-master]: https://david-dm.org/deadratfink/jy-transform/master#info=devDependencies +[nsp-image-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0/badge?style=flat-square +[nsp-url-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0 + +[node-version-image]: https://img.shields.io/node/v/jy-transform.svg?style=flat-square +[node-version-url]: http://nodejs.org/download/ + +[npm-image]: https://nodei.co/npm/jy-transform.png?downloads=true&downloadRank=true&stars=true +[npm-url]: https://nodei.co/npm/jy-transform/ +[npm-downloads-image]: https://nodei.co/npm-dl/jy-transform.png?height=2&months=9 + +[bithound-url]: https://www.bithound.io/github/deadratfink/jy-transform +[bithound-code-image]: https://img.shields.io/bithound/code/github/deadratfink/jy-transform.svg?style=flat-square +[bitHound-dependencies]: https://www.bithound.io/github/deadratfink/jy-transform/master/dependencies/npm +[bitHound-dependencies-image]: https://img.shields.io/bithound/dependencies/github/deadratfink/jy-transform.svg?style=flat-square +[bitHound-dev-dependencies-image]: https://img.shields.io/bithound/devDependencies/github/deadratfink/jy-transform.svg?style=flat-square +[bitHound-score-image]: https://img.shields.io/bithound/score/github/deadratfink/jy-transform.svg?style=flat-square # jy-transform @@ -91,22 +126,30 @@ npm install jy-transform --global ```javascript import { transform } from 'jy-transform'; -const transformOptions = { - src: 'foo/bar.yaml', - target: 'foo/bar.json', - indent: 4, +const options = { + src: 'foo/bar.yaml', // Here: read from YAML file... + transform: async (object) => { // ...with exchanging value... + object.foo = 'new value'; + return object; + }, + dest: 'foo/bar.json', // ...to a new JSON file. + indent: 4, // Ensure an indentation of 4. }; -const transformFunc = async (object) => { - object.foo = 'new value'; - return object; -}; +// ---- Promise style: + +transform(options) + .then(console.log) + .catch(console.error); + + +// ---- async/await style: + -// of course, inside an async try { - const msg = await transform(transformOptions, transformFunc); - console.log(msg); -} catch (err) { + const msg = await transform(options); // Transform, of course, inside an async. + console.log(msg); // Success message! +} catch (err) { // Oops! console.error(err.stack); } ``` @@ -116,11 +159,11 @@ try { ```javascript import { read } from 'jy-transform'; -let object; +const options = { src: 'foo/bar.yaml' }; // Here: read from file. try { - object = await read({ src: 'foo/bar.yaml' }); // here: read from file - console.log(JSON.stringify(object)); + const object = await read(options); + console.log(JSON.stringify(object)); // Print write success message. } catch (err) { console.error(err.stack); } @@ -131,9 +174,11 @@ try { ```javascript import { write } from 'jy-transform'; +const options = { dest: 'foo/bar.yaml' }; // Here: write to file. + try { - const msg = await write(object, { dest: 'foo/bar.yaml' }); - console.log(msg); + const msg = await write(object, options); + console.log(msg); // Print write success message. } catch (err) { console.error(err.stack); } @@ -142,7 +187,7 @@ try { ## Why This Module? After struggling with some huge YAML file and accidentally -occurring wrong indentions which results in an annoying investigation hell, +occurring wrong indentations which results in an annoying investigation hell, I decided to get rid of the YAML file and therefore, create a module which should be aimed as the swiss army knife for transforming YAML, JS and JSON types into each other format. @@ -150,8 +195,7 @@ types into each other format. ## Usage The module can be used on CLI or as API (the latter is fully -[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)) -based). +[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) based). ### Usage Types @@ -279,7 +323,7 @@ Usage: Options: -o, --origin [STRING] The origin type of INPUT-FILE: [ js | json | yaml ]. (Default is if not given, the type is tried to be inferred from the extension of source path, else it is 'yaml') -t, --target [STRING] The target type of OUTPUT-FILE: [ js | json | yaml ]. (Default is if not given, the type is tried to be inferred from the extension of destination path, else it is 'js') - -i, --indent [NUMBER] The indention for pretty-print: 1 - 8. (Default is 4) + -i, --indent [NUMBER] The indentation for pretty-print: 1 - 8. (Default is 4) -f, --force Force overwriting of existing output files on write phase. When files are not overwritten (which is default), then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of foo.yaml it would be foo(1).yaml. @@ -312,7 +356,7 @@ The OPTIONS are more formally defined in the following table: | --- | --- | --- | --- | --- | --- | | `-o` | `--origin` | string of: [ _js_ | _json_ | _yaml_ ] | The transformation origin type. | if not given, the type is tried to be inferred from the extension of source path, else it is _yaml_ | no | | `-t` | `--target` | string of: [ _js_ | _json_ | _yaml_ ] | The transformation target type. | if not given, the type is tried to be inferred from the extension of destination path, else it is _js_ | no | -| `-i` | `--indent` | integer
      [ 1 - 8 ]
      | The code indention used in destination files. | 2 | no | +| `-i` | `--indent` | integer
      [ 1 - 8 ]
      | The code indentation used in destination files. | 2 | no | | `-f` | `--force` | n/a | Force overwriting of existing output files on write phase. When files are not overwritten (which is default), then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | | `-m` | `--imports` | string | Define a 'module.exports[.identifier] = ' identifier (to read from JS _source_ file only, must be a valid JS identifier!) | _undefined_ | no | | `-x` | `--exports` | string | Define a 'module.exports[.identifier] = ' identifier (for usage in JS _destination_ file only, must be a valid JS identifier!) | _undefined_ | no | @@ -322,7 +366,7 @@ The OPTIONS are more formally defined in the following table: | `-h` | `--help` | n/a | Display help and usage details. | n/a | no | -**NOTE:** an invalid indention setting (1 > `-i`, `--indent` > 8) does not raise an error but a default of 4 SPACEs is applied instead. +**NOTE:** an invalid indentation setting (1 > `-i`, `--indent` > 8) does not raise an error but a default of 4 SPACEs is applied instead. #### Examples @@ -487,7 +531,7 @@ By default this feature is not enabled to prevent you from accidentally overwriting your input source or already generated targets. But let's say we want to overwrite the original source now because you want -to change the indention from 2 to 4 SPACEs, then we can do this as follows: +to change the indentation from 2 to 4 SPACEs, then we can do this as follows: ``` $ jyt foo.js -f ``` @@ -557,7 +601,7 @@ The `options` object has to follow this key-values tables: | Option | Type | Description | Default | Required | | --- | --- | --- | --- | --- | | `dest` | [ _String_ | _Stream.Writable_ | _Object_ ] | The destination information object: `String` is used as file path, `Stream.Writable` writes a stringified source and `object` is used for direct JS object assignment of the (stringified or JS object) source. If a string is set as file path then the output and if input and output file path are the same, then the file overwriting is handled depending on the `force` value! | - | yes | -| `indent` | _Number_ | The indention in files. | 2 | no | +| `indent` | _Number_ | The indentation in files. | 2 | no | | `force` | _Boolean_ | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | | `imports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | | `exports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | diff --git a/package.json b/package.json index ca98689..5d58f84 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", "pretest": "mkdir -pv test/tmp", "test": "JYT_DEBUG=false JYT_ERROR=false jest --forceExit --expand --no-cache --coverage --config=./.jestrc.js", - "eslint": "eslint ." + "eslint": "eslint .", + "nsp": "nsp check" }, "engines": { "node": ">=4.0.0" @@ -64,6 +65,7 @@ "jsdoc-babel": " ~0.3.0", "jsdoc-parse": " ~3.0.0", "jsdoc-to-markdown": " ~3.0.0", + "nsp": "~2.6.3", "package-json-to-readme": " ~2.0.0", "winston": " ~2.3.0", "winston-console-formatter": "~0.3.1" diff --git a/readme/BADGES.md b/readme/BADGES.md index e03edb8..a1488bd 100644 --- a/readme/BADGES.md +++ b/readme/BADGES.md @@ -1,9 +1,27 @@ -[![License][gh-license-image]][gh-license-url] [![Issue Stats][gh-issues-image]][gh-issues-url] [![Releases][gh-releases-image]][gh-releases-url] [![Tags][gh-tags-image]][gh-tags-url] [![Build Status][ci-image]][ci-url] [![Waffle][waffle-image]][waffle-url] [![Code Climate][cocl-image]][cocl-url] -[![codecov.io][cc-image-master]][cc-url-master] [![coveralls.io][ca-image-master]][ca-url-master] [![inch-ci.org][inch-image-master]][inch-url-master] [![Dependency Status][dep-image-master]][dep-url-master] [![devDependency Status][devdep-image-master]][devdep-url-master] - -[![NPM](https://nodei.co/npm/jy-transform.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/jy-transform/) - -[![NPM](https://nodei.co/npm-dl/jy-transform.png?height=3&months=9)](https://nodei.co/npm-dl/jy-transform/) +[![Node version][node-version-image]][node-version-url] +[![License][gh-license-image]][gh-license-url] +[![Issue Stats][gh-issues-image]][gh-issues-url] +[![Releases][gh-releases-image]][gh-releases-url] +[![Tags][gh-tags-image]][gh-tags-url] +[![Build Status][ci-image]][ci-url] +[![Waffle][waffle-ready-image]][waffle-url] +[![Waffle][waffle-waffle-in-progress-image]][waffle-url] +[![Code Climate][cocl-image]][cocl-url] +[![codecov.io][cc-image-master]][cc-url-master] +[![coveralls.io][ca-image-master]][ca-url-master] +[![inch-ci.org][inch-image-master]][inch-url-master] +[![bitHound Code][bithound-code-image]][bithound-url] +[![bitHound Dependencies][bitHound-dependencies-image]][bitHound-dependencies] +[![bitHound Dev Dependencies][bitHound-dev-dependencies-image]][bitHound-dependencies] +[![NSP Status][nsp-image-master]][nsp-url-master] + + + + +[![NPM][npm-image]][npm-url] +[![NPM][npm-downloads-image]][npm-url] [gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat-square [gh-license-url]: https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md @@ -25,7 +43,8 @@ [is-issue-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/issue?style=flat-square [is-url]: http://issuestats.com/github/deadratfink/jy-transform -[waffle-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat-square +[waffle-ready-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat-square +[waffle-waffle-in-progress-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=in%20progress&title=Waffle%20In%20Progress&style=flat-square [waffle-url]: https://waffle.io/deadratfink/jy-transform [cocl-image]: https://img.shields.io/codeclimate/github/deadratfink/jy-transform.svg?style=flat-square @@ -48,3 +67,19 @@ [devdep-image-master]: https://img.shields.io/david/dev/deadratfink/jy-transform/master.svg?style=flat-square [devdep-url-master]: https://david-dm.org/deadratfink/jy-transform/master#info=devDependencies +[nsp-image-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0/badge?style=flat-square +[nsp-url-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0 + +[node-version-image]: https://img.shields.io/node/v/jy-transform.svg?style=flat-square +[node-version-url]: http://nodejs.org/download/ + +[npm-image]: https://nodei.co/npm/jy-transform.png?downloads=true&downloadRank=true&stars=true +[npm-url]: https://nodei.co/npm/jy-transform/ +[npm-downloads-image]: https://nodei.co/npm-dl/jy-transform.png?height=2&months=9 + +[bithound-url]: https://www.bithound.io/github/deadratfink/jy-transform +[bithound-code-image]: https://img.shields.io/bithound/code/github/deadratfink/jy-transform.svg?style=flat-square +[bitHound-dependencies]: https://www.bithound.io/github/deadratfink/jy-transform/master/dependencies/npm +[bitHound-dependencies-image]: https://img.shields.io/bithound/dependencies/github/deadratfink/jy-transform.svg?style=flat-square +[bitHound-dev-dependencies-image]: https://img.shields.io/bithound/devDependencies/github/deadratfink/jy-transform.svg?style=flat-square +[bitHound-score-image]: https://img.shields.io/bithound/score/github/deadratfink/jy-transform.svg?style=flat-square diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index a92113e..c5b06f0 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -5,22 +5,30 @@ ```javascript import { transform } from 'jy-transform'; -const transformOptions = { - src: 'foo/bar.yaml', - target: 'foo/bar.json', - indent: 4, +const options = { + src: 'foo/bar.yaml', // Here: read from YAML file... + transform: async (object) => { // ...with exchanging value... + object.foo = 'new value'; + return object; + }, + dest: 'foo/bar.json', // ...to a new JSON file. + indent: 4, // Ensure an indentation of 4. }; -const transformFunc = async (object) => { - object.foo = 'new value'; - return object; -}; +// ---- Promise style: + +transform(options) + .then(console.log) + .catch(console.error); + + +// ---- async/await style: + -// of course, inside an async try { - const msg = await transform(transformOptions, transformFunc); - console.log(msg); -} catch (err) { + const msg = await transform(options); // Transform, of course, inside an async. + console.log(msg); // Success message! +} catch (err) { // Oops! console.error(err.stack); } ``` @@ -30,9 +38,11 @@ try { ```javascript import { read } from 'jy-transform'; +const options = { src: 'foo/bar.yaml' }; // Here: read from file. + try { - const object = await read({ src: 'foo/bar.yaml' }); // here: read from file - console.log(JSON.stringify(object)); + const object = await read(options); + console.log(JSON.stringify(object)); // Print write success message. } catch (err) { console.error(err.stack); } @@ -43,9 +53,11 @@ try { ```javascript import { write } from 'jy-transform'; +const options = { dest: 'foo/bar.yaml' }; // Here: write to file. + try { - const msg = await write(object, { dest: 'foo/bar.yaml' }); - console.log(msg); + const msg = await write(object, options); + console.log(msg); // Print write success message. } catch (err) { console.error(err.stack); } @@ -54,7 +66,7 @@ try { ## Why This Module? After struggling with some huge YAML file and accidentally -occurring wrong indentions which results in an annoying investigation hell, +occurring wrong indentations which results in an annoying investigation hell, I decided to get rid of the YAML file and therefore, create a module which should be aimed as the swiss army knife for transforming YAML, JS and JSON types into each other format. @@ -190,7 +202,7 @@ Usage: Options: -o, --origin [STRING] The origin type of INPUT-FILE: [ js | json | yaml ]. (Default is if not given, the type is tried to be inferred from the extension of source path, else it is 'yaml') -t, --target [STRING] The target type of OUTPUT-FILE: [ js | json | yaml ]. (Default is if not given, the type is tried to be inferred from the extension of destination path, else it is 'js') - -i, --indent [NUMBER] The indention for pretty-print: 1 - 8. (Default is 4) + -i, --indent [NUMBER] The indentation for pretty-print: 1 - 8. (Default is 4) -f, --force Force overwriting of existing output files on write phase. When files are not overwritten (which is default), then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of foo.yaml it would be foo(1).yaml. @@ -223,7 +235,7 @@ The OPTIONS are more formally defined in the following table: | --- | --- | --- | --- | --- | --- | | `-o` | `--origin` | string of: [ _js_ | _json_ | _yaml_ ] | The transformation origin type. | if not given, the type is tried to be inferred from the extension of source path, else it is _yaml_ | no | | `-t` | `--target` | string of: [ _js_ | _json_ | _yaml_ ] | The transformation target type. | if not given, the type is tried to be inferred from the extension of destination path, else it is _js_ | no | -| `-i` | `--indent` | integer
      [ 1 - 8 ]
      | The code indention used in destination files. | 2 | no | +| `-i` | `--indent` | integer
      [ 1 - 8 ]
      | The code indentation used in destination files. | 2 | no | | `-f` | `--force` | n/a | Force overwriting of existing output files on write phase. When files are not overwritten (which is default), then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | | `-m` | `--imports` | string | Define a 'module.exports[.identifier] = ' identifier (to read from JS _source_ file only, must be a valid JS identifier!) | _undefined_ | no | | `-x` | `--exports` | string | Define a 'module.exports[.identifier] = ' identifier (for usage in JS _destination_ file only, must be a valid JS identifier!) | _undefined_ | no | @@ -233,7 +245,7 @@ The OPTIONS are more formally defined in the following table: | `-h` | `--help` | n/a | Display help and usage details. | n/a | no | -**NOTE:** an invalid indention setting (1 > `-i`, `--indent` > 8) does not raise an error but a default of 4 SPACEs is applied instead. +**NOTE:** an invalid indentation setting (1 > `-i`, `--indent` > 8) does not raise an error but a default of 4 SPACEs is applied instead. #### Examples @@ -398,7 +410,7 @@ By default this feature is not enabled to prevent you from accidentally overwriting your input source or already generated targets. But let's say we want to overwrite the original source now because you want -to change the indention from 2 to 4 SPACEs, then we can do this as follows: +to change the indentation from 2 to 4 SPACEs, then we can do this as follows: ``` $ jyt foo.js -f ``` @@ -468,7 +480,7 @@ The `options` object has to follow this key-values tables: | Option | Type | Description | Default | Required | | --- | --- | --- | --- | --- | | `dest` | [ _String_ | _Stream.Writable_ | _Object_ ] | The destination information object: `String` is used as file path, `Stream.Writable` writes a stringified source and `object` is used for direct JS object assignment of the (stringified or JS object) source. If a string is set as file path then the output and if input and output file path are the same, then the file overwriting is handled depending on the `force` value! | - | yes | -| `indent` | _Number_ | The indention in files. | 2 | no | +| `indent` | _Number_ | The indentation in files. | 2 | no | | `force` | _Boolean_ | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | | `imports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | | `exports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | diff --git a/src/cli.js b/src/cli.js index 4459d74..48a7399 100755 --- a/src/cli.js +++ b/src/cli.js @@ -49,7 +49,7 @@ const packagePath = path.join(__dirname, '../package.json'); * Array}} * @private */ -const options = { +const cliOptionsSchema = { origin: ['o', 'The origin type of INPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + ' ]', 'string', ORIGIN_DESCRIPTION], target: ['t', 'The target type of OUTPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML @@ -87,7 +87,7 @@ function error(err) { * * @param {Array} args - The first mandatory argument is the input file (`args[0]`), the second (optional) * argument is the output file (`args[1]`). - * @param {module:jy-transform:type-definitions~TransformerOptions} cliOptions - The options provided via CLI. + * @param {module:jy-transform:type-definitions~TransformOptions} cliOptions - The options provided via CLI. * @private */ function main(args, cliOptions) { @@ -117,9 +117,7 @@ function main(args, cliOptions) { cli.debug('Passed options:\n' + JSON.stringify(cliOptions, null, 4)); - // transform with options - - return transform(cliOptions) + transform(cliOptions) .then(cli.info) .catch(error); } @@ -131,5 +129,5 @@ cli.width = 120; cli.setUsage(usage); cli.setApp(packagePath); cli.enable('version', 'status', 'timeout'); -cli.parse(options); +cli.parse(cliOptionsSchema); cli.main(main); diff --git a/src/reader.js b/src/reader.js index 368114a..da0998d 100644 --- a/src/reader.js +++ b/src/reader.js @@ -4,9 +4,9 @@ import { Buffer } from 'buffer'; import path from 'path'; import fs from 'fs'; import isStream from 'is-stream'; -import stringify from 'json-stringify-safe'; -import logger from 'cli'; -import { readerOptionsSchema } from './validation/options-schema'; +import stringify from 'json-stringify-safe'; // TODO remove +import logger from 'cli'; // TODO remove +import { readOptionsSchema } from './validation/options-schema'; import Joi from './validation/joi-extensions'; import { UTF8, @@ -61,7 +61,7 @@ function readFromStream(readable, origin) { /** * Reads the data from a given JS or JSON source. * - * @param {ReaderOptions} options - Contains the JS/JSON source reference to read from. + * @param {ReadOptions} options - Contains the JS/JSON source reference to read from. * @returns {Promise.} Contains the read JS object. * @private */ @@ -108,7 +108,7 @@ async function readJs(options) { * *NOTE:* this function does not understand multi-document sources, it throws * exception on those. * - * @param {ReaderOptions} options - Contains the YAML source reference to read from. + * @param {ReadOptions} options - Contains the YAML source reference to read from. * @returns {Promise.} Contains the read JS object. * @private */ @@ -149,7 +149,7 @@ async function readYaml(options) { /** * Reads a particular content type from a source provided in the passed `options`. * - * @param {ReaderOptions} options - The read options. + * @param {ReadOptions} options - The read options. * @returns {Promise} The result. * @resolve {string} Resolves with JS object result. * @reject {ValidationError} If any `options` validation occurs. @@ -181,13 +181,13 @@ async function readYaml(options) { * .catch(console.error); */ export async function read(options) { - const assertedOptions = await Joi.validate(options, readerOptionsSchema); - switch (assertedOptions.origin) { + const validatedOptions = await Joi.validate(options, readOptionsSchema); + switch (validatedOptions.origin) { case TYPE_JS: case TYPE_JSON: - return readJs(assertedOptions); + return readJs(validatedOptions); default: - return readYaml(assertedOptions); + return readYaml(validatedOptions); } } diff --git a/src/transformer.js b/src/transformer.js index 663a81c..2efcfac 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -1,10 +1,8 @@ -import logger from 'cli'; -import path from 'path'; -import fs from 'fs'; -import isStream from 'is-stream'; +import logger from 'cli'; // TODO remove +import Joi from './validation/joi-extensions'; import { read } from './reader'; import { write } from './writer'; -import { EXT_TO_TYPE_MAP } from './constants'; +import { transformOptionsSchema } from './validation/options-schema'; /** * @module jy-transform:transformer @@ -13,104 +11,15 @@ import { EXT_TO_TYPE_MAP } from './constants'; */ /** - * Applies the `middleware` function to `object` if is passed. - * - * @param {Object} object - The object to alter by passed `middleware` function. - * @param {Function} [middleware] - The function to alter `object`. - * @returns {Object} The passed `object` which could be altered by optional `middleware` function. - * @private - */ -const callMiddlewareIfExists = async (object, middleware) => { - if (middleware !== undefined && typeof middleware !== 'function') { - throw new TypeError('The provided middleware is not a Function type'); - } - if (!middleware) { - return object; - } - return middleware(object); -}; - -/** - * Turns the destination file name into a name containing a consecutive - * number if it exists. It iterates over the files until it finds a file - * name which does not exist. - * - * @param {string} dest - The destination file. - * @returns {string} - A consecutive file name or the original one if - * `dest` file does not exist. - * @private - */ -function getConsecutiveDestName(dest) { - let tmpDest = dest; - let i = 0; - const destDirName = path.dirname(tmpDest); - const ext = path.extname(tmpDest); - const basename = path.basename(tmpDest, ext); - while (fs.existsSync(tmpDest)) { - tmpDest = path.join(destDirName, basename + '(' + (i += 1) + ')' + ext); - } - return tmpDest; -} - -/** - * Checks if passed `object` is a file stream instance. - * - * @param {*} object - The object to check. - * @returns {boolean} A `true` if passed `object` is a file stream instance, else `false`. - * @private - */ -function isFileStream(object) { - return isStream(object) && object.path; -} - -/** - * Returns the passes `dest` value or an adapted destination path (the latter if `target` is defined an differs from - * destinations path extension). - * - * @param {string} dest - The destination path. - * @param {string} [target] - The target file type of destination. - * @returns {string} The `dest` value or an adapted destination path. - * @private - */ -function adaptTargetPathType(dest, target) { - if (target) { - const tmpDest = dest; - const destDirName = path.dirname(tmpDest); - const ext = path.extname(tmpDest); - const basename = path.basename(tmpDest, ext); - let destType = ext; - if (ext.charAt(0) === '.') { - destType = ext.substr(1); - } - if (EXT_TO_TYPE_MAP[destType] !== target) { - destType = target; - } - return path.join(destDirName, basename + '.' + destType); - } - return dest; -} - -// //////////////////////////////////////////////////////////////////////////// -// PUBLIC API -// //////////////////////////////////////////////////////////////////////////// - -/** - * The entry method for all transformation accepting a configuration object and + * The entry method for all transformations accepting a configuration object and * an (optional) middleware function. It executes the transformation logic. * * 1. Input (read) * 2. Transform [ + Middleware] * 3. Output (write). * - * @param {TransformerOptions} options - The configuration for a transformation. - * @param {Function} [middleware] - This middleware Promise can be used to - * intercept the JSON object for altering the passed JSON, the function signature is: - * ``` - * async function(object) - * ``` - *

      - * **NOTE:** the Promise has to return the processed JS object. - * @returns {Promise} The result. + * @param {TransformOptions} options - The configuration for a transformation. + * @returns {Promise} The transformation result. * @resolve {string} With the transformation result as message (e.g. to be logged by caller). * @reject {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. * @reject {ValidationError} If any `options` validation occurs. @@ -118,16 +27,19 @@ function adaptTargetPathType(dest, target) { * @public * @example * import { transform } from 'jy-transform'; - * const options = {...}; - * - * const middleware = async (object) { - * object.myproperty = 'new value'; - * return object; + * const options = { + * src: 'foo/bar.yaml', // From YAML file... + * transform: async (object) => { // ...with exchanging value... + * object.foo = 'new value'; + * return object; + * }, + * target: 'foo/bar.json', // ...to a new JSON file. + * indent: 4, * }; * * // ---- Promise style: * - * transform(options, middleware) + * transform(options) * .then(console.log) * .catch(console.error); * @@ -135,29 +47,25 @@ function adaptTargetPathType(dest, target) { * // ---- async/await style: * * try { - * const msg = await transform(options, middleware); + * const msg = await transform(options); * console.log(msg); * } catch (err) { * console.error(err.stack); * }; */ -export async function transform(options, middleware) { +export async function transform(options) { // logger.debug('transform'); TODO remove - let object = await read(options); - object = await callMiddlewareIfExists(object, middleware); + const validatedOptions = await Joi.validate(options, transformOptionsSchema); - // Here we have to check the options.dest, if not set we allow to use the source (string as - // file path or File Writable with path) to be used as destination (and even allow to overwrite)! - if (!options.dest && (typeof options.src === 'string' || isFileStream(options.src))) { - if (options.force) { - // Overwrite the source if target is set and does not differ! - options.dest = adaptTargetPathType((options.src.path || options.src), options.target); - } else { - options.dest = getConsecutiveDestName(adaptTargetPathType((options.src.path || options.src), options.target)); - } - } + console.log('Passed transform options:\n' + JSON.stringify(validatedOptions, null, 4)); // TODO remove - return write(object, options); + let object = await read(validatedOptions); + object = await validatedOptions.transform(object); + + if (options.dest) { + validatedOptions.dest = options.dest; // Do not loose ref to original object! + } + return write(object, validatedOptions); } export default { diff --git a/src/type-definitions.js b/src/type-definitions.js index 5f58538..755a398 100644 --- a/src/type-definitions.js +++ b/src/type-definitions.js @@ -48,19 +48,19 @@ /** * The configuration properties provided to the `read` function. - * @typedef {object} ReaderOptions - * @property {(string|Stream.Readable|object)} src - The source (if `string` type is treated as a file path). - * @property {string} [origin=yaml] - The origin type. + * @typedef {object} ReadOptions + * @property {(string|Stream.Readable|object)} src - The source (if `string` type it is treated as a file path). + * @property {string} [origin=yaml] - The source origin type. * @property {string} [imports=undefined] - The exports name for reading from JS source files or objects only. * @public */ /** - * The writer configuration properties provided to the `write` function. - * @typedef {object} WriterOptions - * @property {(string|Stream.Writable|object)} dest - The destination (if `string` type is treated as a file path). - * @property {string} [target=js] - The target type. - * @property {number} [indent=2] - The indention value for pretty-print of output. + * The configuration properties provided to the `write` function. + * @typedef {object} WriteOptions + * @property {(string|Stream.Writable|object)} dest - The destination (if `string` type it is treated as a file path). + * @property {string} [target=js] - The destination target type. + * @property {number} [indent=2] - The indentation value for pretty-print of output. * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. * @property {string} [force=false] - Force overwriting of existing output files on write phase. * @public @@ -68,15 +68,28 @@ /** * The configuration properties provided to the `transform` function. - * @typedef {object} TransformerOptions - * @property {(string|Stream.Readable|object)} src - The source (if `string` type is treated as a file path). - * @property {string} [origin=yaml] - The origin type. - * @property {string} [imports=undefined] - The exports name for reading from JS source files - * or objects only. - * @property {(string|Stream.Writable|object)} [dest] - The destination (if `string` type is treated as a file path). - * @property {string} [target=js] - The target type. - * @property {number} [indent=2] - The indention value for pretty-print of output. - * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. + * @typedef {object} TransformOptions + * @property {(string|Stream.Readable|object)} src - The _read_ source (if `string` type it is treated as a file + * path). + * @property {string} [origin=yaml] - The _read_ source origin type. + * @property {string} [imports=undefined] - The _read_ exports name for reading from JS source files or + * objects only. + * @property {Function} [transform] - The option is a _transformation_ function with the following + * signature: + *

      + * ``` + * [async|Promise] function(object) + * ``` + * @property {(string|Stream.Writable|object)} [dest] - The _write_ destination (if `string` type it is treated as a + * file path). This property could be optional in case we infer a + * value from `src` which is then either a string or a file stream + * where can get the file path from. If this detection process + * cannot be fulfilled then the property is `undefined` and the + * transform process will fail with a `ValidationError` on write + * phase. + * @property {string} [target=js] - The _write_ target type. + * @property {number} [indent=2] - The _write_ indentation value for pretty-print of output. + * @property {string} [exports=undefined] - The _write_ exports name for usage in JS destination files only. * @property {string} [force=false] - Force overwriting of existing output files on write phase. * @public */ diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-helper.js index 04bde7f..30be3da 100644 --- a/src/validation/options-schema-helper.js +++ b/src/validation/options-schema-helper.js @@ -76,7 +76,63 @@ export const inferTargetDefault = (context) => { return type; }; +/** + * Checks if passed `object` is a file stream instance. + * + * @param {*} object - The object to check. + * @returns {boolean} A `true` if passed `object` is a file stream instance, else `false`. + * @private + */ +export const isFileStream = (object) => { + return isStream(object) && object.path; +}; + +/** + * Returns the passes `dest` value or an adapted destination path (the latter if `target` is defined an differs from + * destinations path extension). + * + * @param {string} dest - The destination path. + * @param {string} [target] - The target file type of destination. + * @returns {string} The `dest` value or an adapted destination path. + * @private + */ +export const adaptTargetPathType = (dest, target) => { + if (target) { + const tmpDest = dest; + const destDirName = path.dirname(tmpDest); + const ext = path.extname(tmpDest); + const basename = path.basename(tmpDest, ext); + let destType = ext; + if (ext.charAt(0) === '.') { + destType = ext.substr(1); + } + if (EXT_TO_TYPE_MAP[destType] !== target) { + destType = target; + } + return path.join(destDirName, basename + '.' + destType); + } + return dest; +}; + +/** + * This function is used to infer a _default_ value in case `options.dest` is not defined. + * Checks if `context.src` is either a string or a file stream where can get the file path from. + * If this detection process cannot be fulfilled then the function returns `undefined`. + * + * @param {Object} context - The validation context. + * @returns {string|undefined} The adapted `dest` path if possible, or `undefined`. + */ +export const inferDestDefaultFromSrc = (context) => { + if (typeof context.src === 'string' || isFileStream(context.src)) { + return adaptTargetPathType((context.src.path || context.src), context.target); + } + return undefined; // TODO how to handle this??? Throw Error? +}; + export default { + isFileStream, + adaptTargetPathType, inferOriginDefault, inferTargetDefault, + inferDestDefaultFromSrc, }; diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js index 457177b..778d907 100644 --- a/src/validation/options-schema.js +++ b/src/validation/options-schema.js @@ -1,6 +1,7 @@ import { Stream } from 'stream'; import Joi from './joi-extensions'; import { + inferDestDefaultFromSrc, inferOriginDefault, inferTargetDefault, } from './options-schema-helper'; @@ -24,12 +25,12 @@ import { */ /** - * The prepared {@link external:joi.JoiSchema} for validating the {@link Reader} options. + * The prepared {@link external:joi.JoiSchema} for validating the {@link ReadOptions}. * @type {JoiSchema} * @constant * @private */ -export const readerOptionsSchema = Joi.object().keys({ +export const readOptionsSchema = Joi.object().keys({ src: Joi .alternatives().try( Joi.string() @@ -47,15 +48,14 @@ export const readerOptionsSchema = Joi.object().keys({ then: Joi .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferOriginDefault, - 'tried origin default inferred from src type if not set (Stream.Readable)'), + .default(inferOriginDefault, 'try origin resolution from src type if not set (Stream.Readable)'), otherwise: Joi .when('src', { is: Joi.string(), then: Joi .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferOriginDefault, 'origin resolving from src type if latter not set (String)'), + .default(inferOriginDefault, 'try origin resolution from src type if latter not set (String)'), otherwise: Joi // else could only be JS Object .string() .valid(TYPE_JS) @@ -67,16 +67,17 @@ export const readerOptionsSchema = Joi.object().keys({ .string() .validEs6Identifier() .description('The name of property to import while reading a JS input source.'), -}).unknown() - .required(); +}).default() + .required() + .unknown(); /** - * The prepared {@link external:joi.JoiSchema} for validating the {@link Writer} options. + * The prepared {@link external:joi.JoiSchema} for validating the {@link WriteOptions}. * @type {JoiSchema} * @constant * @private */ -export const writerOptionsSchema = Joi.object().keys({ +export const writeOptionsSchema = Joi.object().keys({ dest: Joi .alternatives().try( Joi.string() @@ -87,6 +88,78 @@ export const writerOptionsSchema = Joi.object().keys({ ) .required() .description('The output destination (if string type is treated as a file path).'), + target: Joi + .when('dest', { + is: Joi.object().type(Stream.Writable), + then: Joi + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(inferTargetDefault, 'try target resolution from dest type if not set (Stream.Writable)'), + otherwise: Joi + .when('dest', { + is: Joi.string(), + then: Joi + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(inferTargetDefault, 'try target resolution from dest type if latter not set (String)'), + otherwise: Joi // check + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(TYPE_JS), + }), + }) + .description('The target type of output.'), + exports: Joi + .string() + .validEs6Identifier() + .description('The name of property to export while writing a JS object to a JS output destination.'), + indent: Joi + .when('target', { + is: TYPE_YAML, + then: Joi + .number() + .integer() + .min(MIN_YAML_INDENT) // Must be 2 for YAML type! + .max(MAX_INDENT) + .default(DEFAULT_INDENT), + otherwise: Joi + .number() + .integer() + .min(MIN_INDENT) + .max(MAX_INDENT) + .default(DEFAULT_INDENT), + }) + .description('The indention value for pretty-print of output.'), + force: Joi + .boolean() + .default(DEFAULT_FORCE_FILE_OVERWRITE) + .description('Force overwriting of existing output files on write phase.'), +}).default() + .required() + .unknown(); + +/** + * The prepared {@link external:joi.JoiSchema} for validating the {@link TransformOptions}. + * @type {JoiSchema} + * @constant + * @private + */ +export const transformOptionsSchema = readOptionsSchema.concat(Joi.object().keys({ + transform: Joi + .func() + .arity(1) + .default(object => object) + .description('The transformation function to alter a read object.'), + dest: Joi + .alternatives().try( + Joi.string() + .min(1) + .label('dest - OUTPUT-FILE'), + Joi.object().type(Stream.Writable), + Joi.object().type(Object), + ) + .default(inferDestDefaultFromSrc, 'try dest resolution from src if not set') // TODO message!? + .description('The output destination (if string type is treated as a file path).'), target: Joi .when('dest', { is: Joi.object().type(Stream.Writable), @@ -101,7 +174,7 @@ export const writerOptionsSchema = Joi.object().keys({ then: Joi .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferTargetDefault, 'try target resolving from dest type if latter not set (String)'), + .default(inferTargetDefault, 'try target resolution from dest type if latter not set (String)'), otherwise: Joi // check .string() .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) @@ -134,10 +207,12 @@ export const writerOptionsSchema = Joi.object().keys({ .boolean() .default(DEFAULT_FORCE_FILE_OVERWRITE) .description('Force overwriting of existing output files on write phase.'), -}).unknown() - .required(); +}).default() + .required() +); export default { - readerOptionsSchema, - writerOptionsSchema, + readOptionsSchema, + writeOptionsSchema, + transformOptionsSchema, }; diff --git a/src/writer.js b/src/writer.js index 51285c3..595c267 100644 --- a/src/writer.js +++ b/src/writer.js @@ -1,4 +1,4 @@ -import logger from 'cli'; +import logger from 'cli'; // TODO remove import fs from 'fs'; import isStream from 'is-stream'; import jsYaml from 'js-yaml'; @@ -15,7 +15,7 @@ import { UTF8 } from './constants'; import Joi from './validation/joi-extensions'; -import { writerOptionsSchema } from './validation/options-schema'; +import { writeOptionsSchema } from './validation/options-schema'; /** * @module jy-transform:writer @@ -85,8 +85,7 @@ async function serializeJsToJsonString(object, indent) { * name which does not exist. * * @param {string} dest - The destination file. - * @returns {string} - A consecutive file name or the original one if - * `dest` file does not exist. + * @returns {string} - A consecutive file name or the original one if `dest` file does not exist. * @private */ function getConsecutiveDestName(dest) { @@ -184,8 +183,8 @@ function writeToStream(object, dest, target) { /** * Writes a JS object to a YAML destination. * - * @param {Object} object - The JS object to write into YAML destination. - * @param {WriterOptions} options - The write destination and indention. + * @param {Object} object - The JS object to write into YAML destination. + * @param {WriteOptions} options - The write destination and indention. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} @@ -215,8 +214,8 @@ async function writeYaml(object, options) { /** * Writes a JS object to a JSON destination. * - * @param {Object} object - The JS object to write into JSON destination. - * @param {WriterOptions} options - The write destination and indention. + * @param {Object} object - The JS object to write into JSON destination. + * @param {WriteOptions} options - The write destination and indention. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} @@ -238,8 +237,8 @@ async function writeJson(object, options) { /** * Writes a JS object to a JS destination. The object is prefixed by `module.exports[.${options.exports}] = `. * - * @param {Object} object - The JSON to write into JS destination. - * @param {WriterOptions} options - The write destination and indention. + * @param {Object} object - The JSON to write into JS destination. + * @param {WriteOptions} options - The write destination and indention. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} @@ -268,8 +267,8 @@ async function writeJs(object, options) { /** * Writes the passe JS object to a particular destination described by the passed `options`. * - * @param {Object} object - The JS source object to write. - * @param {WriterOptions} options - The write options. + * @param {Object} object - The JS source object to write. + * @param {WriteOptions} options - The write options. * @returns {Promise} The result. * @resolve {string} With the write success message. * @reject {Error} If any write error occurs. @@ -316,12 +315,12 @@ async function writeJs(object, options) { * .catch(console.error); */ export async function write(object, options) { - console.log('OPTIONS ON WRITE ===> ' + JSON.stringify(options, null, 4)) - const validatedOptions = await Joi.validate(options, writerOptionsSchema); + // console.log('OPTIONS ON WRITE ===> ' + JSON.stringify(options, null, 4)) // TODO remove + const validatedOptions = await Joi.validate(options, writeOptionsSchema); // HINT: we have to use the original options object because the caller must not loose the reference to options.dest, // so we copy everything here except the assertedOptions.dest (joi does not return the original reference)! - Object.assign(options, { target: validatedOptions.target }, { exports: validatedOptions.exports }, + Object.assign(options, { target: validatedOptions.target }, { exports: validatedOptions.exports }, // TODO does this work without full copy? { indent: validatedOptions.indent }, { force: validatedOptions.force }); validatedOptions.dest = options.dest; switch (validatedOptions.target) { diff --git a/test/unit/test-transformer.js b/test/unit/test-transformer.js index d7d5bd8..5edb68f 100644 --- a/test/unit/test-transformer.js +++ b/test/unit/test-transformer.js @@ -50,21 +50,20 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { /** * Transformation middleware changing value for `foo` property. * - * @param {Object} json - To transform. + * @param {Object} object - To transform. */ - const middlewareFunc = async (json) => { - json.foo = EXPECTED_VALUE; - return json; + const transformFunc = async (object) => { + object.foo = EXPECTED_VALUE; + return object; }; /** * Helper method which asserts the successful transformation. * * @param {Object} options - The transformation options. - * @param {Function} middleware - The transformation middleware. */ - function assertTransformSuccess(options, middleware) { - return transform(options, middleware) + function assertTransformSuccess(options) { + return transform(options) .then((msg) => { logger.info(msg); const stats = fsExtra.statSync(options.dest); @@ -79,11 +78,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { * Helper method which asserts the successful transformation. * * @param {Object} options - The transformation options. - * @param {Function} middleware - The transformation middleware. */ - async function assertYamlTransformSuccess(options, middleware) { + async function assertYamlTransformSuccess(options) { expect.assertions(3); - const msg = await transform(options, middleware); + const msg = await transform(options); logger.info(msg); expect(msg).toEqual(expect.any(String)); const stats = fsExtra.statSync(options.dest); @@ -94,9 +92,20 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { } describe('Testing transform with middleware', () => { - it('should throw TypeError if middleware passed is not a function type', async () => { + it('should throw ValidationError if middleware passed is not a function type', async () => { expect.assertions(1); - await expect(transform({ src: {}, dest: {} }, 'not a function')).rejects.toBeInstanceOf(TypeError); + await expect(transform({ + src: {}, + transform: 'not a function', + dest: {}, + })).rejects.toMatchObject({ name: 'ValidationError', isJoi: true }); + }); + + it('should throw ValidationError if options.dest is not set and cannot be resolved from options.src type', async () => { + expect.assertions(1); + await expect(transform({ + src: {}, // we cannot infer destination from this type! + })).rejects.toMatchObject({ name: 'ValidationError', isJoi: true }); }); it('should not fail if middleware passed is returning a Promise', () => { @@ -104,8 +113,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { const returningPromise = async (object) => { return object; }; - return expect(transform({ src: {}, dest: {} }, returningPromise)) - .resolves.toBe('Writing JS to options.dest successful.'); + return expect(transform({ + src: {}, + transform: returningPromise, + dest: {}, + })).resolves.toBe('Writing JS to options.dest successful.'); }); it('should not fail if middleware passed is not returning a Promise', () => { @@ -113,8 +125,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { const notReturningPromise = (object) => { return object; }; - return expect(transform({ src: {}, dest: {} }, notReturningPromise)) - .resolves.toBe('Writing JS to options.dest successful.'); + return expect(transform({ + src: {}, + transform: notReturningPromise, + dest: {}, + })).resolves.toBe('Writing JS to options.dest successful.'); }); }); @@ -132,8 +147,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const msg = await transform({ src: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml'), + transform: transformFunc, dest: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.js'), - }, middlewareFunc); + }); logger.info(msg); const stats = fs.statSync(DEST); expect(stats.isFile()).toBeTruthy(); @@ -151,9 +167,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const options = { src: path.resolve(SRC), + transform: transformFunc, dest: path.resolve(DEST), }; - await assertTransformSuccess(options, middlewareFunc); + await assertTransformSuccess(options); }); }); @@ -165,9 +182,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const options = { src: path.resolve(SRC), + transform: transformFunc, dest: path.resolve(DEST), }; - await assertTransformSuccess(options, middlewareFunc); + await assertTransformSuccess(options); }); }); @@ -179,9 +197,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const options = { src: path.resolve(SRC), + transform: transformFunc, dest: path.resolve(DEST), }; - await assertTransformSuccess(options, middlewareFunc); + await assertTransformSuccess(options); }); }); @@ -193,9 +212,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const options = { src: path.resolve(SRC), + transform: transformFunc, dest: path.resolve(DEST), }; - await assertTransformSuccess(options, middlewareFunc); + await assertTransformSuccess(options); }); }); @@ -208,10 +228,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const options = { src: path.resolve(SRC), + transform: transformFunc, dest: path.resolve(DEST), }; - transform(options, middlewareFunc) + transform(options) .then((msg) => { logger.info(msg); const stats = fsExtra.statSync(options.dest); @@ -245,10 +266,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const options = { src: path.resolve(SRC), + transform: transformFunc, dest: path.resolve(DEST), }; - transform(options, middlewareFunc) + transform(options) .then((msg) => { logger.info(msg); const stats = fsExtra.statSync(options.dest); @@ -282,9 +304,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const options = { src: path.resolve(SRC), + transform: transformFunc, dest: path.resolve(DEST), }; - await assertTransformSuccess(options, middlewareFunc); + await assertTransformSuccess(options); }); }); @@ -296,9 +319,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const options = { src: path.resolve(SRC), + transform: transformFunc, dest: path.resolve(DEST), }; - await assertYamlTransformSuccess(options, middlewareFunc); + await assertYamlTransformSuccess(options); }); }); @@ -310,9 +334,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { expect.assertions(2); const options = { src: path.resolve(SRC), + transform: transformFunc, dest: path.resolve(DEST), }; - await assertTransformSuccess(options, middlewareFunc); + await assertTransformSuccess(options); }); }); }); diff --git a/test/unit/validation/test-options-schema.js b/test/unit/validation/test-options-schema.js index 9782ab7..0044b70 100644 --- a/test/unit/validation/test-options-schema.js +++ b/test/unit/validation/test-options-schema.js @@ -16,8 +16,8 @@ import { MAX_INDENT, } from '../../../src/constants'; import { - readerOptionsSchema, - writerOptionsSchema, + readOptionsSchema, + writeOptionsSchema, } from '../../../src/validation/options-schema'; import Joi from '../../../src/validation/joi-extensions'; @@ -30,8 +30,8 @@ import Joi from '../../../src/validation/joi-extensions'; /** * Expect a `ValidationError` for a given options function. * - * @param {ReaderOptions|WriterOptions} invalidOptions - The options which potentially produce the error. - * @param {Schema} schema - The validation schema. + * @param {ReadOptions|WriteOptions} invalidOptions - The options which potentially produce the error. + * @param {Schema} schema - The validation schema. * @private */ function expectOptionsValidationError(invalidOptions, schema) { @@ -45,8 +45,8 @@ function expectOptionsValidationError(invalidOptions, schema) { /** * Expect a validation success for a given options. * - * @param {ReaderOptions|WriterOptions} validOptions - The options which should be correct. - * @param {Schema} schema - The validation schema. + * @param {ReadOptions|WriteOptions} validOptions - The options which should be correct. + * @param {Schema} schema - The validation schema. * @private */ function expectOptionsValidationSuccess(validOptions, schema) { @@ -55,13 +55,13 @@ function expectOptionsValidationSuccess(validOptions, schema) { } describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { - describe('Testing readerOptionsSchema validation', () => { + describe('Testing readOptionsSchema validation', () => { it('should reject when options is missing (null)', () => - expectOptionsValidationError(null, readerOptionsSchema) + expectOptionsValidationError(null, readOptionsSchema) ); it('should reject when options is missing (undefined)', () => - expectOptionsValidationError(undefined, readerOptionsSchema) + expectOptionsValidationError(undefined, readOptionsSchema) ); it('should set all defaults', async () => { @@ -70,7 +70,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.yaml', dest: './test/tmp/test-data.js', }; - const validatedOptions = await Joi.validate(options, readerOptionsSchema); + const validatedOptions = await Joi.validate(options, readOptionsSchema); expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); expect(validatedOptions.imports).toBe(DEFAULT_JS_IMPORTS_IDENTIFIER); }); @@ -80,7 +80,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { const options = { src: './test/data/test-data.js', // non default type }; - const validatedOptions = await Joi.validate(options, readerOptionsSchema); + const validatedOptions = await Joi.validate(options, readOptionsSchema); expect(validatedOptions.origin).toBe(TYPE_JS); }); @@ -90,7 +90,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { origin: TYPE_JS, src: new Stream.Readable(), // no inference possible }; - const validatedOptions = await Joi.validate(options, readerOptionsSchema); + const validatedOptions = await Joi.validate(options, readOptionsSchema); expect(validatedOptions.origin).toBe(TYPE_JS); }); @@ -100,7 +100,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: {}, dest: 'some-file', }; - const validatedOptions = await Joi.validate(options, readerOptionsSchema); + const validatedOptions = await Joi.validate(options, readOptionsSchema); expect(validatedOptions.origin).toBe(TYPE_JS); }); }); @@ -110,18 +110,18 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { expectOptionsValidationError({ src: './test', dest: 'some-file', - }, readerOptionsSchema) + }, readOptionsSchema) ); it('should reject when options.src is undefined', () => - expectOptionsValidationError({ dest: 'some-file' }, readerOptionsSchema) + expectOptionsValidationError({ dest: 'some-file' }, readOptionsSchema) ); it('should reject when options.src is null', () => expectOptionsValidationError({ src: null, dest: 'some-file', - }, readerOptionsSchema) + }, readOptionsSchema) ); it('should resolve to default origin ' + DEFAULT_ORIGIN + ' when options.src is Stream.Readable and ' + @@ -132,7 +132,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { dest: new Stream.Writable(), target: TYPE_YAML, }; - const validatedOptions = await Joi.validate(options, readerOptionsSchema); + const validatedOptions = await Joi.validate(options, readOptionsSchema); expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); expect(validatedOptions.target).toBe(TYPE_YAML); }); @@ -144,7 +144,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data', dest: 'some-file' }; - const validatedOptions = await Joi.validate(options, readerOptionsSchema); + const validatedOptions = await Joi.validate(options, readOptionsSchema); expect(validatedOptions.origin).toBe(DEFAULT_ORIGIN); }); @@ -153,7 +153,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data', dest: 'some-file', origin: TYPE_JS, - }, readerOptionsSchema); + }, readOptionsSchema); }); it('should resolve when options.origin has valid target ' + TYPE_JSON, () => { @@ -161,7 +161,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data-json', dest: 'some-file', origin: TYPE_JSON, - }, readerOptionsSchema); + }, readOptionsSchema); }); it('should resolve when options.origin has valid target ' + TYPE_YAML, () => { @@ -169,7 +169,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data-yaml', dest: 'some-file', origin: TYPE_YAML, - }, readerOptionsSchema); + }, readOptionsSchema); }); it('should reject when options.origin is null', () => @@ -177,7 +177,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data-yaml', dest: 'some-file', origin: null, - }, readerOptionsSchema) + }, readOptionsSchema) ); it('should reject when options.origin is not allowed value', () => @@ -185,7 +185,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data-yaml', dest: 'some-file', origin: 'not-allowed', - }, readerOptionsSchema) + }, readOptionsSchema) ); }); @@ -196,7 +196,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', imports: nonStringIdentifier, - }, readerOptionsSchema) + }, readOptionsSchema) ); const invalidIdentifier = '#3/-'; @@ -205,7 +205,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', imports: invalidIdentifier, - }, readerOptionsSchema) + }, readOptionsSchema) ); const validIdentifier = 'bar'; @@ -214,18 +214,18 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', imports: validIdentifier, - }, readerOptionsSchema) + }, readOptionsSchema) ); }); }); - describe('Testing writerOptionsSchema validation', () => { + describe('Testing writeOptionsSchema validation', () => { it('should reject when options is missing (null)', () => - expectOptionsValidationError(null, writerOptionsSchema) + expectOptionsValidationError(null, writeOptionsSchema) ); it('should reject when options is missing (undefined)', () => - expectOptionsValidationError(undefined, writerOptionsSchema) + expectOptionsValidationError(undefined, writeOptionsSchema) ); it('should set all defaults', async () => { @@ -233,7 +233,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { const options = { dest: './test/tmp/test-data.js', }; - const validatedOptions = await Joi.validate(options, writerOptionsSchema); + const validatedOptions = await Joi.validate(options, writeOptionsSchema); expect(validatedOptions.target).toBe(DEFAULT_TARGET); expect(validatedOptions.indent).toBe(DEFAULT_INDENT); expect(validatedOptions.exports).toBe(DEFAULT_JS_EXPORTS_IDENTIFIER); @@ -245,7 +245,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { const options = { dest: 'some-file.yml', // non default type }; - const validatedOptions = await Joi.validate(options, writerOptionsSchema); + const validatedOptions = await Joi.validate(options, writeOptionsSchema); expect(validatedOptions.target).toBe(TYPE_YAML); }); @@ -255,7 +255,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { target: TYPE_YAML, dest: new Stream.Writable(), // no inference possible }; - const validatedOptions = await Joi.validate(options, writerOptionsSchema); + const validatedOptions = await Joi.validate(options, writeOptionsSchema); expect(validatedOptions.target).toBe(TYPE_YAML); }); @@ -264,20 +264,20 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { const options = { dest: {}, }; - const validatedOptions = await Joi.validate(options, writerOptionsSchema); + const validatedOptions = await Joi.validate(options, writeOptionsSchema); expect(validatedOptions.target).toBe(TYPE_JS); }); describe('Testing options.dest schema validation', () => { it('should reject when options.dest is undefined', () => - expectOptionsValidationError({ src: './test/data/test-data.js' }, writerOptionsSchema) + expectOptionsValidationError({ src: './test/data/test-data.js' }, writeOptionsSchema) ); it('should reject when options.dest is null', () => expectOptionsValidationError({ src: './test/data/test-data.js', dest: null, - }, writerOptionsSchema) + }, writeOptionsSchema) ); it('should resolve output when options.dest is Stream.Writable and options.target is set', async () => { @@ -286,7 +286,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { dest: new Stream.Writable(), target: TYPE_YAML, }; - const validatedOptions = await Joi.validate(options, writerOptionsSchema); + const validatedOptions = await Joi.validate(options, writeOptionsSchema); expect(validatedOptions.target).toBe(TYPE_YAML); }); @@ -296,7 +296,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { const options = { dest: fs.createWriteStream('./test/tmp/test-data.yaml'), }; - const validatedOptions = await Joi.validate(options, writerOptionsSchema); + const validatedOptions = await Joi.validate(options, writeOptionsSchema); expect(validatedOptions.target).toBe(TYPE_YAML); }); @@ -306,7 +306,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { const options = { dest: new Stream.Writable(), }; - const validatedOptions = await Joi.validate(options, writerOptionsSchema); + const validatedOptions = await Joi.validate(options, writeOptionsSchema); expect(validatedOptions.target).toBe(DEFAULT_TARGET); }); }); @@ -316,7 +316,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { const options = { dest: 'some-file' }; - const validatedOptions = await Joi.validate(options, writerOptionsSchema); + const validatedOptions = await Joi.validate(options, writeOptionsSchema); expect(validatedOptions.target).toBe(DEFAULT_TARGET); }); @@ -325,7 +325,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', target: TYPE_JS, - }, writerOptionsSchema); + }, writeOptionsSchema); }); it('should resolve when options.target has valid target ' + TYPE_JSON, () => { @@ -333,7 +333,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.json', dest: 'some-file', target: TYPE_JS, - }, writerOptionsSchema); + }, writeOptionsSchema); }); it('should resolve when options.target has valid target ' + TYPE_YAML, () => { @@ -341,7 +341,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.yaml', dest: 'some-file', target: TYPE_YAML, - }, writerOptionsSchema); + }, writeOptionsSchema); }); it('should reject when options.target is null', () => @@ -349,7 +349,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', target: null, - }, writerOptionsSchema) + }, writeOptionsSchema) ); it('should reject when options.target is not allowed value', () => @@ -357,7 +357,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', target: 'not-allowed', - }, writerOptionsSchema) + }, writeOptionsSchema) ); }); @@ -368,7 +368,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', exports: nonStringIdentifier, - }, writerOptionsSchema) + }, writeOptionsSchema) ); const invalidIdentifier = '#3/-'; @@ -377,7 +377,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', exports: invalidIdentifier, - }, writerOptionsSchema) + }, writeOptionsSchema) ); const validIdentifier = 'bar'; @@ -386,7 +386,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', exports: validIdentifier, - }, writerOptionsSchema) + }, writeOptionsSchema) ); }); @@ -397,7 +397,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', force: notBoolean, - }, writerOptionsSchema) + }, writeOptionsSchema) ); it('should accept valid value \'false\'', () => @@ -405,7 +405,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', force: false, - }, writerOptionsSchema) + }, writeOptionsSchema) ); it('should accept valid value \'true\'', () => @@ -413,7 +413,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', force: true, - }, writerOptionsSchema) + }, writeOptionsSchema) ); }); @@ -424,7 +424,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', indent: notInteger, - }, writerOptionsSchema) + }, writeOptionsSchema) ); it('should accept valid \'' + MIN_INDENT + '\'', () => @@ -432,7 +432,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', indent: MIN_INDENT, - }, writerOptionsSchema) + }, writeOptionsSchema) ); it('should accept valid \'' + MAX_INDENT + '\'', () => @@ -440,7 +440,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', indent: MAX_INDENT, - }, writerOptionsSchema) + }, writeOptionsSchema) ); it('should reject < \'' + MIN_INDENT + '\'', () => @@ -448,7 +448,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', indent: MIN_INDENT - 1, - }, writerOptionsSchema) + }, writeOptionsSchema) ); it('should reject > \'' + MAX_INDENT + '\'', () => @@ -456,7 +456,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { src: './test/data/test-data.js', dest: 'some-file', indent: MAX_INDENT + 1, - }, writerOptionsSchema) + }, writeOptionsSchema) ); }); }); From 2061abf94eeb0a049e06bb9796b9cb19dddfd8f8 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 13 Jul 2017 01:44:17 +0200 Subject: [PATCH 17/58] More fixes/features and tests 100% except clit tests --- .eslintignore | 3 + .jestrc.js | 5 +- .travis.yml | 1 - API-PRIVATE.md | 43 +++---- CHANGELOG.md | 2 +- MAKE.md | 2 +- Makefile | 4 +- PACKAGE.md | 2 +- package.json | 15 ++- src/reader.js | 30 ++--- src/transformer.js | 5 - src/validation/options-schema-helper.js | 110 +++++++++--------- src/writer.js | 8 +- test/unit/test-transformer.js | 13 ++- .../validation/test-options-schema-helper.js | 102 +++++++++++----- 15 files changed, 182 insertions(+), 163 deletions(-) diff --git a/.eslintignore b/.eslintignore index dc14c97..70b454f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,3 +8,6 @@ lib/ index.js test/data test/tmp + +# TODO reve later when test os done +test/unit/test-cli.js diff --git a/.jestrc.js b/.jestrc.js index c8c231b..639b0dc 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -27,8 +27,8 @@ module.exports = { // '**/test/unit/test-index.js', //'**/test/unit/test-reader.js', // '**/test/unit/test-writer.js', - //'**/test/unit/validation/test-options-schema.js', - '**/test/unit/**/*.js', + // '**/test/unit/validation/test-options-schema.js', + '**/test/unit/**/*.js', //'**/test/unit/validation/test-options-schema-helper.js', // '**/test/test-log-wrapper.js', @@ -37,7 +37,6 @@ module.exports = { //'/*.js!**/test/functional/util/**', //'!**/test/*.js', ], - // testRegex: '\\/test\\/unit\\/.*|\/\\.js?$/', testEnvironment: 'node', testPathIgnorePatterns: [ // '/test/data/.*', diff --git a/.travis.yml b/.travis.yml index eb52e4b..a3e8ef4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ node_js: - "7" - "6" - "5" -- "4" os: - linux diff --git a/API-PRIVATE.md b/API-PRIVATE.md index 413d717..3811e22 100644 --- a/API-PRIVATE.md +++ b/API-PRIVATE.md @@ -598,13 +598,28 @@ values for origin and target depending on the `options.src` or `options.dest` va **See**: [module:validation:options-schema](module:validation:options-schema) * [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) : Object ℗ + * [~inferDestDefaultFromSrc](#module_jy-transform_validation_options-schema-helper..inferDestDefaultFromSrc) ⇒ string \| undefined * [~inferOriginDefault](#module_jy-transform_validation_options-schema-helper..inferOriginDefault) ⇒ string * [~inferTargetDefault](#module_jy-transform_validation_options-schema-helper..inferTargetDefault) ⇒ string - * [~isFileStream](#module_jy-transform_validation_options-schema-helper..isFileStream) ⇒ boolean ℗ - * [~adaptTargetPathType](#module_jy-transform_validation_options-schema-helper..adaptTargetPathType) ⇒ string ℗ - * [~inferDestDefaultFromSrc](#module_jy-transform_validation_options-schema-helper..inferDestDefaultFromSrc) ⇒ string \| undefined + * [~isFileStream(object)](#module_jy-transform_validation_options-schema-helper..isFileStream) ⇒ boolean ℗ + * [~adaptTargetPathType(dest, [target])](#module_jy-transform_validation_options-schema-helper..adaptTargetPathType) ⇒ string ℗ * [~getTypeFromFilePath(pathStr, defaultValue)](#module_jy-transform_validation_options-schema-helper..getTypeFromFilePath) ⇒ string ℗ + + +### jy-transform:validation:options-schema-helper~inferDestDefaultFromSrc ⇒ string \| undefined +This function is used to infer a _default_ value in case `options.dest` is not defined. +Checks if `context.src` is either a string or a file stream where can get the file path from. +If this detection process cannot be fulfilled (i.e. we cannot infer from options.src `Object` +type or a `Readable` type which is not a _file_ stream) then the function returns `undefined`. + +**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Returns**: string \| undefined - The adapted `dest` path if possible, or `undefined`. + +| Param | Type | Description | +| --- | --- | --- | +| context | Object | The validation context. | + ### jy-transform:validation:options-schema-helper~inferOriginDefault ⇒ string @@ -633,10 +648,10 @@ Infers the _target_ type value from current validation context. -### jy-transform:validation:options-schema-helper~isFileStream ⇒ boolean ℗ +### jy-transform:validation:options-schema-helper~isFileStream(object) ⇒ boolean ℗ Checks if passed `object` is a file stream instance. -**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Kind**: inner method of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) **Returns**: boolean - A `true` if passed `object` is a file stream instance, else `false`. **Access**: private @@ -646,11 +661,11 @@ Checks if passed `object` is a file stream instance. -### jy-transform:validation:options-schema-helper~adaptTargetPathType ⇒ string ℗ +### jy-transform:validation:options-schema-helper~adaptTargetPathType(dest, [target]) ⇒ string ℗ Returns the passes `dest` value or an adapted destination path (the latter if `target` is defined an differs from destinations path extension). -**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) +**Kind**: inner method of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) **Returns**: string - The `dest` value or an adapted destination path. **Access**: private @@ -659,20 +674,6 @@ destinations path extension). | dest | string | The destination path. | | [target] | string | The target file type of destination. | - - -### jy-transform:validation:options-schema-helper~inferDestDefaultFromSrc ⇒ string \| undefined -This function is used to infer a _default_ value in case `options.dest` is not defined. -Checks if `context.src` is either a string or a file stream where can get the file path from. -If this detection process cannot be fulfilled then the function returns `undefined`. - -**Kind**: inner constant of [jy-transform:validation:options-schema-helper](#module_jy-transform_validation_options-schema-helper) -**Returns**: string \| undefined - The adapted `dest` path if possible, or `undefined`. - -| Param | Type | Description | -| --- | --- | --- | -| context | Object | The validation context. | - ### jy-transform:validation:options-schema-helper~getTypeFromFilePath(pathStr, defaultValue) ⇒ string ℗ diff --git a/CHANGELOG.md b/CHANGELOG.md index f96ac71..903fff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,7 +62,7 @@ new interface: - [[#53](https://github.com/deadratfink/jy-transform/issues/53)] Update supported node versions: - **CLI & API Backwards Incompatible Change!** - Add travis build for Node.js v8.x. - - Remove travis build for Node.js < v4.x. + - Remove travis build for Node.js < v5.x. - [[#52](https://github.com/deadratfink/jy-transform/issues/52)] Leverage modern ES6 features: - Integrated by [babel](https://babeljs.io/). - Update of dependencies and amount reduced. diff --git a/MAKE.md b/MAKE.md index c5c37a9..a01285e 100644 --- a/MAKE.md +++ b/MAKE.md @@ -9,4 +9,4 @@ Target Call | Description | Dependencies `$ make nsp` | Runs an [Node Security Plattform](https://nodesecurity.io/opensource) check. | `$ make publish` | Publishes module to NPM registry. | `test readme` `$ make readme` | Creates all the documentation parts of the project | -`$ make test` | Runs the test suite, ESLint and a [Node Security Plattform](https://nodesecurity.io/opensource) check. | +`$ make test` | Runs the test suite, ESLint and a [Node Security Plattform](https://nodesecurity.io/opensource) check. | `build` diff --git a/Makefile b/Makefile index b0444ee..92e9717 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ clean: ## Removes generated files in folders ./node_modules, ./lib and ./coverag rm -rf node_modules rm -rf coverage -test: ## Runs the test suite, ESLint and a [Node Security Plattform](https://nodesecurity.io/opensource) check. +test: build ## Runs the test suite, ESLint and a [Node Security Plattform](https://nodesecurity.io/opensource) check. @printf "Running test suite, ESLint and NSP...\n" npm test npm run eslint @@ -26,8 +26,6 @@ nsp: ## Runs an [Node Security Plattform](https://nodesecurity.io/opensource) ch @printf "Running NSP check...\n" npm run nsp -bithound bithound check git@github.com:deadratfink/jy-transform.git - readme: ## Creates all the documentation parts of the project: _README.md_, _MAKE.md_, _PACKAGE.md_ and _API.md_ (the latter based on [JSDoc](http://usejsdoc.org/)). @printf "Create documentation...\n" npm run readme diff --git a/PACKAGE.md b/PACKAGE.md index d741837..4c1adb0 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -59,4 +59,4 @@ npm test ## License -SEE LICENSE IN [LICENSE.md](https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md) +MIT diff --git a/package.json b/package.json index 5d58f84..c7a8ff4 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "url": "https://github.com/deadratfink" }, "contributors": [], - "license": "SEE LICENSE IN [LICENSE.md](https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md)", + "license": "MIT", "repository": { "type": "git", "url": "https://github.com/deadratfink/jy-transform.git" @@ -30,14 +30,14 @@ "nsp": "nsp check" }, "engines": { - "node": ">=4.0.0" + "node": ">=5.0.0" }, "dependencies": { "babel-cli": "~6.24.1", "cli": " ~1.0.1", "is-stream": " ~1.1.0", "joi": "~10.6.0", - "js-yaml": " ~3.8.4", + "js-yaml": " ~3.9.0", "json-stringify-safe": " ~5.0.1", "mkdirp-then": " ~1.2.0", "promisify-es6": "~1.0.2", @@ -46,17 +46,17 @@ "devDependencies": { "babel-eslint": " ~7.2.3", "babel-plugin-transform-flow-strip-types": "~6.22.0", - "babel-preset-env": "~1.5.2", + "babel-preset-env": "~1.6.0", "chalk": "~2.0.1", "codeclimate-test-reporter": " ~0.5.0", "codecov": " ~2.2.0", "coveralls": " ~2.13.1", "cwd": "~0.10.0", "doctoc": " ~1.3.0", - "eslint": "~4.1.0", + "eslint": "~4.2.0", "eslint-config-airbnb-base": "~11.2.0", "eslint-plugin-filenames": "1.2.0", - "eslint-plugin-import": " ~2.6.1", + "eslint-plugin-import": " ~2.7.0", "eslint-plugin-jest": " ~20.0.3", "eslint-plugin-jest-async": " ~1.0.3", "eslint-plugin-jsdoc": " ~3.1.1", @@ -67,8 +67,7 @@ "jsdoc-to-markdown": " ~3.0.0", "nsp": "~2.6.3", "package-json-to-readme": " ~2.0.0", - "winston": " ~2.3.0", - "winston-console-formatter": "~0.3.1" + "winston": " ~2.3.0" }, "preferGlobal": true, "bin": { diff --git a/src/reader.js b/src/reader.js index da0998d..40872aa 100644 --- a/src/reader.js +++ b/src/reader.js @@ -4,8 +4,6 @@ import { Buffer } from 'buffer'; import path from 'path'; import fs from 'fs'; import isStream from 'is-stream'; -import stringify from 'json-stringify-safe'; // TODO remove -import logger from 'cli'; // TODO remove import { readOptionsSchema } from './validation/options-schema'; import Joi from './validation/joi-extensions'; import { @@ -67,25 +65,19 @@ function readFromStream(readable, origin) { */ async function readJs(options) { if (typeof options.src === 'string') { // path to JSON or JS file - try { - const resolvedPath = path.resolve('', options.src); - if (options.imports) { - // eslint-disable-next-line import/no-dynamic-require, global-require - const object = require(resolvedPath)[options.imports]; - // logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(object, null, 4)); TODO remove - if (!object) { // TODO check this as part of config validation? - throw new Error('an identifier string \'' + options.imports + '\' was specified for JS object' + - ' but could not find this object, pls ensure that file ' + options.src + ' contains it.'); - } else { - return object; - } + const resolvedPath = path.resolve('', options.src); + if (options.imports) { + // eslint-disable-next-line import/no-dynamic-require, global-require + const object = require(resolvedPath)[options.imports]; + if (!object) { // TODO check this as part of config validation? + throw new Error('an identifier string \'' + options.imports + '\' was specified for JS object ' + + 'but could not find this object, pls ensure that file ' + options.src + ' contains it.'); } else { - // eslint-disable-next-line import/no-dynamic-require, global-require - return require(resolvedPath); // reads both: JS and JSON! + return object; } - } catch (err) { // probably a SyntaxError - // logger.error('Unexpected error: ' + err.stack); TODO remove - throw err; + } else { + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(resolvedPath); // reads both: JS and JSON! } } else if (isStream.readable(options.src)) { return readFromStream(options.src, TYPE_JSON); // reads both: JS or JSON! diff --git a/src/transformer.js b/src/transformer.js index 2efcfac..3c41cd2 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -1,4 +1,3 @@ -import logger from 'cli'; // TODO remove import Joi from './validation/joi-extensions'; import { read } from './reader'; import { write } from './writer'; @@ -54,11 +53,7 @@ import { transformOptionsSchema } from './validation/options-schema'; * }; */ export async function transform(options) { - // logger.debug('transform'); TODO remove const validatedOptions = await Joi.validate(options, transformOptionsSchema); - - console.log('Passed transform options:\n' + JSON.stringify(validatedOptions, null, 4)); // TODO remove - let object = await read(validatedOptions); object = await validatedOptions.transform(object); diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-helper.js index 30be3da..85bbd5e 100644 --- a/src/validation/options-schema-helper.js +++ b/src/validation/options-schema-helper.js @@ -15,6 +15,61 @@ import { * @private */ + +/** + * Checks if passed `object` is a file stream instance. + * + * @param {*} object - The object to check. + * @returns {boolean} A `true` if passed `object` is a file stream instance, else `false`. + * @private + */ +const isFileStream = (object) => { + return isStream(object) && object.path; +}; + +/** + * Returns the passes `dest` value or an adapted destination path (the latter if `target` is defined an differs from + * destinations path extension). + * + * @param {string} dest - The destination path. + * @param {string} [target] - The target file type of destination. + * @returns {string} The `dest` value or an adapted destination path. + * @private + */ +const adaptTargetPathType = (dest, target) => { + if (target) { + const tmpDest = dest; + const destDirName = path.dirname(tmpDest); + const ext = path.extname(tmpDest); + const basename = path.basename(tmpDest, ext); + let destType = ext; + if (ext.charAt(0) === '.') { + destType = ext.substr(1); + } + if (EXT_TO_TYPE_MAP[destType] !== target) { + destType = target; + } + return path.join(destDirName, basename + '.' + destType); + } + return dest; +}; + +/** + * This function is used to infer a _default_ value in case `options.dest` is not defined. + * Checks if `context.src` is either a string or a file stream where can get the file path from. + * If this detection process cannot be fulfilled (i.e. we cannot infer from options.src `Object` + * type or a `Readable` type which is not a _file_ stream) then the function returns `undefined`. + * + * @param {Object} context - The validation context. + * @returns {string|undefined} The adapted `dest` path if possible, or `undefined`. + */ +export const inferDestDefaultFromSrc = (context) => { + if (typeof context.src === 'string' || isFileStream(context.src)) { + return adaptTargetPathType((context.src.path || context.src), context.target); + } + return undefined; +}; + /** * Infer from path extension to a type using default value as fallback. * @@ -76,62 +131,7 @@ export const inferTargetDefault = (context) => { return type; }; -/** - * Checks if passed `object` is a file stream instance. - * - * @param {*} object - The object to check. - * @returns {boolean} A `true` if passed `object` is a file stream instance, else `false`. - * @private - */ -export const isFileStream = (object) => { - return isStream(object) && object.path; -}; - -/** - * Returns the passes `dest` value or an adapted destination path (the latter if `target` is defined an differs from - * destinations path extension). - * - * @param {string} dest - The destination path. - * @param {string} [target] - The target file type of destination. - * @returns {string} The `dest` value or an adapted destination path. - * @private - */ -export const adaptTargetPathType = (dest, target) => { - if (target) { - const tmpDest = dest; - const destDirName = path.dirname(tmpDest); - const ext = path.extname(tmpDest); - const basename = path.basename(tmpDest, ext); - let destType = ext; - if (ext.charAt(0) === '.') { - destType = ext.substr(1); - } - if (EXT_TO_TYPE_MAP[destType] !== target) { - destType = target; - } - return path.join(destDirName, basename + '.' + destType); - } - return dest; -}; - -/** - * This function is used to infer a _default_ value in case `options.dest` is not defined. - * Checks if `context.src` is either a string or a file stream where can get the file path from. - * If this detection process cannot be fulfilled then the function returns `undefined`. - * - * @param {Object} context - The validation context. - * @returns {string|undefined} The adapted `dest` path if possible, or `undefined`. - */ -export const inferDestDefaultFromSrc = (context) => { - if (typeof context.src === 'string' || isFileStream(context.src)) { - return adaptTargetPathType((context.src.path || context.src), context.target); - } - return undefined; // TODO how to handle this??? Throw Error? -}; - export default { - isFileStream, - adaptTargetPathType, inferOriginDefault, inferTargetDefault, inferDestDefaultFromSrc, diff --git a/src/writer.js b/src/writer.js index 595c267..dc2e2d5 100644 --- a/src/writer.js +++ b/src/writer.js @@ -1,4 +1,3 @@ -import logger from 'cli'; // TODO remove import fs from 'fs'; import isStream from 'is-stream'; import jsYaml from 'js-yaml'; @@ -315,13 +314,8 @@ async function writeJs(object, options) { * .catch(console.error); */ export async function write(object, options) { - // console.log('OPTIONS ON WRITE ===> ' + JSON.stringify(options, null, 4)) // TODO remove const validatedOptions = await Joi.validate(options, writeOptionsSchema); - - // HINT: we have to use the original options object because the caller must not loose the reference to options.dest, - // so we copy everything here except the assertedOptions.dest (joi does not return the original reference)! - Object.assign(options, { target: validatedOptions.target }, { exports: validatedOptions.exports }, // TODO does this work without full copy? - { indent: validatedOptions.indent }, { force: validatedOptions.force }); + // HINT: we have to use the original options object because the caller must not loose the reference to options.dest! validatedOptions.dest = options.dest; switch (validatedOptions.target) { case TYPE_JSON: diff --git a/test/unit/test-transformer.js b/test/unit/test-transformer.js index 5edb68f..b522d6f 100644 --- a/test/unit/test-transformer.js +++ b/test/unit/test-transformer.js @@ -101,12 +101,13 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { })).rejects.toMatchObject({ name: 'ValidationError', isJoi: true }); }); - it('should throw ValidationError if options.dest is not set and cannot be resolved from options.src type', async () => { - expect.assertions(1); - await expect(transform({ - src: {}, // we cannot infer destination from this type! - })).rejects.toMatchObject({ name: 'ValidationError', isJoi: true }); - }); + it('should throw ValidationError if options.dest is not set and cannot be resolved from options.src type', + async () => { + expect.assertions(1); + await expect(transform({ + src: {}, // we cannot infer destination from this type! + })).rejects.toMatchObject({ name: 'ValidationError', isJoi: true }); + }); it('should not fail if middleware passed is returning a Promise', () => { expect.assertions(1); diff --git a/test/unit/validation/test-options-schema-helper.js b/test/unit/validation/test-options-schema-helper.js index fdcf53c..0e02995 100644 --- a/test/unit/validation/test-options-schema-helper.js +++ b/test/unit/validation/test-options-schema-helper.js @@ -3,6 +3,7 @@ import fs from 'fs'; import { inferOriginDefault, inferTargetDefault, + inferDestDefaultFromSrc, } from '../../../src/validation/options-schema-helper'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; import { @@ -20,16 +21,16 @@ import { describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { describe('Function inferOriginDefault', () => { - it('should infer the correct origin from relative path string with existing file having a known file type', () => - expect(inferOriginDefault({ - src: 'test/unit/validation/test-joi-extensions-file-helper.js' - })).toBe(TYPE_JS) + it('should infer the correct origin from relative path string with existing file having a known file extension', + () => expect(inferOriginDefault({ src: 'test/unit/validation/test-joi-extensions-file-helper.js' })).toBe(TYPE_JS) ); - it('should infer the default origin from relative path string with existing file having an unknown file type', () => - expect(inferOriginDefault({ - src: 'test/data/readable-test-dummy.txt' - })).toBe(DEFAULT_ORIGIN) + it('should infer the default origin from relative path string with existing file having an unsupported file ' + + 'extension', () => expect(inferOriginDefault({ src: 'test/data/readable-test-dummy.txt' })).toBe(DEFAULT_ORIGIN) + ); + + it('should infer the default origin from relative path string with existing file having no file extension', () => + expect(inferOriginDefault({ src: 'test/data/readable-test-dummy' })).toBe(DEFAULT_ORIGIN) ); it('should infer the correct origin from read stream of existing file having a known file ending', () => @@ -39,41 +40,29 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { ); it('should infer the correct origin from read stream of existing file having an unknown file ending', () => - expect(inferOriginDefault({ - src: fs.createReadStream('test/data/readable-test-dummy.txt'), - })).toBe(DEFAULT_ORIGIN) + expect(inferOriginDefault({ src: fs.createReadStream('test/data/readable-test-dummy.txt') })).toBe(DEFAULT_ORIGIN) ); it('should infer the correct origin from plain read stream', () => - expect(inferOriginDefault({ - src: new stream.Readable(), - })).toBe(DEFAULT_ORIGIN) + expect(inferOriginDefault({ src: new stream.Readable() })).toBe(DEFAULT_ORIGIN) ); it('should infer the default origin from unsupported origin.src', () => - expect(inferOriginDefault({ - src: {}, - })).toBe(DEFAULT_ORIGIN) + expect(inferOriginDefault({ src: {} })).toBe(DEFAULT_ORIGIN) ); }); describe('Function inferTargetDefault', () => { it('should infer the correct target from relative path string with existing file having a known file ending', () => - expect(inferTargetDefault({ - dest: 'test/unit/validation/test-joi-extensions-file-helper.yaml' - })).toBe(TYPE_YAML) + expect(inferTargetDefault({ dest: 'test/unit/validation/test-joi-extensions-file-helper.yaml' })).toBe(TYPE_YAML) ); it('should infer the default target from relative path string with existing file having an unknown file type', () => - expect(inferTargetDefault({ - dest: 'test/data/readable-test-dummy.txt' - })).toBe(DEFAULT_TARGET) + expect(inferTargetDefault({ dest: 'test/data/readable-test-dummy.txt' })).toBe(DEFAULT_TARGET) ); it('should infer the correct target from relative path string with existing file having a known file type', () => - expect(inferTargetDefault({ - dest: fs.createWriteStream('test/data/writable-test-dummy.yaml'), - })).toBe(TYPE_YAML) + expect(inferTargetDefault({ dest: fs.createWriteStream('test/data/writable-test-dummy.yaml') })).toBe(TYPE_YAML) ); it('should infer the default target from relative path string with existing file having an' + @@ -84,15 +73,64 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { ); it('should infer the correct origin from plain write stream', () => - expect(inferTargetDefault({ - dest: new stream.Writable(), - })).toBe(DEFAULT_TARGET) + expect(inferTargetDefault({ dest: new stream.Writable() })).toBe(DEFAULT_TARGET) ); it('should infer the default target from unsupported origin.dest', () => - expect(inferTargetDefault({ - dest: {}, - })).toBe(DEFAULT_TARGET) + expect(inferTargetDefault({ dest: {} })).toBe(DEFAULT_TARGET) + ); + }); + + describe('Function inferDestDefaultFromSrc', () => { + it('should resolve missing destination from context.src string type without a target to context.src value', () => { + const context = { src: 'test/data/test-data.js' }; + expect(inferDestDefaultFromSrc(context)).toBe(context.src); + }); + + it('should resolve missing destination from context.src file Readable type without a target to' + + 'context.src.path value', () => { + const context = { src: fs.createReadStream('test/data/test-data.js') }; + expect(inferDestDefaultFromSrc(context)).toBe(context.src.path); + }); + + it('should resolve missing destination from context.src file Readable type with a target to' + + 'context.src.path value', () => { + const context = { + src: fs.createReadStream('test/data/test-data.js'), + target: TYPE_JS, + }; + expect(inferDestDefaultFromSrc(context)).toBe(context.src.path); + }); + + it('should resolve missing destination from context.src string type with a target to context.src' + + 'value where extension is replaced with YAML file type', () => { + const context = { + src: 'test/data/test-data.js', + target: TYPE_YAML, + }; + expect(inferDestDefaultFromSrc(context)).toBe('test/data/test-data.yaml'); + }); + + it('should resolve missing destination from context.src string type (without file extension) but with a target to' + + 'context.src value where extension is replaced with YAML file type', () => { + const context = { + src: 'test/data/test-data', + target: TYPE_YAML, + }; + expect(inferDestDefaultFromSrc(context)).toBe('test/data/test-data.yaml'); + }); + + it('should resolve missing destination from context.src file Readable type without a target to' + + 'context.src.path value but with YAML file type', () => { + const context = { + src: fs.createReadStream('test/data/test-data.js'), + target: TYPE_YAML, + }; + expect(inferDestDefaultFromSrc(context)).toBe('test/data/test-data.yaml'); + }); + + it('should resolve missing destination fom context.src Object type to undefined destination value', () => + expect(inferDestDefaultFromSrc({ src: {} })).toBeUndefined() ); }); }); From 84ecbf4b8535ee805eef1e09f76b77077ba74ace Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 13 Jul 2017 02:02:41 +0200 Subject: [PATCH 18/58] Fix bithound for babel-cli dep --- .bithoundrc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.bithoundrc b/.bithoundrc index 5e0d2eb..527211f 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -1,11 +1,12 @@ { "critics": { - "lint": {"engine": "eslint"} + "lint": { + "engine": "eslint" + } }, "dependencies": { "unused-ignores": [ - "grunt-*", - "bower", + "babel-cli", "eslint" ] } From eccf94a2e8154eb421e976276c1ef25519c76d71 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 13 Jul 2017 02:05:04 +0200 Subject: [PATCH 19/58] Test less verbose --- test/unit/test-transformer.js | 12 ++++++------ test/unit/test-writer.js | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/unit/test-transformer.js b/test/unit/test-transformer.js index b522d6f..e221350 100644 --- a/test/unit/test-transformer.js +++ b/test/unit/test-transformer.js @@ -40,7 +40,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { beforeEach(() => { try { fsExtra.copySync(SRC_YAML, TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml'); - logger.info('copied ' + SRC_YAML + ' to ' + TRANSFORMER_TEST_BASE_DIR); + logger.debug('copied ' + SRC_YAML + ' to ' + TRANSFORMER_TEST_BASE_DIR); } catch (err) { logger.error('could not copy ' + SRC_YAML + ' to ' + TRANSFORMER_TEST_BASE_DIR + err.stack); throw err; @@ -65,7 +65,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { function assertTransformSuccess(options) { return transform(options) .then((msg) => { - logger.info(msg); + logger.debug(msg); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); // eslint-disable-next-line import/no-dynamic-require, global-require @@ -82,7 +82,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { async function assertYamlTransformSuccess(options) { expect.assertions(3); const msg = await transform(options); - logger.info(msg); + logger.debug(msg); expect(msg).toEqual(expect.any(String)); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); @@ -151,7 +151,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { transform: transformFunc, dest: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.js'), }); - logger.info(msg); + logger.debug(msg); const stats = fs.statSync(DEST); expect(stats.isFile()).toBeTruthy(); // eslint-disable-next-line import/no-unresolved, global-require, import/no-dynamic-require @@ -235,7 +235,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { transform(options) .then((msg) => { - logger.info(msg); + logger.debug(msg); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); @@ -273,7 +273,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { transform(options) .then((msg) => { - logger.info(msg); + logger.debug(msg); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); diff --git a/test/unit/test-writer.js b/test/unit/test-writer.js index 26b2b5b..5337843 100644 --- a/test/unit/test-writer.js +++ b/test/unit/test-writer.js @@ -79,7 +79,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { await fsPromised.stat(dest); statErr = new Error('Error expected when checking file = ' + dest); } catch (err) { - logger.info('Error is EXPECTED: ' + err.stack); + logger.debug('Error is EXPECTED: ' + err.stack); expect(err).toBeDefined(); expect(err.code).toBe('ENOENT'); } @@ -178,7 +178,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { const errorThrowingStream = new stream.Writable(); // eslint-disable-next-line no-underscore-dangle, func-names errorThrowingStream._write = function (chunk, encoding, done) { - logger.info('stream emitting Error now'); + logger.debug('stream emitting Error now'); this.emit('error', new Error('Dummy Error')); done(); }; @@ -247,7 +247,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { done(new Error('Error expected, but got success message: ' + msg)); }) .catch((err) => { - logger.info('EXPECTED ERROR: ' + (err.stack ? err.stack : err)); + logger.debug('EXPECTED ERROR: ' + (err.stack ? err.stack : err)); expect(err).toBeDefined(); // NOTE: here wo do not get an Error type but simply an Object: // { @@ -437,9 +437,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { await asyncFunctions.reduce((p, fn, idx) => { return p.then((msg) => { if (msg) { - logger.info('testing overwrite #' + (idx + 1) + '/' + asyncFunctions.length + ': ' + msg); + logger.debug('testing overwrite #' + (idx + 1) + '/' + asyncFunctions.length + ': ' + msg); } else { - logger.info('testing overwrite #' + (idx) + '/' + asyncFunctions.length + ': started!'); + logger.debug('testing overwrite #' + (idx) + '/' + asyncFunctions.length + ': started!'); } return fn().then(result => result).catch(err => err.message); }); From 442d5214773134ad4c7ffdf1a4a5d6c68e4cf19f Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 13 Jul 2017 02:22:42 +0200 Subject: [PATCH 20/58] Fix bithound babel-cli --- .bithoundrc | 1 - package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.bithoundrc b/.bithoundrc index 527211f..be96efc 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -6,7 +6,6 @@ }, "dependencies": { "unused-ignores": [ - "babel-cli", "eslint" ] } diff --git a/package.json b/package.json index c7a8ff4..ddcbb44 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "node": ">=5.0.0" }, "dependencies": { - "babel-cli": "~6.24.1", "cli": " ~1.0.1", "is-stream": " ~1.1.0", "joi": "~10.6.0", @@ -44,6 +43,7 @@ "serialize-js": " ~1.1.0" }, "devDependencies": { + "babel-cli": "~6.24.1", "babel-eslint": " ~7.2.3", "babel-plugin-transform-flow-strip-types": "~6.22.0", "babel-preset-env": "~1.6.0", From 4f3ef038f1537d4f8f7e586403dd695bae77270f Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 13 Jul 2017 02:23:12 +0200 Subject: [PATCH 21/58] Update docs --- PACKAGE.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PACKAGE.md b/PACKAGE.md index 4c1adb0..0cb6389 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -18,7 +18,6 @@ npm test ## Dependencies -- [babel-cli](https://github.com/babel/babel/tree/master/packages): Babel command line. - [cli](https://github.com/node-js-libs/cli): A tool for rapidly building command line apps - [is-stream](https://github.com/sindresorhus/is-stream): Check if something is a Node.js stream - [joi](https://github.com/hapijs/joi): Object schema validation @@ -30,6 +29,7 @@ npm test ## Dev Dependencies +- [babel-cli](https://github.com/babel/babel/tree/master/packages): Babel command line. - [babel-eslint](https://github.com/babel/babel-eslint): Custom parser for ESLint - [babel-plugin-transform-flow-strip-types](https://github.com/babel/babel/tree/master/packages): Strip flow type annotations from your output code. - [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. @@ -54,7 +54,6 @@ npm test - [nsp](https://github.com/nodesecurity/nsp): The Node Security (nodesecurity.io) command line interface - [package-json-to-readme](https://github.com/zeke/package-json-to-readme): Generate a README.md from package.json contents - [winston](https://github.com/winstonjs/winston): A multi-transport async logging library for Node.js -- [winston-console-formatter](https://github.com/eugeny-dementev/winston-console-formatter): Pretty print console formatter in yaml like style ## License From 9fcdcf9da26e5a0c35668dbe909bf682f138f7b0 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 13 Jul 2017 02:52:27 +0200 Subject: [PATCH 22/58] Fix regenerator issue --- .babelrc | 6 ++++++ API-PRIVATE.md | 12 ++++++------ PACKAGE.md | 1 + package.json | 1 + src/writer.js | 17 +++++++---------- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/.babelrc b/.babelrc index aec1179..5bb615c 100644 --- a/.babelrc +++ b/.babelrc @@ -5,5 +5,11 @@ "node": "current" } }] + ], + "plugins": [ + ["transform-runtime", { + "polyfill": false, + "regenerator": true + }] ] } diff --git a/API-PRIVATE.md b/API-PRIVATE.md index 3811e22..6854f98 100644 --- a/API-PRIVATE.md +++ b/API-PRIVATE.md @@ -1082,12 +1082,12 @@ Ensures that all dirs exists for file type `dest` and writes the JS object to fi **Kind**: global variable **Access**: private -| Param | Type | Description | -| --- | --- | --- | -| object | string | The object to write into file. | -| dest | string | The file destination path. | -| target | string | The target type, one of [ 'yaml' | 'json' | 'js' ]. | -| [forceOverwrite] | boolean | Forces overwriting the destination file if `true`. | +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| object | string | | The object to write into file. | +| dest | string | | The file destination path. | +| target | string | | The target type, one of [ 'yaml' | 'json' | 'js' ]. | +| [forceOverwrite] | boolean | false | Forces overwriting the destination file if `true`. | diff --git a/PACKAGE.md b/PACKAGE.md index 0cb6389..3a7cd8a 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -32,6 +32,7 @@ npm test - [babel-cli](https://github.com/babel/babel/tree/master/packages): Babel command line. - [babel-eslint](https://github.com/babel/babel-eslint): Custom parser for ESLint - [babel-plugin-transform-flow-strip-types](https://github.com/babel/babel/tree/master/packages): Strip flow type annotations from your output code. +- [babel-plugin-transform-runtime](https://github.com/babel/babel/tree/master/packages): Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals - [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. - [chalk](https://github.com/chalk/chalk): Terminal string styling done right. Much color - [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects diff --git a/package.json b/package.json index ddcbb44..1d0ce34 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "babel-cli": "~6.24.1", "babel-eslint": " ~7.2.3", "babel-plugin-transform-flow-strip-types": "~6.22.0", + "babel-plugin-transform-runtime": "~6.23.0", "babel-preset-env": "~1.6.0", "chalk": "~2.0.1", "codeclimate-test-reporter": " ~0.5.0", diff --git a/src/writer.js b/src/writer.js index dc2e2d5..ff8ed7d 100644 --- a/src/writer.js +++ b/src/writer.js @@ -84,7 +84,7 @@ async function serializeJsToJsonString(object, indent) { * name which does not exist. * * @param {string} dest - The destination file. - * @returns {string} - A consecutive file name or the original one if `dest` file does not exist. + * @returns {string} A consecutive file name or the original one if `dest` file does not exist. * @private */ function getConsecutiveDestName(dest) { @@ -102,21 +102,18 @@ function getConsecutiveDestName(dest) { /** * Ensures that all dirs exists for file type `dest` and writes the JS object to file. * - * @param {string} object - The object to write into file. - * @param {string} dest - The file destination path. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. + * @param {string} object - The object to write into file. + * @param {string} dest - The file destination path. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @param {boolean} [forceOverwrite=false] - Forces overwriting the destination file if `true`. * @private */ -async function mkdirAndWrite(object, dest, target, forceOverwrite) { +async function mkdirAndWrite(object, dest, target, forceOverwrite = false) { const destDir = path.dirname(dest); - // logger.debug('Destination dir: ' + destDir); TODO remove await mkdirp(destDir); - // logger.debug('Destination dir ' + destDir + ' successfully written'); TODO remove let finalDestination = dest; - if (forceOverwrite === undefined || forceOverwrite === false) { + if (!forceOverwrite) { finalDestination = getConsecutiveDestName(dest); - // logger.debug('Setting was: do not overwrite, using destination ' + finalDestination + '.'); TODO remove } await fsPromisified.writeFile(finalDestination, object, UTF8); return 'Writing \'' + target + '\' file \'' + finalDestination + '\' successful.'; From 4bcba601bd3e13cdc157efb301c89675d7ca8e45 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Mon, 17 Jul 2017 13:00:24 +0200 Subject: [PATCH 23/58] Add codacy --- .jestrc.js | 2 +- .travis.yml | 1 + Makefile | 4 ++++ inch.json | 3 ++- package.json | 5 ++++- test/unit/test-cli.js | 39 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 51 insertions(+), 3 deletions(-) diff --git a/.jestrc.js b/.jestrc.js index 639b0dc..5272f30 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -20,7 +20,7 @@ module.exports = { }, mapCoverage: true, testMatch: [ - // '!**/test/unit/test-cli.js', + // '**/test/unit/test-cli.js', // '**/test/unit/validation/test-joi-extensions-file-helper.js', // '**/test/unit/validation/test-joi-extensions-identifier-helper.js', //'**/test/unit/test-transformer.js', diff --git a/.travis.yml b/.travis.yml index a3e8ef4..acc4075 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ after_success: - ./node_modules/codecov/bin/codecov -e TRAVIS_NODE_VERSION - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js --verbose - ./node_modules/codeclimate-test-reporter/bin/codeclimate.js < ./coverage/lcov.info +- ./node_modules/.bin/codacy-coverage < ./coverage/lcov.info branches: only: # whitelist diff --git a/Makefile b/Makefile index 92e9717..66b6a05 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,10 @@ test: build ## Runs the test suite, ESLint and a [Node Security Plattform](https npm run eslint npm run nsp +inch: build ## Runs [inch](https://github.com/rrrene/inchjs) for api-docs check. **NOTE: due to some bug in inch it does not work at the moment!** + @printf "Running inch to check api-docs status...\n" + npm run inch + nsp: ## Runs an [Node Security Plattform](https://nodesecurity.io/opensource) check. @printf "Running NSP check...\n" npm run nsp diff --git a/inch.json b/inch.json index 4268668..5618920 100644 --- a/inch.json +++ b/inch.json @@ -1,12 +1,13 @@ { "files": { "included": [ - "src/**/*.js", + "lib/**/*.js", "test/test*.js", "index.js", "jyt" ], "excluded": [ + "lib/**/*" ] } } diff --git a/package.json b/package.json index 1d0ce34..7980b12 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "pretest": "mkdir -pv test/tmp", "test": "JYT_DEBUG=false JYT_ERROR=false jest --forceExit --expand --no-cache --coverage --config=./.jestrc.js", "eslint": "eslint .", - "nsp": "nsp check" + "nsp": "nsp check", + "inch": "inchjs suggest && inchjs list --all && inchjs stats" }, "engines": { "node": ">=5.0.0" @@ -49,6 +50,7 @@ "babel-plugin-transform-runtime": "~6.23.0", "babel-preset-env": "~1.6.0", "chalk": "~2.0.1", + "codacy-coverage": "~2.0.2", "codeclimate-test-reporter": " ~0.5.0", "codecov": " ~2.2.0", "coveralls": " ~2.13.1", @@ -62,6 +64,7 @@ "eslint-plugin-jest-async": " ~1.0.3", "eslint-plugin-jsdoc": " ~3.1.1", "fs-extra": " ~3.0.1", + "inchjs": "~0.4.1", "jest": "~20.0.4", "jsdoc-babel": " ~0.3.0", "jsdoc-parse": " ~3.0.0", diff --git a/test/unit/test-cli.js b/test/unit/test-cli.js index fe8f586..cdf9e97 100644 --- a/test/unit/test-cli.js +++ b/test/unit/test-cli.js @@ -30,6 +30,7 @@ const execJyt = async (args) => { console.log(`stdout: ${JSON.stringify(result, null, 4)}`); console.log(`stdout: ${result.stdout}`); console.log(`stderr: ${result.stderr}`); + return result; } catch (err) { console.log(`stdout: ${err.stdout}`); console.log(`stderr: ${err.stderr}`); @@ -80,6 +81,43 @@ const execJyt = async (args) => { //exec('./jyt inch.json inch.yml -i 1') +const CLI_OPTIONS_LONGTO_SHORT_MAP = { + origin: '-o', + target: '-t', + indent: '-i', + force: '-f ', + imports: '-m ', + exports: '-x ' +}; + +function optionsToArgs(options) { + const args = []; + args.push(options.src); + if (options.dest) { + args.push(options.dest) + } + if (options.origin) { + args.push(options.origin) + } +} + +/** + * Helper method which asserts the successful transformation. + * + * @param {Object} options - The transformation options. + */ +function assertTransformSuccess(options) { + return transform(options) + .then((msg) => { + logger.debug(msg); + const stats = fsExtra.statSync(options.dest); + expect(stats.isFile()).toBeTruthy(); + // eslint-disable-next-line import/no-dynamic-require, global-require + const json = require(path.resolve(options.dest)); + expect(json.foo).toBe(EXPECTED_VALUE); + }); +} + describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { const TEST_DATA_DIR = './test/data'; const SRC_YAML = TEST_DATA_DIR + '/test-file.yaml'; @@ -100,5 +138,6 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { it('cli', async () => { await execJyt(['./inch.json', CLI_TEST_BASE_DIR + '/inch.yaml', '-i 2', '-t yaml']); + }); }); From 638ed0bc1cfb8418826ff916f5b9808b20ab07d4 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 21 Jul 2017 19:55:35 +0200 Subject: [PATCH 24/58] Intermediate work --- .circleci/config.yml | 32 ++ .jestrc.js | 6 +- API-PRIVATE.md | 82 ++++- API-PUBLIC.md | 4 +- MAKE.md | 1 + PACKAGE.md | 2 + README.md | 2 + readme/BADGES.md | 2 + src/validation/options-schema.js | 185 +++++------ src/writer.js | 2 +- test/data/test-data-cli.js | 3 + test/data/test-data-cli.json | 3 + test/data/test-data-cli.yaml | 1 + test/helper-constants.js | 16 + test/unit/test-cli.js | 331 ++++++++++++++------ test/unit/test-transformer.js | 292 ++++++++--------- test/unit/validation/test-options-schema.js | 56 ++-- 17 files changed, 613 insertions(+), 407 deletions(-) create mode 100644 .circleci/config.yml create mode 100644 test/data/test-data-cli.js create mode 100644 test/data/test-data-cli.json create mode 100644 test/data/test-data-cli.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..c1cf16a --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,32 @@ +version: 2 +jobs: + build: + docker: + - image: circleci/node:6 + environment: + - NODE_ENV: "test" + branches: + only: + - master + steps: + - checkout + - run: + name: Running tests + command: make test + - run: + name: Bithound + command: | + npm install bithound --save-dev + bithound check git@github.com:deadratfink/jy-transform.git + - run: + name: Codecov Coverage + command: ./node_modules/codecov/bin/codecov + - run: + name: Coveralls Coverage + command: cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js --verbose + - run: + name: Codeclimate Coverage + command: ./node_modules/codeclimate-test-reporter/bin/codeclimate.js < ./coverage/lcov.info + - run: + name: Codacy Coverage + command: ./node_modules/.bin/codacy-coverage < ./coverage/lcov.info diff --git a/.jestrc.js b/.jestrc.js index 5272f30..e2fc01c 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -23,12 +23,12 @@ module.exports = { // '**/test/unit/test-cli.js', // '**/test/unit/validation/test-joi-extensions-file-helper.js', // '**/test/unit/validation/test-joi-extensions-identifier-helper.js', - //'**/test/unit/test-transformer.js', + // '**/test/unit/test-transformer.js', // '**/test/unit/test-index.js', //'**/test/unit/test-reader.js', // '**/test/unit/test-writer.js', - // '**/test/unit/validation/test-options-schema.js', - '**/test/unit/**/*.js', + // '**/test/unit/validation/test-options-schema.js', + '**/test/unit/**/*.js', //'**/test/unit/validation/test-options-schema-helper.js', // '**/test/test-log-wrapper.js', diff --git a/API-PRIVATE.md b/API-PRIVATE.md index 6854f98..81e4dd4 100644 --- a/API-PRIVATE.md +++ b/API-PRIVATE.md @@ -159,7 +159,7 @@ exception on those.

      Writes a JS object to a JS destination. The object is prefixed by module.exports[.${options.exports}] =.

      writePromise
      -

      Writes the passe JS object to a particular destination described by the passed options.

      +

      Writes the passed JS object to a particular destination described by the passed options.

      @@ -697,28 +697,60 @@ The module options schema used in [module:options-validator](module:options-vali **See**: [module:options-validator](module:options-validator) * [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) : Object ℗ - * [~readOptionsSchema](#module_jy-transform_validation_options-schema..readOptionsSchema) : JoiSchema ℗ - * [~writeOptionsSchema](#module_jy-transform_validation_options-schema..writeOptionsSchema) : JoiSchema ℗ - * [~transformOptionsSchema](#module_jy-transform_validation_options-schema..transformOptionsSchema) : JoiSchema ℗ + * [~forceSchema](#module_jy-transform_validation_options-schema..forceSchema) : [Schema](#external_joi.Schema) ℗ + * [~indentSchema](#module_jy-transform_validation_options-schema..indentSchema) : [Schema](#external_joi.Schema) ℗ + * [~exportsSchema](#module_jy-transform_validation_options-schema..exportsSchema) : [Schema](#external_joi.Schema) ℗ + * [~targetSchema](#module_jy-transform_validation_options-schema..targetSchema) : [Schema](#external_joi.Schema) ℗ + * [~readOptionsSchema](#module_jy-transform_validation_options-schema..readOptionsSchema) : [Schema](#external_joi.Schema) ℗ + * [~writeOptionsSchema](#module_jy-transform_validation_options-schema..writeOptionsSchema) : [Schema](#external_joi.Schema) ℗ + * [~transformOptionsSchema](#module_jy-transform_validation_options-schema..transformOptionsSchema) : [Schema](#external_joi.Schema) ℗ + + +### jy-transform:validation:options-schema~forceSchema : [Schema](#external_joi.Schema) ℗ +The `force` options schema. + +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access**: private + + +### jy-transform:validation:options-schema~indentSchema : [Schema](#external_joi.Schema) ℗ +The `indent` options schema. + +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access**: private + + +### jy-transform:validation:options-schema~exportsSchema : [Schema](#external_joi.Schema) ℗ +The `exports` options schema. + +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access**: private + + +### jy-transform:validation:options-schema~targetSchema : [Schema](#external_joi.Schema) ℗ +The `target` options schema. + +**Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) +**Access**: private -### jy-transform:validation:options-schema~readOptionsSchema : JoiSchema ℗ -The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [ReadOptions](#ReadOptions). +### jy-transform:validation:options-schema~readOptionsSchema : [Schema](#external_joi.Schema) ℗ +The prepared [Schema](#external_joi.Schema) for validating the [ReadOptions](#ReadOptions). **Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) **Access**: private -### jy-transform:validation:options-schema~writeOptionsSchema : JoiSchema ℗ -The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [WriteOptions](#WriteOptions). +### jy-transform:validation:options-schema~writeOptionsSchema : [Schema](#external_joi.Schema) ℗ +The prepared [Schema](#external_joi.Schema) for validating the [WriteOptions](#WriteOptions). **Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) **Access**: private -### jy-transform:validation:options-schema~transformOptionsSchema : JoiSchema ℗ -The prepared [external:joi.JoiSchema](external:joi.JoiSchema) for validating the [TransformOptions](#TransformOptions). +### jy-transform:validation:options-schema~transformOptionsSchema : [Schema](#external_joi.Schema) ℗ +The prepared [Schema](#external_joi.Schema) for validating the [TransformOptions](#TransformOptions). **Kind**: inner constant of [jy-transform:validation:options-schema](#module_jy-transform_validation_options-schema) **Access**: private @@ -878,6 +910,34 @@ This function formats the log string by given options to log. This unit test suite checks the correct transformation behaviour of the CLI interface. **Access**: private + +* [jy-transform:unit-test:test-cli](#module_jy-transform_unit-test_test-cli) ℗ + * [~optionsToArgs(options)](#module_jy-transform_unit-test_test-cli..optionsToArgs) ⇒ Array.<string> + * [~assertTransformSuccess(options)](#module_jy-transform_unit-test_test-cli..assertTransformSuccess) + + + +### jy-transform:unit-test:test-cli~optionsToArgs(options) ⇒ Array.<string> +Creates the CLI args/options from given `options` object. + +**Kind**: inner method of [jy-transform:unit-test:test-cli](#module_jy-transform_unit-test_test-cli) +**Returns**: Array.<string> - The CLI args and options. + +| Param | Type | Description | +| --- | --- | --- | +| options | [TransformOptions](#TransformOptions) | The transformation options. | + + + +### jy-transform:unit-test:test-cli~assertTransformSuccess(options) +Helper method which asserts the successful transformation. + +**Kind**: inner method of [jy-transform:unit-test:test-cli](#module_jy-transform_unit-test_test-cli) + +| Param | Type | Description | +| --- | --- | --- | +| options | [TransformOptions](#TransformOptions) | The transformation options. | + ## jy-transform:test-unit:index ℗ @@ -1156,7 +1216,7 @@ Writes a JS object to a JS destination. The object is prefixed by `module.export ## write ⇒ Promise -Writes the passe JS object to a particular destination described by the passed `options`. +Writes the passed JS object to a particular destination described by the passed `options`. **Kind**: global variable **Returns**: Promise - The result. diff --git a/API-PUBLIC.md b/API-PUBLIC.md index 44da18e..75e4c5e 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -29,7 +29,7 @@

      Reads a particular content type from a source provided in the passed options.

      writePromise
      -

      Writes the passe JS object to a particular destination described by the passed options.

      +

      Writes the passed JS object to a particular destination described by the passed options.

      @@ -125,7 +125,7 @@ read(options) ## write ⇒ Promise -Writes the passe JS object to a particular destination described by the passed `options`. +Writes the passed JS object to a particular destination described by the passed `options`. **Kind**: global variable **Returns**: Promise - The result. diff --git a/MAKE.md b/MAKE.md index a01285e..cc19d42 100644 --- a/MAKE.md +++ b/MAKE.md @@ -5,6 +5,7 @@ Target Call | Description | Dependencies `$ make clean` | Removes generated files in folders ./node_modules, ./lib and ./coverage" | `$ make eslint` | Runs ESLint. | `$ make help` | Prints the help about targets. | +`$ make inch` | Runs [inch](https://github.com/rrrene/inchjs) for api-docs check. **NOTE | `build` `$ make install` | Installs all modules | `$ make nsp` | Runs an [Node Security Plattform](https://nodesecurity.io/opensource) check. | `$ make publish` | Publishes module to NPM registry. | `test readme` diff --git a/PACKAGE.md b/PACKAGE.md index 3a7cd8a..9002849 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -35,6 +35,7 @@ npm test - [babel-plugin-transform-runtime](https://github.com/babel/babel/tree/master/packages): Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals - [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. - [chalk](https://github.com/chalk/chalk): Terminal string styling done right. Much color +- [codacy-coverage](https://github.com/codacy/node-codacy-coverage): Code Coverage reporter for Codacy.com - [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects - [codecov](https://github.com/codecov/codecov-node): Uploading report to Codecov: https://codecov.io - [coveralls](https://github.com/nickmerwin/node-coveralls): takes json-cov output into stdin and POSTs to coveralls.io @@ -48,6 +49,7 @@ npm test - [eslint-plugin-jest-async](https://github.com/deadratfink/jy-transform.git): ESLint plugin to detect improper Jest test assertions for asynchronous (Promise-based) actions - [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc): JSDoc linting rules for ESLint. - [fs-extra](https://github.com/jprichardson/node-fs-extra): fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf. +- [inchjs](https://github.com/rrrene/inchjs): JS Wrapper for Inch for JavaScript - [jest](https://github.com/facebook/jest): Delightful JavaScript Testing. - [jsdoc-babel](https://github.com/ctumolosus/jsdoc-babel): A JSDoc plugin that transforms ES6 source files with Babel before they are processsed. - [jsdoc-parse](https://github.com/jsdoc2md/jsdoc-parse): Transforms jsdoc data into something more suitable for use as template input diff --git a/README.md b/README.md index a1dc1d6..f63d4bd 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ [![bitHound Dependencies][bitHound-dependencies-image]][bitHound-dependencies] [![bitHound Dev Dependencies][bitHound-dev-dependencies-image]][bitHound-dependencies] [![NSP Status][nsp-image-master]][nsp-url-master] +[![Codacy Badge](https://img.shields.io/codacy/grade/c2ebaac0f9874062ba468ff6bd7edc4e.svg?style=flat-square)](https://www.codacy.com/app/deadratfink/jy-transform?utm_source=github.com&utm_medium=referral&utm_content=deadratfink/jy-transform&utm_campaign=Badge_Grade) +[![Codacy Badge](https://img.shields.io/codacy/coverage/c2ebaac0f9874062ba468ff6bd7edc4e.svg?style=flat-square)](https://www.codacy.com/app/deadratfink/jy-transform?utm_source=github.com&utm_medium=referral&utm_content=deadratfink/jy-transform&utm_campaign=Badge_Coverage) +[![bitHound Overall Score](https://www.bithound.io/github/deadratfink/jy-transform/badges/score.svg?style=flat)](https://www.bithound.io/github/deadratfink/jy-transform) --> @@ -25,54 +26,54 @@ [![NPM][npm-image]][npm-url] [![NPM][npm-downloads-image]][npm-url] -[gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat-square +[gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat [gh-license-url]: https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md -[gh-issues-image]: https://img.shields.io/github/issues/deadratfink/jy-transform.svg?style=flat-square +[gh-issues-image]: https://img.shields.io/github/issues/deadratfink/jy-transform.svg?style=flat [gh-issues-url]: https://github.com/deadratfink/jy-transform/issues -[gh-releases-image]: https://img.shields.io/github/release/deadratfink/jy-transform.svg?style=flat-square +[gh-releases-image]: https://img.shields.io/github/release/deadratfink/jy-transform.svg?style=flat [gh-releases-url]: https://github.com/deadratfink/jy-transform/releases -[gh-tags-image]: https://img.shields.io/github/tag/deadratfink/jy-transform.svg?style=flat-square +[gh-tags-image]: https://img.shields.io/github/tag/deadratfink/jy-transform.svg?style=flat [gh-tags-url]: https://github.com/deadratfink/jy-transform/tags -[ci-image]: https://img.shields.io/travis/deadratfink/jy-transform.svg?style=flat-square +[ci-image]: https://img.shields.io/travis/deadratfink/jy-transform.svg?style=flat [ci-url]: https://travis-ci.org/deadratfink/jy-transform/branches -[is-pull-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/pr?style=flat-square -[is-issue-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/issue?style=flat-square +[is-pull-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/pr?style=flat +[is-issue-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/issue?style=flat [is-url]: http://issuestats.com/github/deadratfink/jy-transform -[waffle-ready-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat-square -[waffle-waffle-in-progress-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=in%20progress&title=Waffle%20In%20Progress&style=flat-square +[waffle-ready-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat +[waffle-waffle-in-progress-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=in%20progress&title=Waffle%20In%20Progress&style=flat [waffle-url]: https://waffle.io/deadratfink/jy-transform -[cocl-image]: https://img.shields.io/codeclimate/github/deadratfink/jy-transform.svg?style=flat-square +[cocl-image]: https://img.shields.io/codeclimate/github/deadratfink/jy-transform.svg?style=flat [cocl-url]: https://codeclimate.com/github/deadratfink/jy-transform -[cc-image-master]: https://img.shields.io/codecov/c/github/deadratfink/jy-transform/master.svg?style=flat-square +[cc-image-master]: https://img.shields.io/codecov/c/github/deadratfink/jy-transform/master.svg?style=flat [cc-url-master]: https://codecov.io/github/deadratfink/jy-transform?branch=master -[ca-image-master]: https://img.shields.io/coveralls/deadratfink/jy-transform/master.svg?style=flat-square +[ca-image-master]: https://img.shields.io/coveralls/deadratfink/jy-transform/master.svg?style=flat [ca-url-master]: https://coveralls.io/github/deadratfink/jy-transform?branch=master -[inch-image-master]: https://inch-ci.org/github/deadratfink/jy-transform.svg?branch=master&style=flat-square +[inch-image-master]: https://inch-ci.org/github/deadratfink/jy-transform.svg?branch=master&style=flat [inch-url-master]: https://inch-ci.org/github/deadratfink/jy-transform?branch=master -[dep-image-master]: https://img.shields.io/david/deadratfink/jy-transform/master.svg?style=flat-square +[dep-image-master]: https://img.shields.io/david/deadratfink/jy-transform/master.svg?style=flat [dep-url-master]: https://david-dm.org/deadratfink/jy-transform/master -[devdep-image-master]: https://img.shields.io/david/dev/deadratfink/jy-transform/master.svg?style=flat-square +[devdep-image-master]: https://img.shields.io/david/dev/deadratfink/jy-transform/master.svg?style=flat [devdep-url-master]: https://david-dm.org/deadratfink/jy-transform/master#info=devDependencies -[nsp-image-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0/badge?style=flat-square +[nsp-image-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0/badge?style=flat [nsp-url-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0 -[node-version-image]: https://img.shields.io/node/v/jy-transform.svg?style=flat-square +[node-version-image]: https://img.shields.io/node/v/jy-transform.svg?style=flat [node-version-url]: http://nodejs.org/download/ [npm-image]: https://nodei.co/npm/jy-transform.png?downloads=true&downloadRank=true&stars=true @@ -80,11 +81,11 @@ [npm-downloads-image]: https://nodei.co/npm-dl/jy-transform.png?height=2&months=9 [bithound-url]: https://www.bithound.io/github/deadratfink/jy-transform -[bithound-code-image]: https://img.shields.io/bithound/code/github/deadratfink/jy-transform.svg?style=flat-square +[bithound-code-image]: https://img.shields.io/bithound/code/github/deadratfink/jy-transform.svg?style=flat [bitHound-dependencies]: https://www.bithound.io/github/deadratfink/jy-transform/master/dependencies/npm -[bitHound-dependencies-image]: https://img.shields.io/bithound/dependencies/github/deadratfink/jy-transform.svg?style=flat-square -[bitHound-dev-dependencies-image]: https://img.shields.io/bithound/devDependencies/github/deadratfink/jy-transform.svg?style=flat-square -[bitHound-score-image]: https://img.shields.io/bithound/score/github/deadratfink/jy-transform.svg?style=flat-square +[bitHound-dependencies-image]: https://img.shields.io/bithound/dependencies/github/deadratfink/jy-transform.svg?style=flat +[bitHound-dev-dependencies-image]: https://img.shields.io/bithound/devDependencies/github/deadratfink/jy-transform.svg?style=flat +[bitHound-score-image]: https://img.shields.io/bithound/score/github/deadratfink/jy-transform.svg?style=flat # jy-transform @@ -113,6 +114,7 @@ npm install jy-transform --global - [Use Cases](#use-cases) - [Limitations](#limitations) - [CLI Usage](#cli-usage) + - [Examples](#examples) - [Origin and Target Type Inference](#origin-and-target-type-inference) - [API Usage](#api-usage) - [API Reference](#api-reference) @@ -129,7 +131,7 @@ npm install jy-transform --global import { transform } from 'jy-transform'; const options = { - src: 'foo/bar.yaml', // Here: read from YAML file... + src: 'foo/bar.yaml', // E.g. read from YAML file... transform: async (object) => { // ...with exchanging value... object.foo = 'new value'; return object; @@ -147,7 +149,6 @@ transform(options) // ---- async/await style: - try { const msg = await transform(options); // Transform, of course, inside an async. console.log(msg); // Success message! @@ -161,11 +162,19 @@ try { ```javascript import { read } from 'jy-transform'; -const options = { src: 'foo/bar.yaml' }; // Here: read from file. +const options = { src: 'foo/bar.yaml' }; // E.g. read from file. + +// ---- Promise style: + +read(options) + .then((object) => console.log(JSON.stringify(object))) // Print read object. + .catch(console.error); + +// ---- async/await style: try { const object = await read(options); - console.log(JSON.stringify(object)); // Print write success message. + console.log(JSON.stringify(object)); // Print read object. } catch (err) { console.error(err.stack); } @@ -176,11 +185,19 @@ try { ```javascript import { write } from 'jy-transform'; -const options = { dest: 'foo/bar.yaml' }; // Here: write to file. +const options = { dest: 'foo/bar.yaml' }; // E.g. write to file. + +// ---- Promise style: + +write(object, options) + .then(console.log) // Print write success message. + .catch(console.error); + +// ---- async/await style: try { const msg = await write(object, options); - console.log(msg); // Print write success message. + console.log(msg); // Print write success message. } catch (err) { console.error(err.stack); } @@ -197,7 +214,7 @@ types into each other format. ## Usage The module can be used on CLI or as API (the latter is fully -[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) based). +[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)/`async` based). ### Usage Types @@ -213,9 +230,9 @@ Both usage types are described in more detail in the following sections. So, what are the typical use cases for this module? In terms of _transformation_ these consists of different phases: -- Reading files (`Reader`) -- Transforming JSON objects (`Transformer`) or apply dedicated actions on the intermediate JSON objects (`Transformer` + `Middleware`) -- Writing files (`Writer`) + 1. Reading from source + 2. Transforming JSON objects (`Transformer`) or apply dedicated actions on the intermediate JSON objects + 3. Writing to a destination #### Reading From @@ -226,17 +243,18 @@ these consists of different phases: Additionally, on API level from a: - `stream.Readable` - - Contain serialized JS, JSON or YAML - - If not file stream it requires `options.origin` property set - - Reads as UTF-8 + - Contain serialized JS, JSON or YAML + - If not file stream it requires `options.origin` property set + - Reads as UTF-8 - JS `object` - - Actually, this means the reading phase is "skipped", because object is in-memory already - - Of course, this case _cannot_ not be applied to serialized JSON or YAML content + - Actually, this means the reading phase is "skipped", because object is in-memory already + - Of course, this case _cannot_ not be applied to serialized JSON or YAML content -#### Transformation [+ Middleware] +#### Transformation The _transformation_ is usually a format change, but can also be refer to content changes on the -intermediate JS object (the latter via _middleware_). All possible directions are: +intermediate JS object, the latter with the help of a configured `transform` callback function. +All possible directions are: - YAML ⇒ JS - YAML ⇒ JSON @@ -254,8 +272,8 @@ while: - [JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript) = _*.js_ (JS object) - [JSON](http://json.org) = _*.json_ (JS object serialized as JSON) -As mentioned above a _middleware_ can apply particular actions on the intermediate JS object via injected functions. -This is an optional part for [transformation](#transformation) phase. +As mentioned above the configured `transform` callback can apply particular actions on the intermediate JS object, but +this is an optional part for [transformation](#transformation) phase. #### Writing To @@ -266,12 +284,12 @@ This is an optional part for [transformation](#transformation) phase. Additionally, on API level to a: - `stream.Writable` - - Serialized JS, JSON and YAML - - Requires `options.target` property set - - Writes UTF-8 + - Serialized JS, JSON and YAML + - Requires `options.target` property set + - Writes UTF-8 - JS `object - - JS as simple reference - - YAML and JSON as serialized string + - JS as simple reference + - YAML and JSON as serialized string ### Limitations @@ -313,7 +331,7 @@ Additionally, on API level to a: ### CLI Usage The CLI provides the `jyt` command which requires the use of some options. -After the global installation you can access the `Transformer`'s command options +After the global installation you can access the command options with the usual `--help` option which prints an overview about all available CLI properties: @@ -348,7 +366,8 @@ The ARGS are more formally defined in the following table: | `INPUT-FILE` | URI | The source file path for transformation. | - | yes | | `OUTPUT-FILE` | URI | The destination file path to transform to. | When this options is omitted then the output file is stored relative to the input file (same base name but with another extension if type differs). If input and output type are the same then the file overwriting is handled depending on the `--force` value! | no | -**NOTE:** the input file has to be specified and should be _first_ argument (in fact, it can be anywhere but it must be before an out file argument)! +> **NOTE:** the _input file_ has to be specified and should be _first_ argument (in fact, it can be +> anywhere but it must be _before_ an _output file_ argument)! #### CLI Options @@ -367,18 +386,15 @@ The OPTIONS are more formally defined in the following table: | `-v` | `--version` | n/a | Display the current version. | n/a | no | | `-h` | `--help` | n/a | Display help and usage details. | n/a | no | +### Examples -**NOTE:** an invalid indentation setting (1 > `-i`, `--indent` > 8) does not raise an error but a default of 4 SPACEs is applied instead. - -#### Examples - -Now we know which properties can be applied on CLI, so let's assume we +Now we know which properties can be applied on CLI it's time for some examples. Let's assume we have a YAML content located in _foo.yaml_ holding this data: ```yaml foo: bar ``` -##### Example: YAML ⇒ JSON +#### Example: YAML ⇒ JSON Then we can transform it to a JSON content as _foo.json_ file: @@ -390,18 +406,19 @@ Then we can transform it to a JSON content as _foo.json_ file: simply by using this command: -``` +```text $ jyt foo.yaml -t json -i 4 ``` -In this example we have overwritten the standard target type (which is `js`) -and applying an indent of 4 SPACEs instead of the default (2). As default the output +In this example we have overwritten the standard _target_ type (which is `js`) +and applying an _indent_ of 4 SPACEs instead of the default (which is 2). As default the output file _foo.json_ is written relative to the input file (by omitting the `dest` option here). -**NOTE:** here you _have_ to provide the target with option `-t json` or else the -default `js` would have been applied! If the source would have been a `js` -type like +> **NOTE:** here you _have_ to provide the target with option `-t json` or else the +> default `js` would have been applied! + +If the source would have been a `js` type like in this example ``` $ jyt foo.js -t json -i 4 @@ -410,9 +427,11 @@ $ jyt foo.js -t json -i 4 then the `js` value for `origin` is automatically inferred from file extension. Accordingly, this is also true for the `target` option. -##### Example: JSON ⇒ JS +#### Example: JSON ⇒ JS + The command -``` + +```text $ jyt foo.json -i 4 ``` results in _foo.js_: @@ -422,34 +441,42 @@ module.exports = { } ``` -##### Example: JS ⇒ YAML +#### Example: JS ⇒ YAML + The command -``` + +```text $ jyt foo.js -t yaml ``` + results in _foo.yaml_: + ```yaml foo: bar ``` -##### Example: Transformation with Different Destination +#### Example: Transformation with Different Destination Simply specify the _output_ file with a different file name: -``` + +```text $ jyt foo.json results/foobar.yaml ``` -##### Example: Transformation with Unsupported Source File Extension +#### Example: Transformation with Unsupported Source File Extension As said, normally we infer from file extension to the type, but assume the source file has a file name which does not imply the type (here a JSON type in a TEXT file), then you can simply provide the `-o` option with the -correct `origin` type (of course, the `-t` option works analogous): -``` +correct `origin` type: + +```text $ jyt foo.txt foobar.yaml -o json ``` -##### Example: Read from File with Exports Identifier +> **NOTE:** of course, the `-t` (`--target`) option works analogous. + +#### Example: Read from File with Exports Identifier It could be that a JS source `exports` several objects and you want to read from exactly the one you specify, then provide the `-m` (`--imports`) option. @@ -466,81 +493,90 @@ module.exports.bar = { }; ``` but you want to convert only `bar` object, then call: -``` + +```text $ jyt foo.js bar.yaml -m bar ``` + to get the YAML result: + ```yaml bar: foo ``` -**NOTE:** the same applies on API level when using JS objects as `dest`: - -```javascript -const fooBar = { - foo: 'bar', - bar: 'foo' -}; - -const options = { - src: fooBar, - dest: {}, - exports: 'bar' -}; - -//...transform -``` - -The transformation will result in this in-memory object: - -```javascript -bar: { - foo: 'bar', - bar: 'foo' -} -``` -Of course, as sub-node of `options.dest`. - -##### Example: Write Exports Identifier for JS File +> **NOTE:** the same applies on API level when using JS objects as `dest`: +> +> ```javascript +> const fooBar = { +> foo: 'bar', +> bar: 'foo' +> }; +> +> const options = { +> src: fooBar, +> dest: {}, +> exports: 'bar' +> }; + +> //...transform +> ``` +> +> The transformation will result in this in-memory object: +> +> ```javascript +> bar: { +> foo: 'bar', +> bar: 'foo' +> } +> ``` +> +> Of course, as sub-node of `options.dest`. + +#### Example: Write Exports Identifier for JS File Assume you want to generate a JS file with an exports string which gets an identifier. We reuse the YAML file from above: + ```yaml foo: bar ``` + using this command: + ``` $ jyt foo.yaml foobar.js -x foobar ``` + This generates the following output in JS file using `foobar` as identifier: + ```javascript module.exports.foobar = { foo: "bar" } ``` -**NOTE:** the identifier must be a valid JS identifier accoding to ECMAScript 6 -(see also [Valid JavaScript variable names in ECMAScript 6](https://mathiasbynens.be/notes/javascript-identifiers-es6) -and [Generating a regular expression to match valid JavaScript identifiers](https://mathiasbynens.be/demo/javascript-identifier-regex)). +> **NOTE:** the identifier must be a valid JS identifier accoding to ECMAScript 6 +> (see also [Valid JavaScript variable names in ECMAScript 6](https://mathiasbynens.be/notes/javascript-identifiers-es6) +> and [Generating a regular expression to match valid JavaScript identifiers](https://mathiasbynens.be/demo/javascript-identifier-regex)). -##### Example: Force Overwriting +#### Example: Force Overwriting -**IMPORTANT NOTE:** when using this feature then any subsequent -execution which uses the same target/file name, -will overwrite the original source or target created beforehand! +> **IMPORTANT NOTE:** when using this feature then any subsequent execution which +> uses the same target/file name, can _overwrite_ the original source or target created beforehand! -By default this feature is not enabled to prevent you from accidentally +Therefore, this feature is _not_ enabled by default to prevent you from accidentally overwriting your input source or already generated targets. But let's say we want to overwrite the original source now because you want to change the indentation from 2 to 4 SPACEs, then we can do this as follows: + +```text +$ jyt foo.js -i 4 -f ``` -$ jyt foo.js -f -``` -Of course, leaving out the `-f` switch creates a new file relatively to -the `origin`, named as _foo(1).js_ (note the consecutive number). Naturally, -another run of the command would result in a file called _foo(2).js_ -and so forth. + +> **NOTE:** the other way round (i.e. leaving out the `-f` (`--force`)) switch would create a _new file_ relatively to +> the `src` _foo.js_, named as _foo(1).js_; note the consecutive number! Naturally, +> another run of the command would result in a file called _foo(2).js_ and so forth. ### Origin and Target Type Inference @@ -554,8 +590,7 @@ extensions. This is supported as shown by the following table (from-to): | _*.js_ | _js_ | | _*.json_ | _json_ | -**NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to -specify the origin or target type! +> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! ### API Usage @@ -564,12 +599,12 @@ Since the usage on CLI is a 2-step process: 1. Read from source file to JS object ⇒ 2. Write out (maybe to other type) -the direct API calls additionally provide the usage of a _middleware_ function -where you can alter the input JS object before it is written and therefore, which turns +the direct API calls additionally provide the optional usage of a `transform` function +where you can alter the intermediate JS object before it is written and therefore, turns this into a 3-step process: 1. Read from source ⇒ - 2. Alter the JS object ⇒ + 2. Transform the JS object ⇒ 3. Write out (maybe to other type) For more details about this and all the functions provided by this module please refer to the @@ -579,97 +614,85 @@ The `origin` and `target` type inference is also standard for the API level. #### API Properties -The `Transformer` exposes the following function which takes besides an (optional) -`middleware` function the necessary `options` for the transformation: +The public `transform` function (that does not mean the optional `transform `callback here!) takes +the necessary `options` for the transformation: ```javascript -async function transform(options, middleware) +async function transform(options) ``` -#### Options - -The `options` object has to follow this key-values tables: +> **HINT:** of course, if you like you can use the `read`and `write` functionality solely besides any transformation needs. -##### Reader Options - -| Option | Type | Description | Default | Required | -| --- | --- | --- | --- | --- | -| `origin` | _String_ | The origin type. | If not given, the type is tried to be inferred from the extension of source path, else it is _yaml_. | no | -| `target` | _String_ | The target type. | If not given, the type is tried to be inferred from the extension of destination path, else it is _js_. | no | -| `src` | [ _String_ | _Stream.Readable_ | _Object_ ] | The source information object: `String` is used as file path, `Stream.Readable` provides a stringified source and `object` is used as direct JS source. | - | yes | - -##### Writer Options - -| Option | Type | Description | Default | Required | -| --- | --- | --- | --- | --- | -| `dest` | [ _String_ | _Stream.Writable_ | _Object_ ] | The destination information object: `String` is used as file path, `Stream.Writable` writes a stringified source and `object` is used for direct JS object assignment of the (stringified or JS object) source. If a string is set as file path then the output and if input and output file path are the same, then the file overwriting is handled depending on the `force` value! | - | yes | -| `indent` | _Number_ | The indentation in files. | 2 | no | -| `force` | _Boolean_ | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | -| `imports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | -| `exports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | +#### Options -##### Transformer Options +For a detailed description see: -These are a combination of the [Reader](#reader-options) and [Writer](#writer-options) Options. +- [Read Options](API-PUBLIC.md#readoptions--codeobjectcode) +- [Write Options](API-PUBLIC.md#writeoptions--codeobjectcode) +- [Transform Options](API-PUBLIC.md#transformoptions--codeobjectcode) ##### Example ```javascript const options = { - origin: 'json', - target: 'yaml', src: 'foo.json', + origin: 'json', // actually, not needed here, inferred from src's extension automatically! dest: './foo/bar.yaml', + target: 'yaml', // actually, not needed here, inferred from dest's extension automatically! indent: 4 } ``` -#### Using Middleware +#### Using Transform Callback + +The `transform` property is optional but if provided it must be of type `Function` and could but must not be +a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)/`async`. + +- When being a Promise it has to resolve with the `data` object or reject with an `Error` based object. +- In case of normal or `async` function return `data` or throw an `Error`. -The `middleware` is optional but if provided it must be of type `Function` and -a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). One of the easiest ones is the identity function _f(data) → data_ -which could be expressed as [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) -function as follows: +which could be expressed as follows: ```javascript -const identity = async (data) => { - return data; -} +const identity = data => data; ``` Of course, this would have no effect on the provided JS data. Actually, this one is -used internally when no middleware is provided to ensure the proper promised +used internally as default when no `transform` function is configured to ensure the proper control flow. -OK, lets go back to a more practical example, e.g. we want to alter the value of +OK, lets go back to a more practical example, e.g. we want to alter the value of a JS property before it is written to a file. Assuming we have this piece of YAML -object as input: +as input from a file called _src.yaml_: ```yaml foo: old bar ``` -Applying this [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) -as middleware +Applying this `transform` callback option to the `src` content ```javascript import { transform } from 'jy-transform'; -const middleware = async (data) => { - data.foo = 'new bar'; - return data; +const options = { + src: 'src.yaml', + dest: 'result.json', + transform: async (data) => { + data.foo = 'new bar'; + return data; + } }; -transform(options, middleware) +transform(options) .then(console.log) .catch(console.error); ``` -will result in such JSON file: +will result in such JSON file content: ```json { @@ -687,43 +710,44 @@ Let's assume we have some Promise functions to apply. For simplicity reasons we simulate these for the moment by some functions, each adding a key-value to the given (initially empty) JS object. -**NOTE:** each of them has to resolve with the `data` object! +> **NOTE:** each of them has to resolve with the `object` object! ```javascript -const key1 = async (data) => { - odata.key1 = 'value1'; - return data; +const key1 = async (object) => { + object.key1 = 'value1'; + return object; }; -const key2 = async (data) => { - data.key2 = 'value2'; - return data; +const key2 = async (object) => { + object.key2 = 'value2'; + return object; }; -const key3 = async (data) => { - data.key3 = 'value3'; - return data; +const key3 = async (object) => { + object.key3 = 'value3'; + return object; }; ``` These can be collected by different aggregation or composition functions of the underlying Promise framework, e.g. using the [`Promise.all([...])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) -function. This one can collect all three functions above and ensure their proper subsequent execution: +function. This one can collect all three functions above and ensure their proper execution: ```javascript import { transform } from 'jy-transform'; -const middleware = (data) => { - return Promise.all([key1(data), key2(data), key3(data)]) - .then(result => result[result.length - 1]); +const options = { + src: {}, + transform: async (object) => Promise.all([key1(object), key2(object), key3(object)]) + .then(result => result[result.length - 1]) }; -return transform({ src: {} }, middleware) +transform(options) .then(console.log) .catch(console.error); ``` -Then the result in the `middleware` function can be retrieved from the returned +The result in the `transform` function can be retrieved from the returned array, i.e. in case of [`Promise.all([...])`](http://bluebirdjs.com/docs/api/promise.all.html) you have to pick the _last_ element which contains the "final product". @@ -737,26 +761,24 @@ From our example above it would be result in this object } ``` -which then is passed back to the transformation chain. Following this pattern +which is passed back to the transformation chain. Following this pattern you can do almost everything with the JS object, like -- deleting properties -- changing properties to other types -- validating and throwing error if not valid +- Deleting properties +- Changing properties to other types +- Validating and throwing/resolving with error if not valid - ... -Whatever you do during transformation, just keep it valid ;-) - ## API Reference For more details on how to use the API, please refer to the -[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v3) +[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-public-v3) wiki which describes the full API and provides more examples. ## Contributing Pull requests and stars are always welcome. For bugs and feature requests, please create an -[issue](https://github.com/deadratfink/jy-transform/issues). +[issue](https://github.com/deadratfink/jy-transform/issues) or create a PR. See the wiki [Contributing](https://github.com/deadratfink/jy-transform/wiki/Contributing) section for more details about conventions. diff --git a/jyt b/jyt index e15fef3..c86e3b2 100755 --- a/jyt +++ b/jyt @@ -1,3 +1,4 @@ #!/usr/bin/env node +// eslint-disable-next-line import/no-commonjs require('./lib/cli'); diff --git a/package.json b/package.json index 7980b12..fac4177 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,10 @@ "private": false, "scripts": { "build": "NODE_ENV=production babel src --out-dir lib --copy-files --source-maps", - "doc": "cat docs/LOGO.md > README.md && cat docs/BADGES.md >> README.md && cat docs/TOC.md >> README.md && package-json-to-readme --no-footer ./package.json >> README.md && cat docs/USAGE.md >> README.md && echo '\n# Changelog\n' >> README.md && cat docs/CHANGELOG.md >> README.md && doctoc README.md --github --title '# TOC' --maxlevel 2", "readme": "bin/create-readme.sh -a true -c false -m true -l false", "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", "pretest": "mkdir -pv test/tmp", - "test": "JYT_DEBUG=false JYT_ERROR=false jest --forceExit --expand --no-cache --coverage --config=./.jestrc.js", + "test": "jest --forceExit --expand --no-cache --coverage --config=./.jestrc.js", "eslint": "eslint .", "nsp": "nsp check", "inch": "inchjs suggest && inchjs list --all && inchjs stats" @@ -41,7 +40,7 @@ "json-stringify-safe": " ~5.0.1", "mkdirp-then": " ~1.2.0", "promisify-es6": "~1.0.2", - "serialize-js": " ~1.1.0" + "serialize-js": "deadratfink/serialize-js#feature/single_quote_only" }, "devDependencies": { "babel-cli": "~6.24.1", diff --git a/readme/BADGES.md b/readme/BADGES.md index e90bae4..1dcc8f8 100644 --- a/readme/BADGES.md +++ b/readme/BADGES.md @@ -13,11 +13,12 @@ [![bitHound Code][bithound-code-image]][bithound-url] [![bitHound Dependencies][bitHound-dependencies-image]][bitHound-dependencies] [![bitHound Dev Dependencies][bitHound-dev-dependencies-image]][bitHound-dependencies] +[![Codacy Badge](https://img.shields.io/codacy/grade/c2ebaac0f9874062ba468ff6bd7edc4e.svg?style=flat)](https://www.codacy.com/app/deadratfink/jy-transform?utm_source=github.com&utm_medium=referral&utm_content=deadratfink/jy-transform&utm_campaign=Badge_Grade) +[![Codacy Badge](https://img.shields.io/codacy/coverage/c2ebaac0f9874062ba468ff6bd7edc4e.svg?style=flat)](https://www.codacy.com/app/deadratfink/jy-transform?utm_source=github.com&utm_medium=referral&utm_content=deadratfink/jy-transform&utm_campaign=Badge_Coverage) +[![Greenkeeper badge](https://badges.greenkeeper.io/deadratfink/jy-transform.svg?style=flat)](https://greenkeeper.io/) [![NSP Status][nsp-image-master]][nsp-url-master] -[![Codacy Badge](https://img.shields.io/codacy/grade/c2ebaac0f9874062ba468ff6bd7edc4e.svg?style=flat-square)](https://www.codacy.com/app/deadratfink/jy-transform?utm_source=github.com&utm_medium=referral&utm_content=deadratfink/jy-transform&utm_campaign=Badge_Grade) -[![Codacy Badge](https://img.shields.io/codacy/coverage/c2ebaac0f9874062ba468ff6bd7edc4e.svg?style=flat-square)](https://www.codacy.com/app/deadratfink/jy-transform?utm_source=github.com&utm_medium=referral&utm_content=deadratfink/jy-transform&utm_campaign=Badge_Coverage) +[![bitHound Overall Score](https://www.bithound.io/github/deadratfink/jy-transform/badges/score.svg?style=flat)](https://www.bithound.io/github/deadratfink/jy-transform) --> @@ -25,54 +26,54 @@ [![NPM][npm-image]][npm-url] [![NPM][npm-downloads-image]][npm-url] -[gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat-square +[gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat [gh-license-url]: https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md -[gh-issues-image]: https://img.shields.io/github/issues/deadratfink/jy-transform.svg?style=flat-square +[gh-issues-image]: https://img.shields.io/github/issues/deadratfink/jy-transform.svg?style=flat [gh-issues-url]: https://github.com/deadratfink/jy-transform/issues -[gh-releases-image]: https://img.shields.io/github/release/deadratfink/jy-transform.svg?style=flat-square +[gh-releases-image]: https://img.shields.io/github/release/deadratfink/jy-transform.svg?style=flat [gh-releases-url]: https://github.com/deadratfink/jy-transform/releases -[gh-tags-image]: https://img.shields.io/github/tag/deadratfink/jy-transform.svg?style=flat-square +[gh-tags-image]: https://img.shields.io/github/tag/deadratfink/jy-transform.svg?style=flat [gh-tags-url]: https://github.com/deadratfink/jy-transform/tags -[ci-image]: https://img.shields.io/travis/deadratfink/jy-transform.svg?style=flat-square +[ci-image]: https://img.shields.io/travis/deadratfink/jy-transform.svg?style=flat [ci-url]: https://travis-ci.org/deadratfink/jy-transform/branches -[is-pull-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/pr?style=flat-square -[is-issue-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/issue?style=flat-square +[is-pull-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/pr?style=flat +[is-issue-image]: http://issuestats.com/github/deadratfink/jy-transform/badge/issue?style=flat [is-url]: http://issuestats.com/github/deadratfink/jy-transform -[waffle-ready-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat-square -[waffle-waffle-in-progress-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=in%20progress&title=Waffle%20In%20Progress&style=flat-square +[waffle-ready-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=ready&title=Waffle%20Ready&style=flat +[waffle-waffle-in-progress-image]: https://img.shields.io/waffle/label/deadratfink/jy-transform.svg?label=in%20progress&title=Waffle%20In%20Progress&style=flat [waffle-url]: https://waffle.io/deadratfink/jy-transform -[cocl-image]: https://img.shields.io/codeclimate/github/deadratfink/jy-transform.svg?style=flat-square +[cocl-image]: https://img.shields.io/codeclimate/github/deadratfink/jy-transform.svg?style=flat [cocl-url]: https://codeclimate.com/github/deadratfink/jy-transform -[cc-image-master]: https://img.shields.io/codecov/c/github/deadratfink/jy-transform/master.svg?style=flat-square +[cc-image-master]: https://img.shields.io/codecov/c/github/deadratfink/jy-transform/master.svg?style=flat [cc-url-master]: https://codecov.io/github/deadratfink/jy-transform?branch=master -[ca-image-master]: https://img.shields.io/coveralls/deadratfink/jy-transform/master.svg?style=flat-square +[ca-image-master]: https://img.shields.io/coveralls/deadratfink/jy-transform/master.svg?style=flat [ca-url-master]: https://coveralls.io/github/deadratfink/jy-transform?branch=master -[inch-image-master]: https://inch-ci.org/github/deadratfink/jy-transform.svg?branch=master&style=flat-square +[inch-image-master]: https://inch-ci.org/github/deadratfink/jy-transform.svg?branch=master&style=flat [inch-url-master]: https://inch-ci.org/github/deadratfink/jy-transform?branch=master -[dep-image-master]: https://img.shields.io/david/deadratfink/jy-transform/master.svg?style=flat-square +[dep-image-master]: https://img.shields.io/david/deadratfink/jy-transform/master.svg?style=flat [dep-url-master]: https://david-dm.org/deadratfink/jy-transform/master -[devdep-image-master]: https://img.shields.io/david/dev/deadratfink/jy-transform/master.svg?style=flat-square +[devdep-image-master]: https://img.shields.io/david/dev/deadratfink/jy-transform/master.svg?style=flat [devdep-url-master]: https://david-dm.org/deadratfink/jy-transform/master#info=devDependencies -[nsp-image-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0/badge?style=flat-square +[nsp-image-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0/badge?style=flat [nsp-url-master]: https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0 -[node-version-image]: https://img.shields.io/node/v/jy-transform.svg?style=flat-square +[node-version-image]: https://img.shields.io/node/v/jy-transform.svg?style=flat [node-version-url]: http://nodejs.org/download/ [npm-image]: https://nodei.co/npm/jy-transform.png?downloads=true&downloadRank=true&stars=true @@ -80,8 +81,8 @@ [npm-downloads-image]: https://nodei.co/npm-dl/jy-transform.png?height=2&months=9 [bithound-url]: https://www.bithound.io/github/deadratfink/jy-transform -[bithound-code-image]: https://img.shields.io/bithound/code/github/deadratfink/jy-transform.svg?style=flat-square +[bithound-code-image]: https://img.shields.io/bithound/code/github/deadratfink/jy-transform.svg?style=flat [bitHound-dependencies]: https://www.bithound.io/github/deadratfink/jy-transform/master/dependencies/npm -[bitHound-dependencies-image]: https://img.shields.io/bithound/dependencies/github/deadratfink/jy-transform.svg?style=flat-square -[bitHound-dev-dependencies-image]: https://img.shields.io/bithound/devDependencies/github/deadratfink/jy-transform.svg?style=flat-square -[bitHound-score-image]: https://img.shields.io/bithound/score/github/deadratfink/jy-transform.svg?style=flat-square +[bitHound-dependencies-image]: https://img.shields.io/bithound/dependencies/github/deadratfink/jy-transform.svg?style=flat +[bitHound-dev-dependencies-image]: https://img.shields.io/bithound/devDependencies/github/deadratfink/jy-transform.svg?style=flat +[bitHound-score-image]: https://img.shields.io/bithound/score/github/deadratfink/jy-transform.svg?style=flat diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index c5b06f0..5833f57 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -6,7 +6,7 @@ import { transform } from 'jy-transform'; const options = { - src: 'foo/bar.yaml', // Here: read from YAML file... + src: 'foo/bar.yaml', // E.g. read from YAML file... transform: async (object) => { // ...with exchanging value... object.foo = 'new value'; return object; @@ -24,7 +24,6 @@ transform(options) // ---- async/await style: - try { const msg = await transform(options); // Transform, of course, inside an async. console.log(msg); // Success message! @@ -38,11 +37,19 @@ try { ```javascript import { read } from 'jy-transform'; -const options = { src: 'foo/bar.yaml' }; // Here: read from file. +const options = { src: 'foo/bar.yaml' }; // E.g. read from file. + +// ---- Promise style: + +read(options) + .then((object) => console.log(JSON.stringify(object))) // Print read object. + .catch(console.error); + +// ---- async/await style: try { const object = await read(options); - console.log(JSON.stringify(object)); // Print write success message. + console.log(JSON.stringify(object)); // Print read object. } catch (err) { console.error(err.stack); } @@ -53,11 +60,19 @@ try { ```javascript import { write } from 'jy-transform'; -const options = { dest: 'foo/bar.yaml' }; // Here: write to file. +const options = { dest: 'foo/bar.yaml' }; // E.g. write to file. + +// ---- Promise style: + +write(object, options) + .then(console.log) // Print write success message. + .catch(console.error); + +// ---- async/await style: try { const msg = await write(object, options); - console.log(msg); // Print write success message. + console.log(msg); // Print write success message. } catch (err) { console.error(err.stack); } @@ -74,7 +89,7 @@ types into each other format. ## Usage The module can be used on CLI or as API (the latter is fully -[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) based). +[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)/`async` based). ### Usage Types @@ -90,9 +105,9 @@ Both usage types are described in more detail in the following sections. So, what are the typical use cases for this module? In terms of _transformation_ these consists of different phases: -- Reading files (`Reader`) -- Transforming JSON objects (`Transformer`) or apply dedicated actions on the intermediate JSON objects (`Transformer` + `Middleware`) -- Writing files (`Writer`) + 1. Reading from source + 2. Transforming JSON objects (`Transformer`) or apply dedicated actions on the intermediate JSON objects + 3. Writing to a destination #### Reading From @@ -103,17 +118,18 @@ these consists of different phases: Additionally, on API level from a: - `stream.Readable` - - Contain serialized JS, JSON or YAML - - If not file stream it requires `options.origin` property set - - Reads as UTF-8 + - Contain serialized JS, JSON or YAML + - If not file stream it requires `options.origin` property set + - Reads as UTF-8 - JS `object` - - Actually, this means the reading phase is "skipped", because object is in-memory already - - Of course, this case _cannot_ not be applied to serialized JSON or YAML content + - Actually, this means the reading phase is "skipped", because object is in-memory already + - Of course, this case _cannot_ not be applied to serialized JSON or YAML content -#### Transformation [+ Middleware] +#### Transformation The _transformation_ is usually a format change, but can also be refer to content changes on the -intermediate JS object (the latter via _middleware_). All possible directions are: +intermediate JS object, the latter with the help of a configured `transform` callback function. +All possible directions are: - YAML ⇒ JS - YAML ⇒ JSON @@ -131,8 +147,8 @@ while: - [JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript) = _*.js_ (JS object) - [JSON](http://json.org) = _*.json_ (JS object serialized as JSON) -As mentioned above a _middleware_ can apply particular actions on the intermediate JS object via injected functions. -This is an optional part for [transformation](#transformation) phase. +As mentioned above the configured `transform` callback can apply particular actions on the intermediate JS object, but +this is an optional part for [transformation](#transformation) phase. #### Writing To @@ -143,12 +159,12 @@ This is an optional part for [transformation](#transformation) phase. Additionally, on API level to a: - `stream.Writable` - - Serialized JS, JSON and YAML - - Requires `options.target` property set - - Writes UTF-8 + - Serialized JS, JSON and YAML + - Requires `options.target` property set + - Writes UTF-8 - JS `object - - JS as simple reference - - YAML and JSON as serialized string + - JS as simple reference + - YAML and JSON as serialized string ### Limitations @@ -190,7 +206,7 @@ Additionally, on API level to a: ### CLI Usage The CLI provides the `jyt` command which requires the use of some options. -After the global installation you can access the `Transformer`'s command options +After the global installation you can access the command options with the usual `--help` option which prints an overview about all available CLI properties: @@ -225,7 +241,8 @@ The ARGS are more formally defined in the following table: | `INPUT-FILE` | URI | The source file path for transformation. | - | yes | | `OUTPUT-FILE` | URI | The destination file path to transform to. | When this options is omitted then the output file is stored relative to the input file (same base name but with another extension if type differs). If input and output type are the same then the file overwriting is handled depending on the `--force` value! | no | -**NOTE:** the input file has to be specified and should be _first_ argument (in fact, it can be anywhere but it must be before an out file argument)! +> **NOTE:** the _input file_ has to be specified and should be _first_ argument (in fact, it can be +> anywhere but it must be _before_ an _output file_ argument)! #### CLI Options @@ -244,18 +261,15 @@ The OPTIONS are more formally defined in the following table: | `-v` | `--version` | n/a | Display the current version. | n/a | no | | `-h` | `--help` | n/a | Display help and usage details. | n/a | no | +### Examples -**NOTE:** an invalid indentation setting (1 > `-i`, `--indent` > 8) does not raise an error but a default of 4 SPACEs is applied instead. - -#### Examples - -Now we know which properties can be applied on CLI, so let's assume we +Now we know which properties can be applied on CLI it's time for some examples. Let's assume we have a YAML content located in _foo.yaml_ holding this data: ```yaml foo: bar ``` -##### Example: YAML ⇒ JSON +#### Example: YAML ⇒ JSON Then we can transform it to a JSON content as _foo.json_ file: @@ -267,18 +281,19 @@ Then we can transform it to a JSON content as _foo.json_ file: simply by using this command: -``` +```text $ jyt foo.yaml -t json -i 4 ``` -In this example we have overwritten the standard target type (which is `js`) -and applying an indent of 4 SPACEs instead of the default (2). As default the output +In this example we have overwritten the standard _target_ type (which is `js`) +and applying an _indent_ of 4 SPACEs instead of the default (which is 2). As default the output file _foo.json_ is written relative to the input file (by omitting the `dest` option here). -**NOTE:** here you _have_ to provide the target with option `-t json` or else the -default `js` would have been applied! If the source would have been a `js` -type like +> **NOTE:** here you _have_ to provide the target with option `-t json` or else the +> default `js` would have been applied! + +If the source would have been a `js` type like in this example ``` $ jyt foo.js -t json -i 4 @@ -287,9 +302,11 @@ $ jyt foo.js -t json -i 4 then the `js` value for `origin` is automatically inferred from file extension. Accordingly, this is also true for the `target` option. -##### Example: JSON ⇒ JS +#### Example: JSON ⇒ JS + The command -``` + +```text $ jyt foo.json -i 4 ``` results in _foo.js_: @@ -299,34 +316,42 @@ module.exports = { } ``` -##### Example: JS ⇒ YAML +#### Example: JS ⇒ YAML + The command -``` + +```text $ jyt foo.js -t yaml ``` + results in _foo.yaml_: + ```yaml foo: bar ``` -##### Example: Transformation with Different Destination +#### Example: Transformation with Different Destination Simply specify the _output_ file with a different file name: -``` + +```text $ jyt foo.json results/foobar.yaml ``` -##### Example: Transformation with Unsupported Source File Extension +#### Example: Transformation with Unsupported Source File Extension As said, normally we infer from file extension to the type, but assume the source file has a file name which does not imply the type (here a JSON type in a TEXT file), then you can simply provide the `-o` option with the -correct `origin` type (of course, the `-t` option works analogous): -``` +correct `origin` type: + +```text $ jyt foo.txt foobar.yaml -o json ``` -##### Example: Read from File with Exports Identifier +> **NOTE:** of course, the `-t` (`--target`) option works analogous. + +#### Example: Read from File with Exports Identifier It could be that a JS source `exports` several objects and you want to read from exactly the one you specify, then provide the `-m` (`--imports`) option. @@ -343,81 +368,90 @@ module.exports.bar = { }; ``` but you want to convert only `bar` object, then call: -``` + +```text $ jyt foo.js bar.yaml -m bar ``` + to get the YAML result: + ```yaml bar: foo ``` -**NOTE:** the same applies on API level when using JS objects as `dest`: - -```javascript -const fooBar = { - foo: 'bar', - bar: 'foo' -}; - -const options = { - src: fooBar, - dest: {}, - exports: 'bar' -}; - -//...transform -``` - -The transformation will result in this in-memory object: - -```javascript -bar: { - foo: 'bar', - bar: 'foo' -} -``` -Of course, as sub-node of `options.dest`. - -##### Example: Write Exports Identifier for JS File +> **NOTE:** the same applies on API level when using JS objects as `dest`: +> +> ```javascript +> const fooBar = { +> foo: 'bar', +> bar: 'foo' +> }; +> +> const options = { +> src: fooBar, +> dest: {}, +> exports: 'bar' +> }; + +> //...transform +> ``` +> +> The transformation will result in this in-memory object: +> +> ```javascript +> bar: { +> foo: 'bar', +> bar: 'foo' +> } +> ``` +> +> Of course, as sub-node of `options.dest`. + +#### Example: Write Exports Identifier for JS File Assume you want to generate a JS file with an exports string which gets an identifier. We reuse the YAML file from above: + ```yaml foo: bar ``` + using this command: + ``` $ jyt foo.yaml foobar.js -x foobar ``` + This generates the following output in JS file using `foobar` as identifier: + ```javascript module.exports.foobar = { foo: "bar" } ``` -**NOTE:** the identifier must be a valid JS identifier accoding to ECMAScript 6 -(see also [Valid JavaScript variable names in ECMAScript 6](https://mathiasbynens.be/notes/javascript-identifiers-es6) -and [Generating a regular expression to match valid JavaScript identifiers](https://mathiasbynens.be/demo/javascript-identifier-regex)). +> **NOTE:** the identifier must be a valid JS identifier accoding to ECMAScript 6 +> (see also [Valid JavaScript variable names in ECMAScript 6](https://mathiasbynens.be/notes/javascript-identifiers-es6) +> and [Generating a regular expression to match valid JavaScript identifiers](https://mathiasbynens.be/demo/javascript-identifier-regex)). -##### Example: Force Overwriting +#### Example: Force Overwriting -**IMPORTANT NOTE:** when using this feature then any subsequent -execution which uses the same target/file name, -will overwrite the original source or target created beforehand! +> **IMPORTANT NOTE:** when using this feature then any subsequent execution which +> uses the same target/file name, can _overwrite_ the original source or target created beforehand! -By default this feature is not enabled to prevent you from accidentally +Therefore, this feature is _not_ enabled by default to prevent you from accidentally overwriting your input source or already generated targets. But let's say we want to overwrite the original source now because you want to change the indentation from 2 to 4 SPACEs, then we can do this as follows: + +```text +$ jyt foo.js -i 4 -f ``` -$ jyt foo.js -f -``` -Of course, leaving out the `-f` switch creates a new file relatively to -the `origin`, named as _foo(1).js_ (note the consecutive number). Naturally, -another run of the command would result in a file called _foo(2).js_ -and so forth. + +> **NOTE:** the other way round (i.e. leaving out the `-f` (`--force`)) switch would create a _new file_ relatively to +> the `src` _foo.js_, named as _foo(1).js_; note the consecutive number! Naturally, +> another run of the command would result in a file called _foo(2).js_ and so forth. ### Origin and Target Type Inference @@ -431,8 +465,7 @@ extensions. This is supported as shown by the following table (from-to): | _*.js_ | _js_ | | _*.json_ | _json_ | -**NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to -specify the origin or target type! +> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! ### API Usage @@ -441,12 +474,12 @@ Since the usage on CLI is a 2-step process: 1. Read from source file to JS object ⇒ 2. Write out (maybe to other type) -the direct API calls additionally provide the usage of a _middleware_ function -where you can alter the input JS object before it is written and therefore, which turns +the direct API calls additionally provide the optional usage of a `transform` function +where you can alter the intermediate JS object before it is written and therefore, turns this into a 3-step process: 1. Read from source ⇒ - 2. Alter the JS object ⇒ + 2. Transform the JS object ⇒ 3. Write out (maybe to other type) For more details about this and all the functions provided by this module please refer to the @@ -456,97 +489,85 @@ The `origin` and `target` type inference is also standard for the API level. #### API Properties -The `Transformer` exposes the following function which takes besides an (optional) -`middleware` function the necessary `options` for the transformation: +The public `transform` function (that does not mean the optional `transform `callback here!) takes +the necessary `options` for the transformation: ```javascript -async function transform(options, middleware) +async function transform(options) ``` -#### Options - -The `options` object has to follow this key-values tables: +> **HINT:** of course, if you like you can use the `read`and `write` functionality solely besides any transformation needs. -##### Reader Options - -| Option | Type | Description | Default | Required | -| --- | --- | --- | --- | --- | -| `origin` | _String_ | The origin type. | If not given, the type is tried to be inferred from the extension of source path, else it is _yaml_. | no | -| `target` | _String_ | The target type. | If not given, the type is tried to be inferred from the extension of destination path, else it is _js_. | no | -| `src` | [ _String_ | _Stream.Readable_ | _Object_ ] | The source information object: `String` is used as file path, `Stream.Readable` provides a stringified source and `object` is used as direct JS source. | - | yes | - -##### Writer Options - -| Option | Type | Description | Default | Required | -| --- | --- | --- | --- | --- | -| `dest` | [ _String_ | _Stream.Writable_ | _Object_ ] | The destination information object: `String` is used as file path, `Stream.Writable` writes a stringified source and `object` is used for direct JS object assignment of the (stringified or JS object) source. If a string is set as file path then the output and if input and output file path are the same, then the file overwriting is handled depending on the `force` value! | - | yes | -| `indent` | _Number_ | The indentation in files. | 2 | no | -| `force` | _Boolean_ | Force overwriting of existing output files on write phase. When files are not overwritten, then the next transformation with same output file name gets a consecutive number on the base file name, e.g. in case of _foo.yaml_ it would be _foo(1).yaml_. | _false_ | no | -| `imports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (to read from JS _source_ only, must be a valid JS identifier!) | _undefined_ | no | -| `exports` | _String_ | Define a `module.exports[.identifier] = ...` identifier (for usage in JS _destination_ only, must be a valid JS identifier!) | _undefined_ | no | +#### Options -##### Transformer Options +For a detailed description see: -These are a combination of the [Reader](#reader-options) and [Writer](#writer-options) Options. +- [Read Options](API-PUBLIC.md#readoptions--codeobjectcode) +- [Write Options](API-PUBLIC.md#writeoptions--codeobjectcode) +- [Transform Options](API-PUBLIC.md#transformoptions--codeobjectcode) ##### Example ```javascript const options = { - origin: 'json', - target: 'yaml', src: 'foo.json', + origin: 'json', // actually, not needed here, inferred from src's extension automatically! dest: './foo/bar.yaml', + target: 'yaml', // actually, not needed here, inferred from dest's extension automatically! indent: 4 } ``` -#### Using Middleware +#### Using Transform Callback + +The `transform` property is optional but if provided it must be of type `Function` and could but must not be +a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)/`async`. + +- When being a Promise it has to resolve with the `data` object or reject with an `Error` based object. +- In case of normal or `async` function return `data` or throw an `Error`. -The `middleware` is optional but if provided it must be of type `Function` and -a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). One of the easiest ones is the identity function _f(data) → data_ -which could be expressed as [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) -function as follows: +which could be expressed as follows: ```javascript -const identity = async (data) => { - return data; -} +const identity = data => data; ``` Of course, this would have no effect on the provided JS data. Actually, this one is -used internally when no middleware is provided to ensure the proper promised +used internally as default when no `transform` function is configured to ensure the proper control flow. -OK, lets go back to a more practical example, e.g. we want to alter the value of +OK, lets go back to a more practical example, e.g. we want to alter the value of a JS property before it is written to a file. Assuming we have this piece of YAML -object as input: +as input from a file called _src.yaml_: ```yaml foo: old bar ``` -Applying this [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) -as middleware +Applying this `transform` callback option to the `src` content ```javascript import { transform } from 'jy-transform'; -const middleware = async (data) => { - data.foo = 'new bar'; - return data; +const options = { + src: 'src.yaml', + dest: 'result.json', + transform: async (data) => { + data.foo = 'new bar'; + return data; + } }; -transform(options, middleware) +transform(options) .then(console.log) .catch(console.error); ``` -will result in such JSON file: +will result in such JSON file content: ```json { @@ -564,43 +585,44 @@ Let's assume we have some Promise functions to apply. For simplicity reasons we simulate these for the moment by some functions, each adding a key-value to the given (initially empty) JS object. -**NOTE:** each of them has to resolve with the `data` object! +> **NOTE:** each of them has to resolve with the `object` object! ```javascript -const key1 = async (data) => { - odata.key1 = 'value1'; - return data; +const key1 = async (object) => { + object.key1 = 'value1'; + return object; }; -const key2 = async (data) => { - data.key2 = 'value2'; - return data; +const key2 = async (object) => { + object.key2 = 'value2'; + return object; }; -const key3 = async (data) => { - data.key3 = 'value3'; - return data; +const key3 = async (object) => { + object.key3 = 'value3'; + return object; }; ``` These can be collected by different aggregation or composition functions of the underlying Promise framework, e.g. using the [`Promise.all([...])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) -function. This one can collect all three functions above and ensure their proper subsequent execution: +function. This one can collect all three functions above and ensure their proper execution: ```javascript import { transform } from 'jy-transform'; -const middleware = (data) => { - return Promise.all([key1(data), key2(data), key3(data)]) - .then(result => result[result.length - 1]); +const options = { + src: {}, + transform: async (object) => Promise.all([key1(object), key2(object), key3(object)]) + .then(result => result[result.length - 1]) }; -return transform({ src: {} }, middleware) +transform(options) .then(console.log) .catch(console.error); ``` -Then the result in the `middleware` function can be retrieved from the returned +The result in the `transform` function can be retrieved from the returned array, i.e. in case of [`Promise.all([...])`](http://bluebirdjs.com/docs/api/promise.all.html) you have to pick the _last_ element which contains the "final product". @@ -614,26 +636,24 @@ From our example above it would be result in this object } ``` -which then is passed back to the transformation chain. Following this pattern +which is passed back to the transformation chain. Following this pattern you can do almost everything with the JS object, like -- deleting properties -- changing properties to other types -- validating and throwing error if not valid +- Deleting properties +- Changing properties to other types +- Validating and throwing/resolving with error if not valid - ... -Whatever you do during transformation, just keep it valid ;-) - ## API Reference For more details on how to use the API, please refer to the -[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-v3) +[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-public-v3) wiki which describes the full API and provides more examples. ## Contributing Pull requests and stars are always welcome. For bugs and feature requests, please create an -[issue](https://github.com/deadratfink/jy-transform/issues). +[issue](https://github.com/deadratfink/jy-transform/issues) or create a PR. See the wiki [Contributing](https://github.com/deadratfink/jy-transform/wiki/Contributing) section for more details about conventions. diff --git a/src/cli.js b/src/cli.js index 48a7399..5906e5b 100755 --- a/src/cli.js +++ b/src/cli.js @@ -10,7 +10,10 @@ import { TARGET_DESCRIPTION, TYPE_JS, TYPE_JSON, - TYPE_YAML + TYPE_YAML, + DEFAULT_STRICT, + DEFAULT_NO_ES6, + DEFAULT_NO_SINGLE_QUOTES, } from './constants'; import { transform } from './transformer'; @@ -50,18 +53,24 @@ const packagePath = path.join(__dirname, '../package.json'); * @private */ const cliOptionsSchema = { - origin: ['o', 'The origin type of INPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML - + ' ]', 'string', ORIGIN_DESCRIPTION], - target: ['t', 'The target type of OUTPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML - + ' ]', 'string', TARGET_DESCRIPTION], + origin: ['o', 'The origin type of INPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + ' ]', + 'string', ORIGIN_DESCRIPTION], + target: ['t', 'The target type of OUTPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + ' ]', + 'string', TARGET_DESCRIPTION], indent: ['i', 'The indention for pretty-print: 1 - 8', 'int', DEFAULT_INDENT], force: ['f', 'Force overwriting of existing output files on write phase: when files are not overwritten (which' + ' is default), then the next transformation with same output file name gets a consecutive number on the base' + ' file name, e.g. in case of foo.yaml it would be foo(1).yaml', 'boolean', DEFAULT_FORCE_FILE_OVERWRITE], - imports: ['m', 'Define a \'module.exports[.identifier] = \' identifier for object (to read from JS source file ' + - 'only, must be a valid JS identifier!)', 'string', DEFAULT_JS_IMPORTS_IDENTIFIER], - exports: ['x', 'Define a \'module.exports[.identifier] = \' identifier for object (to write in JS destination file ' + - 'only, must be a valid JS identifier!)', 'string', DEFAULT_JS_EXPORTS_IDENTIFIER] + imports: ['m', 'Define an identifier for object (to read as "export const identifier / module.exports[.identifier]"' + + ' from JS source file only, must be a valid JS identifier!)', 'string', DEFAULT_JS_IMPORTS_IDENTIFIER], + exports: ['x', 'Define an identifier for object (write to "export const identifier / module.exports[.identifier]"' + + ' in JS destination file only, must be a valid JS identifier!)', 'string', DEFAULT_JS_EXPORTS_IDENTIFIER], + strict: ['s', 'Whether to write a "use strict;" in JS type output', + 'boolean', DEFAULT_STRICT], + 'no-es6': [false, 'Whether not to use ECMAScript6 syntax for JS type output like "module.exports" instead of ' + + '"export default", applicable only for JS output', 'boolean', DEFAULT_NO_ES6], + 'no-single': [false, 'Whether not to use single-quotes style for values in JS type output (i.e. double-quotes)', + 'boolean', DEFAULT_NO_SINGLE_QUOTES], }; /** diff --git a/src/constants.js b/src/constants.js index 5ed1626..8c1aebc 100644 --- a/src/constants.js +++ b/src/constants.js @@ -105,6 +105,31 @@ export const DEFAULT_TARGET = TYPE_JS; */ export const DEFAULT_FORCE_FILE_OVERWRITE = false; +/** + * Whether to write a "use strict;" in JS type output. + * @type {boolean} + * @constant + * @private + */ +export const DEFAULT_STRICT = false; + + +/** + * Whether _not_ to use ECMAScript6 syntax for JS type output. + * @type {boolean} + * @constant + * @private + */ +export const DEFAULT_NO_ES6 = false; + +/** + * Whether _not_ to use single-quotes style for values in JS type output (i.e. double-quotes). + * @type {boolean} + * @constant + * @private + */ +export const DEFAULT_NO_SINGLE_QUOTES = false; + /** * The `origin` description value. * @type {string} diff --git a/src/io-utils.js b/src/io-utils.js new file mode 100644 index 0000000..d5ff4c3 --- /dev/null +++ b/src/io-utils.js @@ -0,0 +1,219 @@ +import mkdirp from 'mkdirp-then'; +import path from 'path'; +import fs from 'fs'; +import { Buffer } from 'buffer'; +import jsYaml from 'js-yaml'; +import promisify from 'promisify-es6'; +import { + UTF8, + TYPE_JS, + TYPE_JSON, +} from './constants'; + +/** + * Promisified `fs` module. + * @private + */ +const fsPromisified = promisify(fs); + +/** + * @module jy-transform:io-utils + * @description This module provides an I/O interface for files, streams or `Object`. + * @private + */ + +/** + * Reads JS or JSON from file. + * + * @param {string} file - The file path. + * @param {string} [imports] - An object which is exported in the file. + * @returns {Object} The read object. + * @throws {Error} When an `imports` is given but the declared object key is not exported by the file. + * @private + */ +export function readJsOrJsonFromFile(file, imports) { + const resolvedPath = path.resolve('', file); + if (imports) { + // eslint-disable-next-line import/no-dynamic-require, global-require + const object = require(resolvedPath)[imports]; + if (!object) { + throw new Error('an identifier string \'' + imports + '\' was specified for JS object ' + + 'but could not find this object, pls ensure that file ' + file + ' exports it.'); + } + return object; + } + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(resolvedPath); // reads both: JS and JSON! +} + +/** + * Reads JS from JS object. + * + * @param {string} object - The JS object source. + * @param {string} [imports] - An object which is is a sub-object in `object`. + * @returns {Object} The given `object` object or any sub-object specified by `imports`. + * @throws {Error} When an `imports` is given but the declared object key is not contained in the source object. + * @private + */ +export function readJsFromObject(object, imports) { + if (imports) { + const subObject = object[imports]; + if (!subObject) { + throw new Error('an identifier string \'' + imports + '\' was specified for JS sub-object ' + + 'but could not find this sub-object, pls ensure that object source contains it.'); + } + return Object.assign({}, subObject); // clone, do not alter original object! + } + return Object.assign({}, object); // clone, do not alter original object! +} + +/** + * Reads YAML from file. + * + * @param {string} file - The YAML file source. + * @returns {Object} The read JS object from YAML file. + * @throws {Error} When any I/O error occurs while the source file. + * @private + */ +export async function readYamlFromfile(file) { + // load source from YAML file + const yaml = await fsPromisified.readFile(file, UTF8); + try { + return jsYaml.safeLoad(yaml); // TODO promisify??? + } catch (err) { // probably a YAMLException + throw err; + } +} + +/** + * Reads from a passed stream and resolves by callback. + * + * @param {Stream.Readable} readable - The source to read from. + * @param {string} origin - Origin type, must be 'yaml' or 'json'/'js'. + * @returns {Promise.} The read content as JS object representation. + * @see {@link TYPE_YAML} + * @see {@link TYPE_JSON} + * @see {@link TYPE_JS} + * @private + */ +export function readFromStream(readable, origin) { + return new Promise((resolve, reject) => { + const buffers = []; + readable + .on('data', data => buffers.push(data)) + .on('error', reject) + .on('end', () => { + const buffer = Buffer.concat(buffers); + try { + if (origin === TYPE_JS || origin === TYPE_JSON) { + resolve(JSON.parse(buffer.toString(UTF8))); + } else { // Validation allows only YAML here! + resolve(jsYaml.safeLoad(buffer.toString(UTF8))); + } + } catch (err) { // probably a SyntaxError for JSON or a YAMLException + readable.emit('error', err); // send to .on('error',... + } + }); + }); +} + +/** + * Turns the destination file name into a name containing a consecutive + * number if it exists. It iterates over the files until it finds a file + * name which does not exist. + * + * @param {string} dest - The destination file. + * @returns {string} A consecutive file name or the original one if `dest` file does not exist. + * @private + */ +function getConsecutiveDestName(dest) { + let tmpDest = dest; + let i = 0; + const destDirName = path.dirname(tmpDest); + const ext = path.extname(tmpDest); + const basename = path.basename(tmpDest, ext); + while (fs.existsSync(tmpDest)) { + tmpDest = path.join(destDirName, basename + '(' + (i += 1) + ')' + ext); + } + return tmpDest; +} + +/** + * Ensures that all dirs exists for file type `dest` and writes the JS object to file. + * + * @param {string} object - The object to write into file. + * @param {string} dest - The file destination path. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @param {boolean} [forceOverwrite=false] - Forces overwriting the destination file if `true`. + * @see {@link TYPE_YAML} + * @see {@link TYPE_JSON} + * @see {@link TYPE_JS} + * @private + */ +async function mkdirAndWrite(object, dest, target, forceOverwrite = false) { + const destDir = path.dirname(dest); + await mkdirp(destDir); + let finalDestination = dest; + if (!forceOverwrite) { + finalDestination = getConsecutiveDestName(dest); + } + await fsPromisified.writeFile(finalDestination, object, UTF8); + return 'Writing \'' + target + '\' file \'' + finalDestination + '\' successful.'; +} + +/** + * Writes a serialized object to file. + * + * @param {string} object - The object to write into file. + * @param {string} dest - The file destination path. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. + * @see {@link TYPE_YAML} + * @see {@link TYPE_JSON} + * @see {@link TYPE_JS} + * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). + * @throws {Error} If serialized JSON file could not be written due to any reason. + * @private + */ +export function writeToFile(object, dest, target, forceOverwrite) { + return new Promise((resolve, reject) => { + fsPromisified.stat(dest) + .then((stats) => { + if (stats.isDirectory()) { + reject(new Error('Destination file is a directory, pls specify a valid file resource!')); + return; + } + // file exists + resolve(mkdirAndWrite(object, dest, target, forceOverwrite)); + }) + .catch(() => { + // ignore error (because file could possibly not exist at this point of time) + resolve(mkdirAndWrite(object, dest, target, forceOverwrite)); + }); + }); +} + +/** + * Writes a string serialized data object to a stream. + * + * @param {string} object - The data to write into stream. + * @param {string} dest - The stream destination. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @see {@link TYPE_YAML} + * @see {@link TYPE_JSON} + * @see {@link TYPE_JS} + * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). + * @throws {Error} If serialized JS object could not be written due to any reason. + * @private + */ +export function writeToStream(object, dest, target) { + return new Promise((resolve, reject) => { + dest + .on('error', reject) + .on('finish', () => resolve('Writing ' + target + ' to stream successful.')); + + // write stringified data + dest.write(object); + dest.end(); + }); +} diff --git a/src/reader.js b/src/reader.js index 40872aa..6ed060d 100644 --- a/src/reader.js +++ b/src/reader.js @@ -1,13 +1,13 @@ -import jsYaml from 'js-yaml'; -import promisify from 'promisify-es6'; -import { Buffer } from 'buffer'; -import path from 'path'; -import fs from 'fs'; import isStream from 'is-stream'; import { readOptionsSchema } from './validation/options-schema'; import Joi from './validation/joi-extensions'; import { - UTF8, + readJsFromObject, + readJsOrJsonFromFile, + readYamlFromfile, + readFromStream, +} from './io-utils'; +import { TYPE_YAML, TYPE_JS, TYPE_JSON, @@ -15,46 +15,10 @@ import { /** * @module jy-transform:reader - * @description This module provides the _read_ functionality for YAML, JS or JSON sources. - * @private - */ - -/** - * Promisified `fs` module. + * @description This module provides the _public_ interface for the _read_ functionality of YAML, JS or JSON sources + * (file, `Object` or {@link stream.Readable}). * @private */ -const fsPromisified = promisify(fs); - -/** - * Reads from a passed stream and resolves by callback. - * - * @param {Stream.Readable} readable - The source to read from. - * @param {string} origin - Origin type, must be 'yaml' or 'json'/'js'. - * @returns {Promise.} The read content as JS object representation. - * @private - */ -function readFromStream(readable, origin) { - return new Promise((resolve, reject) => { - const buffers = []; - readable - .on('data', data => buffers.push(data)) - .on('error', reject) - .on('end', () => { - const buffer = Buffer.concat(buffers); - try { - // logger.debug(origin + ' reading from Readable'); TODO remove - if (origin === TYPE_JSON || origin === TYPE_JS) { - resolve(JSON.parse(buffer.toString(UTF8))); - } else { // HINT: commented (see below): if (origin === YAML) { - resolve(jsYaml.safeLoad(buffer.toString(UTF8))); - } - } catch (err) { // probably a SyntaxError for JSON or a YAMLException - // logger.error('Unexpected error: ' + err.stack); TODO remove - readable.emit('error', err); // send to .on('error',... - } - }); - }); -} /** * Reads the data from a given JS or JSON source. @@ -63,36 +27,14 @@ function readFromStream(readable, origin) { * @returns {Promise.} Contains the read JS object. * @private */ -async function readJs(options) { +async function readJsOrJson(options) { if (typeof options.src === 'string') { // path to JSON or JS file - const resolvedPath = path.resolve('', options.src); - if (options.imports) { - // eslint-disable-next-line import/no-dynamic-require, global-require - const object = require(resolvedPath)[options.imports]; - if (!object) { // TODO check this as part of config validation? - throw new Error('an identifier string \'' + options.imports + '\' was specified for JS object ' + - 'but could not find this object, pls ensure that file ' + options.src + ' contains it.'); - } else { - return object; - } - } else { - // eslint-disable-next-line import/no-dynamic-require, global-require - return require(resolvedPath); // reads both: JS and JSON! - } + return readJsOrJsonFromFile(options.src, options.imports); } else if (isStream.readable(options.src)) { return readFromStream(options.src, TYPE_JSON); // reads both: JS or JSON! - } else if (options.imports) { // options.src is JS object here! - const subObject = options.src[options.imports]; - // logger.debug('LOADED JSON object (' + options.imports + '):: ' + stringify(subObject, null, 4)); TODO remove - if (!subObject) { - throw new Error('an identifier string \'' + options.imports + '\' was specified for JS object ' + - 'but could not find this object, pls ensure that object source contains it.'); - } else { - return Object.assign({}, subObject); // clone, do not alter original object! - } - } else { // options.src is JS object here! - return Object.assign({}, options.src); // clone, do not alter original object! } + // options.src is JS object here! + return readJsFromObject(options.src, options.imports); } /** @@ -106,15 +48,7 @@ async function readJs(options) { */ async function readYaml(options) { if (typeof options.src === 'string') { - // load source from YAML file - const yaml = await fsPromisified.readFile(options.src, UTF8); - // logger.debug('YAML loaded from file ' + options.src); TODO remove - try { - return jsYaml.safeLoad(yaml); - } catch (err) { // probably a YAMLException - // logger.error('Unexpected error: ' + err.stack); TODO remove - throw err; - } + return readYamlFromfile(options.src); } // as validation has passed already, this can only be stream here return readFromStream(options.src, TYPE_YAML); @@ -177,7 +111,7 @@ export async function read(options) { switch (validatedOptions.origin) { case TYPE_JS: case TYPE_JSON: - return readJs(validatedOptions); + return readJsOrJson(validatedOptions); default: return readYaml(validatedOptions); } diff --git a/src/serialize-utils.js b/src/serialize-utils.js new file mode 100644 index 0000000..a32026e --- /dev/null +++ b/src/serialize-utils.js @@ -0,0 +1,62 @@ +import os from 'os'; +import jsonStringifySafe from 'json-stringify-safe'; +import serializeJs from 'serialize-js'; + +/** + * @module jy-transform:serialize-utils + * @description This module provides an interface for serializing JS either to string or the JSON format. + * @private + */ + +/** + * Creates a potential named `'module.exports[.exportsTo]'` string. + * + * @param {boolean} es6 - Whether to use ECMAScript6 export syntax. + * @param {string} [exportsTo] - The export name. + * @returns {Promise.} Resolves with the exports string. + * @private + */ +async function createExportString(es6, exportsTo) { + console.log('################## ####### es6 = ' + es6) + let exports = es6 ? 'export' : 'module.exports'; + if (exportsTo) { + exports += es6 ? ` const ${exportsTo} = ` : '.' + exportsTo + ' = '; + } else { + exports += es6 ? ' default ' : ' = '; + } + return exports; +} + +/** + * Serialize a JS object to string. + * + * @param {Object} object - The JS Object to serialize. + * @param {WriteOptions} options - The write options. + * @returns {Promise.} - Promise resolve with the serialized JS content. + * @private + */ +export async function serializeJsToString(object, options) { + let useStrict = ''; + if (options.strict) { + const quote = options['no-single'] ? '"' : '\''; + useStrict = `${quote}use strict;${quote}${os.EOL}${os.EOL}`; + } + const exportsStr = await createExportString(!options['no-es6'], options.exports); + return `${useStrict}${exportsStr}${serializeJs.serialize(object, { + indent: options.indent, + forceSingleQuotes: !options['no-single'], + })};${os.EOL}`; +} + +/** + * Serialize a JS object to JSON string. + * + * @param {Object} object - The object to serialize. + * @param {number} indent - The code indention. + * @returns {string} The serialized JSON. + * @private + */ +export async function serializeJsToJsonString(object, indent) { + return jsonStringifySafe(object, null, indent) + os.EOL; +} + diff --git a/src/type-definitions.js b/src/type-definitions.js index 755a398..d15fbb1 100644 --- a/src/type-definitions.js +++ b/src/type-definitions.js @@ -63,6 +63,11 @@ * @property {number} [indent=2] - The indentation value for pretty-print of output. * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. * @property {string} [force=false] - Force overwriting of existing output files on write phase. + * @property {boolean} [no-es6=false] - Whether not to use ECMAScript6 syntax for JS type output like + * `module.exports` instead of `export default`, applicable only + * for JS output. + * @property {boolean} [no-single=false] - Whether _not_ to use single-quotes style for values in JS type + * output (i.e. double-quotes). * @public */ @@ -91,5 +96,10 @@ * @property {number} [indent=2] - The _write_ indentation value for pretty-print of output. * @property {string} [exports=undefined] - The _write_ exports name for usage in JS destination files only. * @property {string} [force=false] - Force overwriting of existing output files on write phase. + * @property {boolean} [no-es6=false] - Whether not to use ECMAScript6 syntax for JS type output like + * `module.exports` instead of `export default`, applicable only + * for JS output. + * @property {boolean} [no-single=false] - Whether _not_ to use single-quotes style for values in JS type + * output (i.e. double-quotes). * @public */ diff --git a/src/validation/joi-extensions-file-helper.js b/src/validation/joi-extensions-file-utils.js similarity index 91% rename from src/validation/joi-extensions-file-helper.js rename to src/validation/joi-extensions-file-utils.js index e43b663..4b00f24 100644 --- a/src/validation/joi-extensions-file-helper.js +++ b/src/validation/joi-extensions-file-utils.js @@ -2,7 +2,7 @@ import fs from 'fs'; import path from 'path'; /** - * @module jy-transform:validation:joi-extensions-file-helper + * @module jy-transform:validation:joi-extensions-file-utils * @description An (extended) Joi validation schema helper functions for the module options on FS validation. * @private */ diff --git a/src/validation/joi-extensions-identifier-helper.js b/src/validation/joi-extensions-identifier-utils.js similarity index 99% rename from src/validation/joi-extensions-identifier-helper.js rename to src/validation/joi-extensions-identifier-utils.js index 397770c..7fd3d80 100644 --- a/src/validation/joi-extensions-identifier-helper.js +++ b/src/validation/joi-extensions-identifier-utils.js @@ -1,5 +1,5 @@ /** - * @module jy-transform:validation:joi-extensions-identifier-helper + * @module jy-transform:validation:joi-extensions-identifier-utils * @description An (extended) Joi validation schema helper function for the module options to validate ES6 identifiers. * @private */ @@ -20,7 +20,7 @@ const identifierRegExpECMAScript6 = /^(?!(?:do|if|in|for|let|new|try|var|case|el * @returns {boolean} A `true` if valid, else `false`. * @protected * @example - * import { isValidEs6Identifier } from './validation/joi-extensions-identifier-helper.js'; + * import { isValidEs6Identifier } from './validation/joi-extensions-identifier-utils.js'; * const identifier = 'foo'; * console.log('valid = ' + isValidEs6Identifier(identifier)); */ diff --git a/src/validation/joi-extensions.js b/src/validation/joi-extensions.js index 3e8d3da..005eea2 100644 --- a/src/validation/joi-extensions.js +++ b/src/validation/joi-extensions.js @@ -1,7 +1,7 @@ import JoiBase from 'joi'; import promisify from 'promisify-es6'; -import { isExistingFile } from './joi-extensions-file-helper'; -import { isValidEs6Identifier } from './joi-extensions-identifier-helper'; +import { isExistingFile } from './joi-extensions-file-utils'; +import { isValidEs6Identifier } from './joi-extensions-identifier-utils'; /** * @module jy-transform:validation:joi-extension diff --git a/src/validation/options-schema-helper.js b/src/validation/options-schema-utils.js similarity index 98% rename from src/validation/options-schema-helper.js rename to src/validation/options-schema-utils.js index 85bbd5e..d0f7de2 100644 --- a/src/validation/options-schema-helper.js +++ b/src/validation/options-schema-utils.js @@ -7,7 +7,7 @@ import { } from '../constants'; /** - * @module jy-transform:validation:options-schema-helper + * @module jy-transform:validation:options-schema-utils * @description Provides some helper functions used in {@link module:validation:options-schema} to resolve default * values for origin and target depending on the `options.src` or `options.dest` value. * @type {Object} diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js index b1c486d..7a62e97 100644 --- a/src/validation/options-schema.js +++ b/src/validation/options-schema.js @@ -4,7 +4,7 @@ import { inferDestDefaultFromSrc, inferOriginDefault, inferTargetDefault, -} from './options-schema-helper'; +} from './options-schema-utils'; import { TYPE_YAML, TYPE_JS, @@ -14,6 +14,9 @@ import { MIN_INDENT, MIN_YAML_INDENT, MAX_INDENT, + DEFAULT_STRICT, + DEFAULT_NO_ES6, + DEFAULT_NO_SINGLE_QUOTES, } from '../constants'; /** @@ -25,7 +28,7 @@ import { */ /** - * The `force` options schema. + * The `force` option schema. * @type {external:joi.Schema} * @private */ @@ -35,30 +38,59 @@ const forceSchema = Joi .description('Force overwriting of existing output files on write phase.'); /** - * The `indent` options schema. + * The `force` option schema. * @type {external:joi.Schema} * @private */ -const indentSchema = Joi - .when('target', { - is: TYPE_YAML, - then: Joi - .number() - .integer() - .min(MIN_YAML_INDENT) // Must be 2 for YAML type! - .max(MAX_INDENT) - .default(DEFAULT_INDENT), - otherwise: Joi - .number() - .integer() - .min(MIN_INDENT) - .max(MAX_INDENT) - .default(DEFAULT_INDENT), - }) - .description('The indention value for pretty-print of output.'); +const strictSchema = Joi + .boolean() + .default(DEFAULT_STRICT) + .description('Whether to write a "use strict;" in JS type output.'); + +/** + * The `no-es6` option schema. + * @type {external:joi.Schema} + * @private + */ +const noES6Schema = Joi + .boolean() + .default(DEFAULT_NO_ES6) + .description('Whether not to use ECMAScript6 syntax for JS type output like "module.exports" instead of ' + + '"export default", applicable only for JS output.'); + +/** + * The `no-single` option schema. + * @type {external:joi.Schema} + * @private + */ +const noSingleSchema = Joi + .boolean() + .default(DEFAULT_NO_SINGLE_QUOTES) + .description('Whether not to use single-quotes style for values in JS type output (i.e. double-quotes).'); + +/** + * The `indent` option schema. + * @type {external:joi.Schema} + * @private + */ +const indentSchema = Joi.when('target', { + is: TYPE_YAML, + then: Joi + .number() + .integer() + .min(MIN_YAML_INDENT) // Must be 2 for YAML type! + .max(MAX_INDENT) + .default(DEFAULT_INDENT), + otherwise: Joi + .number() + .integer() + .min(MIN_INDENT) + .max(MAX_INDENT) + .default(DEFAULT_INDENT), +}).description('The indention value for pretty-print of output.'); /** - * The `exports` options schema. + * The `exports` option schema. * @type {external:joi.Schema} * @private */ @@ -68,31 +100,29 @@ const exportsSchema = Joi .description('The name of property to export while writing a JS object to a JS output destination.'); /** - * The `target` options schema. + * The `target` option schema. * @type {external:joi.Schema} * @private */ -const targetSchema = Joi - .when('dest', { - is: Joi.object().type(Stream.Writable), - then: Joi - .string() - .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferTargetDefault, 'try target default resolution from dest type if not set (Stream.Writable)'), - otherwise: Joi - .when('dest', { - is: Joi.string(), - then: Joi - .string() - .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(inferTargetDefault, 'try target resolution from dest type if latter not set (String)'), - otherwise: Joi // check - .string() - .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) - .default(TYPE_JS), - }), - }) - .description('The target type of output.'); +const targetSchema = Joi.when('dest', { + is: Joi.object().type(Stream.Writable), + then: Joi + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(inferTargetDefault, 'try target default resolution from dest type if not set (Stream.Writable)'), + otherwise: Joi + .when('dest', { + is: Joi.string(), + then: Joi + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(inferTargetDefault, 'try target resolution from dest type if latter not set (String)'), + otherwise: Joi // check + .string() + .valid(TYPE_YAML, TYPE_JSON, TYPE_JS) + .default(TYPE_JS), + }), +}).description('The target type of output.'); /** * The prepared {@link external:joi.Schema} for validating the {@link ReadOptions}. @@ -162,6 +192,9 @@ export const writeOptionsSchema = Joi.object().keys({ exports: exportsSchema, indent: indentSchema, force: forceSchema, + strict: strictSchema, + 'no-es6': noES6Schema, + 'no-single': noSingleSchema, }).default() .required() .unknown(); @@ -192,6 +225,9 @@ export const transformOptionsSchema = readOptionsSchema.concat(Joi.object().keys exports: exportsSchema, indent: indentSchema, force: forceSchema, + strict: strictSchema, + 'no-es6': noES6Schema, + 'no-single': noSingleSchema, }).default() .required() ); diff --git a/src/writer.js b/src/writer.js index a2574ba..0f96e49 100644 --- a/src/writer.js +++ b/src/writer.js @@ -1,181 +1,32 @@ -import fs from 'fs'; import isStream from 'is-stream'; import jsYaml from 'js-yaml'; -import jsonStringifySafe from 'json-stringify-safe'; -import mkdirp from 'mkdirp-then'; -import os from 'os'; -import path from 'path'; -import promisify from 'promisify-es6'; -import serializeJs from 'serialize-js'; import { TYPE_JS, TYPE_JSON, TYPE_YAML, - UTF8 } from './constants'; import Joi from './validation/joi-extensions'; import { writeOptionsSchema } from './validation/options-schema'; +import { + serializeJsToJsonString, + serializeJsToString +} from './serialize-utils'; +import { + writeToFile, + writeToStream, +} from './io-utils'; /** * @module jy-transform:writer - * @description This module provides the _write_ functionality to write JS objects from memory to a JSON/JS/YAML - * destination (file, object or {@link stream.Readable}). - * @private - */ - -/** - * Promisified `fs` module. + * @description This module provides the _public_ interface for the _write_ functionality to write JS objects from + * memory to a JSON/JS/YAML destination (file, `Object` or {@link stream.Writable}). * @private */ -const fsPromisified = promisify(fs); // //////////////////////////////////////////////////////////////////////////// // METHODS (PRIVATE) // //////////////////////////////////////////////////////////////////////////// -/** - * Creates a potential named `'module.exports[.exportsTo]'` string. - * - * @param {string} [exportsTo] - The export name. - * @returns {Promise.} Resolves with the exports string. - * @private - */ -async function createExportsString(exportsTo) { - let exports = 'module.exports'; - if (exportsTo) { - exports += '.' + exportsTo + ' = '; - } else { - exports += ' = '; - } - return exports; -} - -/** - * Serialize a JS object to string. - * - * @param {Object} object - The JS Object to serialize. - * @param {number} indent - The indention. - * @param {string} [exportsTo] - Name for export (*IMPORTANT:* must be a valid ES6 identifier). - * @returns {Promise.} - Promise resolve with the serialized JS content. - * @private - * @todo [[#35](https://github.com/deadratfink/jy-transform/issues/35)] Add `'use strict';` in JS output file (-> - * `'\'use strict\';' + os.EOL + os.EOL + ...`)? - */ -async function serializeJsToString(object, indent, exportsTo) { - const exportsStr = await createExportsString(exportsTo); - return exportsStr + serializeJs.serialize(object, { indent }) + ';' + os.EOL; -} - -/** - * Serialize a JS object to JSON string. - * - * @param {Object} object - The object to serialize. - * @param {number} indent - The code indention. - * @returns {string} The serialized JSON. - * @private - */ -async function serializeJsToJsonString(object, indent) { - return jsonStringifySafe(object, null, indent) + os.EOL; -} - -/** - * Turns the destination file name into a name containing a consecutive - * number if it exists. It iterates over the files until it finds a file - * name which does not exist. - * - * @param {string} dest - The destination file. - * @returns {string} A consecutive file name or the original one if `dest` file does not exist. - * @private - */ -function getConsecutiveDestName(dest) { - let tmpDest = dest; - let i = 0; - const destDirName = path.dirname(tmpDest); - const ext = path.extname(tmpDest); - const basename = path.basename(tmpDest, ext); - while (fs.existsSync(tmpDest)) { - tmpDest = path.join(destDirName, basename + '(' + (i += 1) + ')' + ext); - } - return tmpDest; -} - -/** - * Ensures that all dirs exists for file type `dest` and writes the JS object to file. - * - * @param {string} object - The object to write into file. - * @param {string} dest - The file destination path. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @param {boolean} [forceOverwrite=false] - Forces overwriting the destination file if `true`. - * @private - */ -async function mkdirAndWrite(object, dest, target, forceOverwrite = false) { - const destDir = path.dirname(dest); - await mkdirp(destDir); - let finalDestination = dest; - if (!forceOverwrite) { - finalDestination = getConsecutiveDestName(dest); - } - await fsPromisified.writeFile(finalDestination, object, UTF8); - return 'Writing \'' + target + '\' file \'' + finalDestination + '\' successful.'; -} - -/** - * Writes a serialized object to file. - * - * @param {string} object - The object to write into file. - * @param {string} dest - The file destination path. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. - * @see {@link TYPE_YAML} - * @see {@link TYPE_JSON} - * @see {@link TYPE_JS} - * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). - * @throws {Error} If serialized JSON file could not be written due to any reason. - * @private - */ -function writeToFile(object, dest, target, forceOverwrite) { - return new Promise((resolve, reject) => { - fsPromisified.stat(dest) - .then((stats) => { - if (stats.isDirectory()) { - reject(new Error('Destination file is a directory, pls specify a valid file resource!')); - return; - } - // file exists - resolve(mkdirAndWrite(object, dest, target, forceOverwrite)); - }) - .catch(() => { - // ignore error (because file could possibly not exist at this point of time) - resolve(mkdirAndWrite(object, dest, target, forceOverwrite)); - }); - }); -} - -/** - * Writes a string serialized data object to a stream. - * - * @param {string} object - The data to write into stream. - * @param {string} dest - The stream destination. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. - * @see {@link TYPE_YAML} - * @see {@link TYPE_JSON} - * @see {@link TYPE_JS} - * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). - * @throws {Error} If serialized JS object could not be written due to any reason. - * @private - */ -function writeToStream(object, dest, target) { - return new Promise((resolve, reject) => { - dest - .on('error', reject) - .on('finish', () => resolve('Writing ' + target + ' to stream successful.')); - - // write stringified data - dest.write(object); - dest.end(); - }); -} - /** * Writes a JS object to a YAML destination. * @@ -231,18 +82,18 @@ async function writeJson(object, options) { } /** - * Writes a JS object to a JS destination. The object is prefixed by `module.exports[.${options.exports}] = `. + * Writes a JS object to a JS destination. * * @param {Object} object - The JSON to write into JS destination. - * @param {WriteOptions} options - The write destination and indention. + * @param {WriteOptions} options - The write options. * @see {@link MIN_INDENT} * @see {@link DEFAULT_INDENT} * @see {@link MAX_INDENT} - * @returns {Promise.} - Containing the write success message to handle by caller (e.g. for logging). + * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). * @private */ async function writeJs(object, options) { - const data = await serializeJsToString(object, options.indent, options.exports); + const data = await serializeJsToString(object, options); if (typeof options.dest === 'string') { // file return writeToFile(data, options.dest, TYPE_JS, options.force); } else if (isStream.writable(options.dest)) { // stream diff --git a/test/unit/test-cli.js b/test/functional/test-jyt-cli.js similarity index 67% rename from test/unit/test-cli.js rename to test/functional/test-jyt-cli.js index 06bed89..2078645 100644 --- a/test/unit/test-cli.js +++ b/test/functional/test-jyt-cli.js @@ -16,7 +16,6 @@ import { EXPECTED_VALUE, } from '../helper-constants'; - const fsPromised = promisify(fs); /** @@ -31,7 +30,7 @@ const fsPromised = promisify(fs); * @constant * @private */ -const CLI_TEST_BASE_DIR = './test/tmp/cli'; +const CLI_TEST_BASE_DIR = './test/functional/tmp/cli'; /** * A YAML source file path. @@ -59,18 +58,22 @@ const SRC_JS = TEST_DATA_DIR + '/test-data-cli.js'; /** * Object mapping from JS option to to short CLI option. - * @type {{src: string, dest: string, origin: string, target: string, indent: string, force: string, imports: string, exports: string}} + * @type {{src: string, dest: string, origin: string, target: string, indent: string, force: string, imports: string, + * exports: string}} * @private */ const CLI_OPTIONS_LONG_TO_SHORT_MAP = { src: '', dest: '', - origin: '-o ', - target: '-t ', - indent: '-i ', - force: '-f ', - imports: '-m ', - exports: '-x ' + origin: '-o', + target: '-t', + indent: '-i', + force: '-f', + imports: '-m', + exports: '-x', + strict: '-s', + 'no-es6': '--no-es6', + 'no-single': '--no-single', }; /** @@ -92,7 +95,7 @@ function createOptions(src, dest) { * Executes `./jyt` script with given args (which includes source, destination and all options). * * @param {string[]} args - The source, destination CLI arguments and all CLI options. - * @return {Promise} + * @returns {Promise} A result, see details. * @resolve {string} The transformation success message. * @rejects {Error} Any error occurred. * @private @@ -102,7 +105,7 @@ function execJyt(args) { console.log('CWD: ' + cwd()); const command = './jyt ' + args.join(' '); logger.info('executing command: ' + command); - const childProcess = exec(command, { cwd: cwd() /*, encoding: 'utf8' */ }, (err, stdout, stderr) => { + const childProcess = exec(command, { cwd: cwd() /* , encoding: 'utf8' */ }, (err, stdout, stderr) => { if (err) { logger.error(`err: ${err}`); reject(err); @@ -113,7 +116,8 @@ function execJyt(args) { resolve(stdout || stderr); }); childProcess.on('error', logger.error); - childProcess.on('exit', (code, signal) => logger.debug(`child process exited with code ${code} and signal ${signal}`)); + childProcess.on('exit', (code, signal) => + logger.debug(`child process exited with code ${code} and signal ${signal}`)); }); } @@ -121,12 +125,12 @@ function execJyt(args) { * Creates the CLI args/options from given `options` object. * * @param {TransformOptions} options - The transformation options. - * @return {string[]} The CLI args and options. + * @returns {string[]} The CLI args and options. * @private */ function optionsToArgs(options) { const keys = Object.keys(options); - return keys.map(key => CLI_OPTIONS_LONG_TO_SHORT_MAP[key] + options[key]); + return keys.map(key => `${CLI_OPTIONS_LONG_TO_SHORT_MAP[key]} ${options[key]}`); } /** @@ -135,14 +139,14 @@ function optionsToArgs(options) { * @param {TransformOptions} options - The transformation options. * @private */ -async function assertTransformSuccess(options) { +async function assertTransformSuccess(options, es6 = true) { expect.assertions(2); const msg = await execJyt(optionsToArgs(options)); logger.debug(msg); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); // eslint-disable-next-line import/no-dynamic-require, global-require - const json = require(path.resolve(options.dest)); + const json = es6 ? require(path.resolve(options.dest)).default : require(path.resolve(options.dest)); expect(json.foo).toBe(EXPECTED_VALUE); } @@ -171,17 +175,22 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - ./jyt -> ./src/cli.js - ', () => { describe('Testing CLI transforming from YAML to JS to relative path', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data.js'; + const DEST_NO_ES6 = CLI_TEST_BASE_DIR + '/test-data-no-es6.js'; - it('should store ' + DEST + ' file relative to ' + CLI_TEST_BASE_DIR + '/test-data.yaml', async () => { - expect.assertions(2); + beforeAll(async () => { // Prepare test data. try { fsExtra.copySync(SRC_YAML, CLI_TEST_BASE_DIR + '/test-data.yaml'); logger.debug('copied ' + SRC_YAML + ' to ' + CLI_TEST_BASE_DIR + '/test-data.yaml'); } catch (err) { - logger.error('could not copy ' + SRC_YAML + ' to ' + CLI_TEST_BASE_DIR + '/test-data.yaml: ' + err.stack); + logger.error('could not copy ' + SRC_YAML + ' to ' + CLI_TEST_BASE_DIR + '/test-data.yaml: ' + + err.stack); throw err; } + }); + + it('should store ' + DEST + ' file relative to ' + CLI_TEST_BASE_DIR + '/test-data.yaml', async () => { + expect.assertions(2); const msg = await execJyt(optionsToArgs({ src: path.resolve(CLI_TEST_BASE_DIR + '/test-data.yaml'), target: TYPE_JS, @@ -191,80 +200,97 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - ./jyt -> ./src/cli.js - ', () => { logger.debug('STATS: ' + JSON.stringify(stats, null, 4)); expect(stats.isFile()).toBe(true); // eslint-disable-next-line import/no-unresolved, global-require, import/no-dynamic-require - const json = require('../tmp/cli/test-data.js'); - expect(json.foo).toBe(EXPECTED_VALUE); + const result = require('./tmp/cli/test-data.js').default; + expect(result.foo).toBe(EXPECTED_VALUE); + }); + + it('should store ' + CLI_TEST_BASE_DIR + '/test-data-no-es6.js file relative to ' + + CLI_TEST_BASE_DIR + '/test-data.yaml (non-ES6 syntax)', async () => { + expect.assertions(2); + const msg = await execJyt(optionsToArgs({ + src: path.resolve(CLI_TEST_BASE_DIR + '/test-data.yaml'), + dest: path.resolve(DEST_NO_ES6), + target: TYPE_JS, + 'no-es6': true, + })); + logger.debug(msg); + const stats = fs.statSync(DEST_NO_ES6); + expect(stats.isFile()).toBe(true); + // eslint-disable-next-line import/no-unresolved, global-require, import/no-dynamic-require + const result = require('./tmp/cli/test-data-no-es6.js'); + expect(result.foo).toBe(EXPECTED_VALUE); }); }); describe('Testing CLI transforming from YAML to JS', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data-transform-yaml-js.js'; - it('should store ' + SRC_YAML + ' file to ' + DEST, async () => - await assertTransformSuccess(createOptions(SRC_YAML, DEST)) + it('should store ' + SRC_YAML + ' file to ' + DEST, () => + assertTransformSuccess(createOptions(SRC_YAML, DEST)) ); }); describe('Testing CLI transforming from YAML to JSON', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data-transform-yaml-json.json'; - it('should store ' + SRC_YAML + ' file to ' + DEST, async () => - await assertTransformSuccess(createOptions(SRC_YAML, DEST)) + it('should store ' + SRC_YAML + ' file to ' + DEST, () => + assertTransformSuccess(createOptions(SRC_YAML, DEST), false) ); }); describe('Testing CLI transforming from JSON to JS', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data-transform-json-js.js'; - it('should store ' + SRC_JSON + ' file to ' + DEST, async () => - await assertTransformSuccess(createOptions(SRC_JSON, DEST)) + it('should store ' + SRC_JSON + ' file to ' + DEST, () => + assertTransformSuccess(createOptions(SRC_JSON, DEST)) ); }); describe('Testing CLI transforming from JS to JSON', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data-transform-js-json.json'; - it('should store ' + SRC_JS + ' file to ' + DEST, async () => - await assertTransformSuccess(createOptions(SRC_JS, DEST)) + it('should store ' + SRC_JS + ' file to ' + DEST, () => + assertTransformSuccess(createOptions(SRC_JS, DEST), false) ); }); describe('Testing CLI transforming from JS to YAML', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data-transform-js-yaml.yaml'; - it('should store ' + SRC_JS + ' file to ' + DEST, async () => - await assertYamlTransformSuccess(createOptions(SRC_JS, DEST)) + it('should store ' + SRC_JS + ' file to ' + DEST, () => + assertYamlTransformSuccess(createOptions(SRC_JS, DEST)) ); }); describe('Testing CLI transforming from YAML to YAML', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data-transform-yaml-yaml.yaml'; - it('should store ' + SRC_YAML + ' file to ' + DEST, async () => - await assertYamlTransformSuccess(createOptions(SRC_YAML, DEST)) + it('should store ' + SRC_YAML + ' file to ' + DEST, () => + assertYamlTransformSuccess(createOptions(SRC_YAML, DEST)) ); }); describe('Testing CLI transforming from JSON to JSON', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data-transform-json-json.json'; - it('should store ' + SRC_JS + ' file to ' + DEST, async () => - await assertTransformSuccess(createOptions(SRC_JS, DEST)) + it('should store ' + SRC_JS + ' file to ' + DEST, () => + assertTransformSuccess(createOptions(SRC_JS, DEST), false) ); }); describe('Testing CLI transforming from JSON to YAML', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data-transform-json-yaml.yaml'; - it('should store ' + SRC_JSON + ' file to ' + DEST, async () => - await assertYamlTransformSuccess(createOptions(SRC_JSON, DEST)) + it('should store ' + SRC_JSON + ' file to ' + DEST, () => + assertYamlTransformSuccess(createOptions(SRC_JSON, DEST)) ); }); describe('Testing CLI transforming from JS to JS', () => { const DEST = CLI_TEST_BASE_DIR + '/test-data-transform-js-js.js'; - it('should store ' + SRC_JS + ' file to ' + DEST, async () => - await assertTransformSuccess(createOptions(SRC_JS, DEST)) + it('should store ' + SRC_JS + ' file to ' + DEST, () => + assertTransformSuccess(createOptions(SRC_JS, DEST)) ); }); }); diff --git a/test/unit/test-reader.js b/test/functional/test-reader.js similarity index 100% rename from test/unit/test-reader.js rename to test/functional/test-reader.js diff --git a/test/unit/test-transformer.js b/test/functional/test-transformer.js similarity index 79% rename from test/unit/test-transformer.js rename to test/functional/test-transformer.js index d92067e..f77647d 100644 --- a/test/unit/test-transformer.js +++ b/test/functional/test-transformer.js @@ -15,7 +15,11 @@ import { EXPECTED_VALUE, } from '../helper-constants'; -const fsPromised = promisify(fs); +/** + * Promisified `fs` module. + * @private + */ +const fsPromisified = promisify(fs); /** * @module jy-transform:unit-test:test-transformer @@ -53,8 +57,7 @@ const SRC_JS = TEST_DATA_DIR + '/test-file.js'; * @constant * @private */ -const TRANSFORMER_TEST_BASE_DIR = './test/tmp/transformer'; - +const TRANSFORMER_TEST_BASE_DIR = './test/functional/tmp/transformer'; /** * Transformation middleware changing value for `foo` property. @@ -71,14 +74,14 @@ async function transformFunc(object) { * * @param {Object} options - The transformation options. */ -async function assertTransformSuccess(options) { +async function assertTransformSuccess(options, es6 = true) { expect.assertions(2); const msg = await transform(options); logger.debug(msg); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); // eslint-disable-next-line import/no-dynamic-require, global-require - const json = require(path.resolve(options.dest)); + const json = es6 ? require(path.resolve(options.dest)).default : require(path.resolve(options.dest)); expect(json.foo).toBe(EXPECTED_VALUE); } @@ -94,7 +97,7 @@ async function assertYamlTransformSuccess(options) { expect(msg).toEqual(expect.any(String)); const stats = fsExtra.statSync(options.dest); expect(stats.isFile()).toBeTruthy(); - const yaml = await fsPromised.readFile(options.dest, UTF8); + const yaml = await fsPromisified.readFile(options.dest, UTF8); const object = jsYaml.safeLoad(yaml); expect(object.foo).toBe(EXPECTED_VALUE); } @@ -125,7 +128,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { describe('Testing transform with middleware', () => { it('should throw ValidationError if middleware passed is not a function type', async () => { expect.assertions(1); - await expect(transform(createOptions({}, 'not a function', {}))) + await expect(transform(createOptions({}, {}, 'not a function'))) .rejects.toMatchObject({ name: 'ValidationError', isJoi: true }); }); @@ -160,9 +163,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { describe('Testing Transformer transforming from YAML to JS to relative path', () => { const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data.js'; + const DEST_NO_ES6 = TRANSFORMER_TEST_BASE_DIR + '/test-data-no-es6.js'; + const DEST_DQ_AND_STRICT = TRANSFORMER_TEST_BASE_DIR + '/test-data-double-quotes-and-strict.js'; - it('should store ' + DEST + ' file relative to ' + TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml', async () => { - expect.assertions(2); + beforeAll(async () => { // Prepare test data. try { fsExtra.copySync(SRC_YAML, TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml'); @@ -172,6 +176,10 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { err.stack); throw err; } + }); + + it('should store ' + DEST + ' file relative to ' + TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml', async () => { + expect.assertions(2); const msg = await transform({ src: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml'), transform: transformFunc, @@ -181,7 +189,43 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { const stats = fs.statSync(DEST); expect(stats.isFile()).toBe(true); // eslint-disable-next-line import/no-unresolved, global-require, import/no-dynamic-require - const json = require('../tmp/transformer/test-data.js'); + const json = require('./tmp/transformer/test-data.js').default; + expect(json.foo).toBe(EXPECTED_VALUE); + }); + + it('should store ' + DEST + ' file with double-quotes and strict', async () => { + expect.assertions(2); + const msg = await transform({ + src: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml'), + dest: path.resolve(DEST_DQ_AND_STRICT), + transform: transformFunc, + target: TYPE_JS, + strict: true, + 'no-single': true, + }); + logger.debug(msg); + const stats = fs.statSync(DEST_DQ_AND_STRICT); + expect(stats.isFile()).toBe(true); + // eslint-disable-next-line import/no-unresolved, global-require, import/no-dynamic-require + const jsContentString = await fsPromisified.readFile(DEST_DQ_AND_STRICT, UTF8); + expect(jsContentString.startsWith('"use strict;"')).toBe(true); + }); + + it('should store ' + TRANSFORMER_TEST_BASE_DIR + '/test-data-no-es6.js file relative to ' + + TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml (non-ES6 syntax)', async () => { + expect.assertions(2); + const msg = await transform({ + src: path.resolve(TRANSFORMER_TEST_BASE_DIR + '/test-data.yaml'), + dest: path.resolve(DEST_NO_ES6), + transform: transformFunc, + target: TYPE_JS, + 'no-es6': true, + }); + logger.debug(msg); + const stats = fs.statSync(DEST_NO_ES6); + expect(stats.isFile()).toBe(true); + // eslint-disable-next-line import/no-unresolved, global-require, import/no-dynamic-require + const json = require('./tmp/transformer/test-data-no-es6.js'); expect(json.foo).toBe(EXPECTED_VALUE); }); }); @@ -210,7 +254,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { transform: transformFunc, dest: path.resolve(DEST), }; - await assertTransformSuccess(options); + await assertTransformSuccess(options, false); }); }); @@ -238,7 +282,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { transform: transformFunc, dest: path.resolve(DEST), }; - await assertTransformSuccess(options); + await assertTransformSuccess(options, false); }); }); @@ -281,7 +325,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { transform: transformFunc, dest: path.resolve(DEST), }; - await assertTransformSuccess(options); + await assertTransformSuccess(options, false); }); }); diff --git a/test/unit/test-writer.js b/test/functional/test-writer.js similarity index 98% rename from test/unit/test-writer.js rename to test/functional/test-writer.js index 5337843..12942a8 100644 --- a/test/unit/test-writer.js +++ b/test/functional/test-writer.js @@ -36,7 +36,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { * @constant * @private */ - const WRITER_TEST_BASE_DIR = './test/tmp/writer'; + const WRITER_TEST_BASE_DIR = './test/functional/tmp/writer'; beforeAll(() => { fsExtra.ensureDirSync(WRITER_TEST_BASE_DIR); @@ -143,7 +143,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { expect(msg).toBeDefined(); await expectDestFileExists(file); // eslint-disable-next-line import/no-unresolved, global-require - const object = require('../tmp/writer/test-data-by-js-stream-with-exports-identifier.js').test; + const object = require('./tmp/writer/test-data-by-js-stream-with-exports-identifier.js').test; expect(object.test).toBeDefined(); expect(object.test).toBe('value'); }); diff --git a/test/unit/test-serialize-js-utils.js b/test/unit/test-serialize-js-utils.js new file mode 100644 index 0000000..b33cae3 --- /dev/null +++ b/test/unit/test-serialize-js-utils.js @@ -0,0 +1,3 @@ +/** + * Created by jens.krefeldt on 24.08.17. + */ diff --git a/test/unit/validation/test-joi-extensions-file-helper.js b/test/unit/validation/test-joi-extensions-file-utils.js similarity index 86% rename from test/unit/validation/test-joi-extensions-file-helper.js rename to test/unit/validation/test-joi-extensions-file-utils.js index 11b8e63..ac4a1dc 100644 --- a/test/unit/validation/test-joi-extensions-file-helper.js +++ b/test/unit/validation/test-joi-extensions-file-utils.js @@ -1,8 +1,8 @@ import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; -import { isExistingFile } from '../../../src/validation/joi-extensions-file-helper'; +import { isExistingFile } from '../../../src/validation/joi-extensions-file-utils'; /** - * @module jy-transform:test-unit:test-joi-extension-file-helper + * @module jy-transform:test-unit:test-joi-extension-file-utils * @description This unit test module tests validation FS helper method. * @private */ @@ -10,11 +10,11 @@ import { isExistingFile } from '../../../src/validation/joi-extensions-file-help describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-file-helper - ', () => { describe('Method isExistingFile(pathStr) ', () => { it('should return true on relative path string with existing file', () => - expect(isExistingFile('test/unit/validation/test-joi-extensions-file-helper.js')).toBe(true) + expect(isExistingFile('test/unit/validation/test-joi-extensions-file-utils.js')).toBe(true) ); it('should return true on relative path string with existing file starting with \'./\'', () => - expect(isExistingFile('./test/unit/validation/test-joi-extensions-file-helper.js')).toBe(true) + expect(isExistingFile('./test/unit/validation/test-joi-extensions-file-utils.js')).toBe(true) ); it('should return false on incorrect path string with non-existing file', () => diff --git a/test/unit/validation/test-joi-extensions-identifier-helper.js b/test/unit/validation/test-joi-extensions-identifier-utils.js similarity index 96% rename from test/unit/validation/test-joi-extensions-identifier-helper.js rename to test/unit/validation/test-joi-extensions-identifier-utils.js index c9b76d2..6d1d483 100644 --- a/test/unit/validation/test-joi-extensions-identifier-helper.js +++ b/test/unit/validation/test-joi-extensions-identifier-utils.js @@ -1,8 +1,8 @@ -import { isValidEs6Identifier } from '../../../src/validation/joi-extensions-identifier-helper'; +import { isValidEs6Identifier } from '../../../src/validation/joi-extensions-identifier-utils'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; /** - * @module jy-transform:unit-test:test-joi-extensions-identifier-helper + * @module jy-transform:unit-test:test-joi-extensions-identifier-utils * @description This unit test suite checks validity and correctness of ES6 identifiers. * @private */ diff --git a/test/unit/validation/test-options-schema-helper.js b/test/unit/validation/test-options-schema-utils.js similarity index 96% rename from test/unit/validation/test-options-schema-helper.js rename to test/unit/validation/test-options-schema-utils.js index 0e02995..13dd0c2 100644 --- a/test/unit/validation/test-options-schema-helper.js +++ b/test/unit/validation/test-options-schema-utils.js @@ -4,7 +4,7 @@ import { inferOriginDefault, inferTargetDefault, inferDestDefaultFromSrc, -} from '../../../src/validation/options-schema-helper'; +} from '../../../src/validation/options-schema-utils'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; import { TYPE_YAML, @@ -14,7 +14,7 @@ import { } from '../../../src/constants'; /** - * @module jy-transform:unit-test:test-options-schema-helper + * @module jy-transform:unit-test:test-options-schema-utils * @description This unit test suite checks the validity and correctness of options schema helper methods. * @private */ @@ -22,7 +22,7 @@ import { describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { describe('Function inferOriginDefault', () => { it('should infer the correct origin from relative path string with existing file having a known file extension', - () => expect(inferOriginDefault({ src: 'test/unit/validation/test-joi-extensions-file-helper.js' })).toBe(TYPE_JS) + () => expect(inferOriginDefault({ src: 'test/unit/validation/test-joi-extensions-file-utils.js' })).toBe(TYPE_JS) ); it('should infer the default origin from relative path string with existing file having an unsupported file ' + @@ -35,7 +35,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { it('should infer the correct origin from read stream of existing file having a known file ending', () => expect(inferOriginDefault({ - src: fs.createReadStream('test/unit/validation/test-joi-extensions-file-helper.js'), + src: fs.createReadStream('test/unit/validation/test-joi-extensions-file-utils.js'), })).toBe(TYPE_JS) ); diff --git a/test/unit/validation/test-options-schema.js b/test/unit/validation/test-options-schema.js index 64cef28..8462458 100644 --- a/test/unit/validation/test-options-schema.js +++ b/test/unit/validation/test-options-schema.js @@ -14,6 +14,9 @@ import { DEFAULT_INDENT, MIN_INDENT, MAX_INDENT, + DEFAULT_STRICT, + DEFAULT_NO_ES6, + DEFAULT_NO_SINGLE_QUOTES, } from '../../../src/constants'; import { readOptionsSchema, @@ -229,7 +232,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { ); it('should set all defaults', async () => { - expect.assertions(4); + expect.assertions(7); const options = { dest: './test/tmp/test-data.js', }; @@ -238,6 +241,9 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { expect(validatedOptions.indent).toBe(DEFAULT_INDENT); expect(validatedOptions.exports).toBe(DEFAULT_JS_EXPORTS_IDENTIFIER); expect(validatedOptions.force).toBe(DEFAULT_FORCE_FILE_OVERWRITE); + expect(validatedOptions.strict).toBe(DEFAULT_STRICT); + expect(validatedOptions['no-es6']).toBe(DEFAULT_NO_ES6); + expect(validatedOptions['no-single']).toBe(DEFAULT_NO_SINGLE_QUOTES); }); it('should infer options.target from file type', async () => { From 5e00e59c65dd252e8d2ffb70cd5a330b58215e68 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 24 Aug 2017 13:52:29 +0200 Subject: [PATCH 26/58] Deps updated --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index be45906..24d67b0 100644 --- a/package.json +++ b/package.json @@ -36,27 +36,27 @@ "cli": "~1.0.1", "is-stream": "~1.1.0", "joi": "~10.6.0", - "js-yaml": "~3.6.1", + "js-yaml": " ~3.9.1", "json-stringify-safe": "~5.0.1", "mkdirp-then": "~1.2.0", "promisify-es6": "~1.0.2", "serialize-js": "deadratfink/serialize-js#feature/single_quote_only" }, "devDependencies": { - "babel-cli": "~6.24.1", + "babel-cli": "~6.26.0", "babel-eslint": " ~7.2.3", "babel-plugin-transform-flow-strip-types": "~6.22.0", "babel-plugin-transform-runtime": "~6.23.0", "babel-preset-env": "~1.6.0", - "chalk": "~2.0.1", + "chalk": "~2.1.0", "codacy-coverage": "~2.0.2", "codeclimate-test-reporter": "~0.5.0", "codecov": " ~2.3.0", "coveralls": " ~2.13.1", "cwd": "~0.10.0", "doctoc": " ~1.3.0", - "eslint": "~4.2.0", - "eslint-config-airbnb-base": "~11.2.0", + "eslint": "~4.5.0", + "eslint-config-airbnb-base": "~11.3.2", "eslint-plugin-filenames": "1.2.0", "eslint-plugin-import": " ~2.7.0", "eslint-plugin-jest": " ~20.0.3", @@ -68,7 +68,7 @@ "jsdoc-babel": " ~0.3.0", "jsdoc-parse": "~3.0.0", "jsdoc-to-markdown": "~3.0.0", - "nsp": "~2.6.3", + "nsp": "~2.7.0", "package-json-to-readme": "~2.0.0", "winston": "~2.3.0" }, From e781fb5d683e1706770f8eea74a088f290163740 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 24 Aug 2017 13:52:55 +0200 Subject: [PATCH 27/58] Deps updated --- PACKAGE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PACKAGE.md b/PACKAGE.md index 9002849..d95726f 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -34,7 +34,7 @@ npm test - [babel-plugin-transform-flow-strip-types](https://github.com/babel/babel/tree/master/packages): Strip flow type annotations from your output code. - [babel-plugin-transform-runtime](https://github.com/babel/babel/tree/master/packages): Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals - [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. -- [chalk](https://github.com/chalk/chalk): Terminal string styling done right. Much color +- [chalk](https://github.com/chalk/chalk): Terminal string styling done right - [codacy-coverage](https://github.com/codacy/node-codacy-coverage): Code Coverage reporter for Codacy.com - [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects - [codecov](https://github.com/codecov/codecov-node): Uploading report to Codecov: https://codecov.io From e9692b63e9d6cbcbbb78665cd104a63eea3b0369 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 1 Sep 2017 09:15:29 +0200 Subject: [PATCH 28/58] More tests and improvements --- .eslintignore | 1 + .jestrc.js | 3 +- .jsdoc-public.json | 3 +- API-PRIVATE.md | 32 ++- API-PUBLIC.md | 197 ++++++++++++++++-- CHANGELOG.md | 2 +- README.md | 75 ++++--- bin/create-readme.sh | 20 +- index.js | 15 +- readme/DOCUMENTATION.md | 67 +++--- src/jy-transform.js | 171 +++++++++++++++ src/reader.js | 26 +-- src/serialize-utils.js | 3 +- src/transformer.js | 29 +-- src/writer.js | 39 ---- test/functional/test-jyt-cli.js | 14 +- test/functional/test-reader.js | 4 +- test/functional/test-transformer.js | 42 ++-- test/functional/test-writer.js | 4 +- test/unit/test-index.js | 14 +- test/unit/test-jy-transform.js | 48 +++++ test/unit/test-serialize-js-utils.js | 3 - test/unit/test-serialize-utils.js | 101 +++++++++ .../test-joi-extensions-file-utils.js | 2 +- .../test-joi-extensions-identifier-utils.js | 2 +- .../validation/test-options-schema-utils.js | 4 +- 26 files changed, 651 insertions(+), 270 deletions(-) create mode 100755 src/jy-transform.js create mode 100644 test/unit/test-jy-transform.js delete mode 100644 test/unit/test-serialize-js-utils.js create mode 100644 test/unit/test-serialize-utils.js diff --git a/.eslintignore b/.eslintignore index 70b454f..7d53bb8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,6 +8,7 @@ lib/ index.js test/data test/tmp +test/functional/tmp # TODO reve later when test os done test/unit/test-cli.js diff --git a/.jestrc.js b/.jestrc.js index 310dfeb..8bdb047 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -33,7 +33,8 @@ module.exports = { '**/test/functional/**/*.js', //'**/test/unit/validation/test-options-schema-helper.js', - // '**/test/test-log-wrapper.js', + + //'**/test/unit/test-serialize-utils.js', // '**/test/unit/test-middleware.js', // '**/test/test-index.js', //'/*.js!**/test/functional/util/**', diff --git a/.jsdoc-public.json b/.jsdoc-public.json index 86923ee..1e14c9e 100644 --- a/.jsdoc-public.json +++ b/.jsdoc-public.json @@ -1,7 +1,8 @@ { "source": { "include": [ - "./" + "./src/jy-transform.js", + "./src/type-definitions.js" ], "exclude": [ "test", diff --git a/API-PRIVATE.md b/API-PRIVATE.md index 871cc9c..1345812 100644 --- a/API-PRIVATE.md +++ b/API-PRIVATE.md @@ -27,6 +27,7 @@ - [jy-transform:unit:helper-constants : Object ℗](#jy-transformunithelper-constants--codeobjectcode-%E2%84%97) - [jy-transform:unit:logger : Object ℗](#jy-transformunitlogger--codeobjectcode-%E2%84%97) - [jy-transform:test-unit:index ℗](#jy-transformtest-unitindex-%E2%84%97) +- [jy-transform:test-unit:serialize-utils ℗](#jy-transformtest-unitserialize-utils-%E2%84%97) - [jy-transform:test-unit:test-joi-extension-file-utils ℗](#jy-transformtest-unittest-joi-extension-file-utils-%E2%84%97) - [jy-transform:unit-test:test-joi-extensions-identifier-utils ℗](#jy-transformunit-testtest-joi-extensions-identifier-utils-%E2%84%97) - [jy-transform:unit-test:test-options-schema-utils ℗](#jy-transformunit-testtest-options-schema-utils-%E2%84%97) @@ -119,6 +120,9 @@ memory to a JSON/JS/YAML destination (file, Object or jy-transform:test-unit:index

      This unit test module tests the correct exporting from ./index.js.

      +
      jy-transform:test-unit:serialize-utils
      +

      This unit test suite checks the validity and correctness of JS serialization utility methods.

      +
      jy-transform:test-unit:test-joi-extension-file-utils

      This unit test module tests validation FS helper method.

      @@ -563,7 +567,7 @@ This module provides the _public_ interface for the _read_ functionality of YAML * [jy-transform:reader](#module_jy-transform_reader) ℗ * [~readJsOrJson](#module_jy-transform_reader..readJsOrJson) ⇒ Promise.<Object> ℗ * [~readYaml](#module_jy-transform_reader..readYaml) ⇒ Promise.<Object> ℗ - * [~read](#module_jy-transform_reader..read) ⇒ Promise + * [~read](#module_jy-transform_reader..read) ⇒ Promise @@ -595,12 +599,12 @@ exception on those. -### jy-transform:reader~read ⇒ Promise +### jy-transform:reader~read ⇒ Promise ℗ Reads a particular content type from a source provided in the passed `options`. **Kind**: inner property of [jy-transform:reader](#module_jy-transform_reader) **Returns**: Promise - The result. -**Access**: public +**Access**: private **Resolve**: string Resolves with JS object result. **Reject**: ValidationError If any `options` validation occurs. **Reject**: Error If any write error occurs. @@ -1220,7 +1224,7 @@ Executes `./jyt` script with given args (which includes source, destination and **Returns**: Promise - A result, see details. **Access**: private **Resolve**: string The transformation success message. -**Rejects**: Error Any error occurred. +**Reject**: Error Any error occurred. | Param | Type | Description | | --- | --- | --- | @@ -1419,6 +1423,12 @@ This function formats the log string by given options to log. ## jy-transform:test-unit:index ℗ This unit test module tests the correct exporting from _./index.js_. +**Access**: private + + +## jy-transform:test-unit:serialize-utils ℗ +This unit test suite checks the validity and correctness of JS serialization utility methods. + **Access**: private @@ -1547,9 +1557,10 @@ Helper method which asserts the successful transformation. **Kind**: global variable **Access**: private -| Param | Type | Description | -| --- | --- | --- | -| options | [TransformOptions](#TransformOptions) | The transformation options. | +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| options | [TransformOptions](#TransformOptions) | | The transformation options. | +| [es6] | boolean | true | Whether to use ES6 syntax. | @@ -1580,9 +1591,10 @@ Helper method which asserts the successful transformation. **Kind**: global variable -| Param | Type | Description | -| --- | --- | --- | -| options | Object | The transformation options. | +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| options | Object | | The transformation options. | +| [es6] | boolean | true | Whether to use ES6 syntax. | diff --git a/API-PUBLIC.md b/API-PUBLIC.md index 2819ca3..721eb98 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -4,7 +4,7 @@ - [Modules](#modules) - [Typedefs](#typedefs) -- [jy-transform:constants](#jy-transformconstants) +- [jy-transform](#jy-transform) - [ReadOptions : object](#readoptions--codeobjectcode) - [WriteOptions : object](#writeoptions--codeobjectcode) - [TransformOptions : object](#transformoptions--codeobjectcode) @@ -14,8 +14,8 @@ ## Modules
      -
      jy-transform:constants
      -

      Useful constants used for the module and its usage.

      +
      jy-transform
      +

      This module provides the public interface for the read, write and transform functionality.

      @@ -33,38 +33,193 @@ - + -## jy-transform:constants -Useful constants used for the module and its usage. +## jy-transform +This module provides the _public_ interface for the _read_, _write_ and _transform_ functionality. **Access**: public +**Author**: Jens Krefeldt -* [jy-transform:constants](#module_jy-transform_constants) - * [~TYPE_YAML](#module_jy-transform_constants..TYPE_YAML) : string - * [~TYPE_JSON](#module_jy-transform_constants..TYPE_JSON) : string - * [~TYPE_JS](#module_jy-transform_constants..TYPE_JS) : string +* [jy-transform](#module_jy-transform) + * [~transform](#module_jy-transform..transform) ⇒ Promise + * [~read](#module_jy-transform..read) ⇒ Promise + * [~write](#module_jy-transform..write) ⇒ Promise + * [~TYPE_YAML](#module_jy-transform..TYPE_YAML) : string + * [~TYPE_JS](#module_jy-transform..TYPE_JS) : string + * [~TYPE_JSON](#module_jy-transform..TYPE_JSON) : string - + -### jy-transform:constants~TYPE_YAML : string -The `'yaml'` type constant. +### jy-transform~transform ⇒ Promise +The entry method for all transformations accepting a configuration object and +an (optional) middleware function. It executes the transformation logic. + +1. Input (read) +2. Transform [ + Middleware] +3. Output (write). -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Kind**: inner constant of [jy-transform](#module_jy-transform) +**Returns**: Promise - The transformation result. **Access**: public - +**Resolve**: string With the transformation result as message (e.g. to be logged by caller). +**Reject**: TypeError Will throw this error when the passed `middleware` is not type of `Function`. +**Reject**: ValidationError If any `options` validation occurs. +**Reject**: Error Will throw any error if read, transform or write operation failed due to any reason. -### jy-transform:constants~TYPE_JSON : string -The `'json'` type constant. +| Param | Type | Description | +| --- | --- | --- | +| options | [TransformOptions](#TransformOptions) | The configuration for a transformation. | + +**Example** +```js +import { transform } from 'jy-transform'; +const options = { + src: 'foo/bar.yaml', // From YAML file... + transform: async (object) => { // ...with exchanging value... + object.foo = 'new value'; + return object; + }, + target: 'foo/bar.json', // ...to a new JSON file. + indent: 4, +}; + +// ---- Promise style: + +transform(options) + .then(console.log) + .catch(console.error); + + +// ---- async/await style: + +try { + const msg = await transform(options); + console.log(msg); +} catch (err) { + console.error(err.stack); +}; +``` + + +### jy-transform~read ⇒ Promise +Reads a particular content type from a source provided in the passed `options`. + +**Kind**: inner constant of [jy-transform](#module_jy-transform) +**Returns**: Promise - The result. +**Access**: public +**Resolve**: string Resolves with JS object result. +**Reject**: ValidationError If any `options` validation occurs. +**Reject**: Error If any write error occurs. + +| Param | Type | Description | +| --- | --- | --- | +| options | [ReadOptions](#ReadOptions) | The read options. | + +**Example** +```js +import { read } from 'jy-transform'; + + +// --- from file path + +options = { + src: 'foo.yml' +}; + +read(options) + .then(obj => console.log(JSON.stringify(obj))) + .catch(console.error); + + +// --- from Readable + +options = { + src: fs.createReadStream('foo.yml') +}; + +read(options) + .then(obj => console.log(JSON.stringify(obj))) + .catch(console.error); +``` + + +### jy-transform~write ⇒ Promise +Writes the passed JS object to a particular destination described by the passed `options`. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Kind**: inner constant of [jy-transform](#module_jy-transform) +**Returns**: Promise - The result. **Access**: public - +**Resolve**: string With the write success message. +**Reject**: Error If any write error occurs. +**Reject**: ValidationError If any `options` validation occurs. -### jy-transform:constants~TYPE_JS : string +| Param | Type | Description | +| --- | --- | --- | +| object | Object | The JS source object to write. | +| options | [WriteOptions](#WriteOptions) | The write options. | + +**Example** +```js +import { write } from 'jy-transform'; + + +// ---- write obj to file --- + +const obj = {...}; +const options = { + dest: 'result.js', + indent: 4 +} + +write(obj, options) + .then(console.log) + .catch(console.error); + + +// ---- write obj to Writable --- + +options = { + dest: fs.createWriteStream('result.json'), + indent: 4 +} + +write(obj, options) + .then(console.log) + .catch(console.error); + + +// ---- write obj to object --- + +options = { + dest: {}, + indent: 4 +} + +write(obj, options) + .then(console.log) + .catch(console.error); +``` + + +### jy-transform~TYPE_YAML : string +The `'yaml'` type constant. + +**Kind**: inner constant of [jy-transform](#module_jy-transform) +**Access**: public + + +### jy-transform~TYPE_JS : string The `'js'` type constant. -**Kind**: inner constant of [jy-transform:constants](#module_jy-transform_constants) +**Kind**: inner constant of [jy-transform](#module_jy-transform) +**Access**: public + + +### jy-transform~TYPE_JSON : string +The `'json'` type constant. + +**Kind**: inner constant of [jy-transform](#module_jy-transform) **Access**: public diff --git a/CHANGELOG.md b/CHANGELOG.md index 95538f4..cd47d25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ new interface: - This is the default now: - Usage of `export default` instead of `module.exports`. - Usage of `export const foo` instead of `module.exports.foo`. - - Can be suppressed by `options[no-es6]` (default `false`). + - Can be suppressed by `options[no-es6] = true` (default `false`). - [[#62](https://github.com/deadratfink/jy-transform/issues/62)] The `options.transform` function (formerly aka _middleware_ function) is no longer necessary to be a Promise/`async` one. - [[#59](https://github.com/deadratfink/jy-transform/issues/59)] Support single-quotes options for JS output. diff --git a/README.md b/README.md index 3ce54e7..707d5d0 100644 --- a/README.md +++ b/README.md @@ -105,9 +105,9 @@ npm install jy-transform --global ## TOC - [API in a Minute](#api-in-a-minute) - - [Read, Transform & Write from Source to Destination](#read-transform--write-from-source-to-destination) - - [Read into JS object from particular Source (File, Stream or JS Object) only](#read-into-js-object-from-particular-source-file-stream-or-js-object-only) - - [Write JS object to particular Destination only](#write-js-object-to-particular-destination-only) + - [Transform from Source to Destination](#transform-from-source-to-destination) + - [Read into JS object from particular Source (File, Stream or JS Object)](#read-into-js-object-from-particular-source-file-stream-or-js-object) + - [Write JS object to particular Destination](#write-js-object-to-particular-destination) - [Why This Module?](#why-this-module) - [Usage](#usage) - [Usage Types](#usage-types) @@ -125,7 +125,7 @@ npm install jy-transform --global ## API in a Minute -### Read, Transform & Write from Source to Destination +### Transform from Source to Destination ```javascript import { transform } from 'jy-transform'; @@ -153,11 +153,11 @@ try { const msg = await transform(options); // Transform, of course, inside an async. console.log(msg); // Success message! } catch (err) { // Oops! - console.error(err.stack); + console.error(err); } ``` -### Read into JS object from particular Source (File, Stream or JS Object) only +### Read into JS object from particular Source (File, Stream or JS Object) ```javascript import { read } from 'jy-transform'; @@ -176,11 +176,11 @@ try { const object = await read(options); console.log(JSON.stringify(object)); // Print read object. } catch (err) { - console.error(err.stack); + console.error(err); } ``` -### Write JS object to particular Destination only +### Write JS object to particular Destination ```javascript import { write } from 'jy-transform'; @@ -199,7 +199,7 @@ try { const msg = await write(object, options); console.log(msg); // Print write success message. } catch (err) { - console.error(err.stack); + console.error(err); } ``` @@ -231,26 +231,28 @@ So, what are the typical use cases for this module? In terms of _transformation_ these consists of different phases: 1. Reading from source - 2. Transforming JSON objects (`Transformer`) or apply dedicated actions on the intermediate JSON objects + 2. Transforming JSON objects or apply dedicated actions on the intermediate JSON objects 3. Writing to a destination -#### Reading From +#### Read Case -- _*.yaml_ file -- _*.js_ file -- _*.json_ file +Reading from a file: -Additionally, on API level from a: +- _*.yaml_ +- _*.js_ +- _*.json_ -- `stream.Readable` - - Contain serialized JS, JSON or YAML - - If not file stream it requires `options.origin` property set +Additionally, on API level from: + +- `stream.Readable`: + - Contains serialized JS, JSON or YAML + - If not a file stream then setting requires `options.origin` property is mandatory - Reads as UTF-8 -- JS `object` +- JS `object`: - Actually, this means the reading phase is "skipped", because object is in-memory already - Of course, this case _cannot_ not be applied to serialized JSON or YAML content -#### Transformation +#### Transformation Case The _transformation_ is usually a format change, but can also be refer to content changes on the intermediate JS object, the latter with the help of a configured `transform` callback function. @@ -275,21 +277,23 @@ while: As mentioned above the configured `transform` callback can apply particular actions on the intermediate JS object, but this is an optional part for [transformation](#transformation) phase. -#### Writing To +#### Write Case + +Writing to a file: -- _*.yaml_ file -- _*.js_ file -- _*.json_ file +- _*.yaml_ +- _*.js_ +- _*.json_ -Additionally, on API level to a: +Additionally, on API level to: -- `stream.Writable` +- `stream.Writable`: - Serialized JS, JSON and YAML - Requires `options.target` property set - Writes UTF-8 -- JS `object - - JS as simple reference - - YAML and JSON as serialized string +- JS `object`: + - JS as a simple reference + - YAML and JSON as a serialized string ### Limitations @@ -324,9 +328,6 @@ Additionally, on API level to a: and JS only, but at the moment we require that each document to transform is a _single_ one per source (or in case of JS could be identified)! This feature is planned and reflected in [#14](https://github.com/deadratfink/jy-transform/issues/14). -- Schema validation for input and output is another topic which is planned by - [#1](https://github.com/deadratfink/jy-transform/issues/1) and - [#2](https://github.com/deadratfink/jy-transform/issues/2). ### CLI Usage @@ -400,7 +401,7 @@ Then we can transform it to a JSON content as _foo.json_ file: ```json { - "foo": "bar" + "foo": "bar" } ``` @@ -432,13 +433,11 @@ Accordingly, this is also true for the `target` option. The command ```text -$ jyt foo.json -i 4 +$ jyt foo.json ``` results in _foo.js_: ```javascript -module.exports = { - foo: "bar" -} +export default {foo: 'bar'} ``` #### Example: JS ⇒ YAML @@ -790,7 +789,5 @@ section for more details about conventions. - [Public Api Reference](./API-PUBLIC.md) -- [Private Api Reference](./API-PRIVATE.md) - - [Makefile Reference](./MAKE.md) diff --git a/bin/create-readme.sh b/bin/create-readme.sh index 19e7f65..d5aac43 100755 --- a/bin/create-readme.sh +++ b/bin/create-readme.sh @@ -82,16 +82,16 @@ fi # API-PRIVATE.md ############################################################################### -if [ "$api" == "true" ]; then - printf "Create documentation (API-PRIVATE.md)\n" - touch API-PRIVATE.md - printf "\n\n\n" >> API-PRIVATE.md - node node_modules/.bin/jsdoc2md --no-cache --private --configure .jsdoc.json . > API-PRIVATE.md - node node_modules/.bin/doctoc API-PRIVATE.md --github --title "## TOC" --maxlevel 2 - - printf -- "- [Private Api Reference](./API-PRIVATE.md)" >> README.md - printf "\n\n" >> README.md -fi +#if [ "$api" == "true" ]; then +# printf "Create documentation (API-PRIVATE.md)\n" +# touch API-PRIVATE.md +# printf "\n\n\n" >> API-PRIVATE.md +# node node_modules/.bin/jsdoc2md --no-cache --private --configure .jsdoc.json . > API-PRIVATE.md +# node node_modules/.bin/doctoc API-PRIVATE.md --github --title "## TOC" --maxlevel 2 +# +# printf -- "- [Private Api Reference](./API-PRIVATE.md)" >> README.md +# printf "\n\n" >> README.md +#fi ############################################################################### # MAKE.md diff --git a/index.js b/index.js index aad8f64..3a2685e 100755 --- a/index.js +++ b/index.js @@ -1,14 +1 @@ -const Transformer = require('./src/transformer.js').default; -const Reader = require('./src/reader.js').default; -const Writer = require('./src/writer.js').default; -const Constants = require('./src/constants.js'); - -module.exports = { - transform: Transformer.transform, - read: Reader.read, - write: Writer.write, - TYPE_YAML: Constants.TYPE_YAML, - TYPE_JS: Constants.TYPE_JS, - TYPE_JSON: Constants.TYPE_JSON, -}; - +module.exports = require('./src/jy-transform'); diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index 5833f57..70fcdcb 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -1,6 +1,6 @@ ## API in a Minute -### Read, Transform & Write from Source to Destination +### Transform from Source to Destination ```javascript import { transform } from 'jy-transform'; @@ -28,11 +28,11 @@ try { const msg = await transform(options); // Transform, of course, inside an async. console.log(msg); // Success message! } catch (err) { // Oops! - console.error(err.stack); + console.error(err); } ``` -### Read into JS object from particular Source (File, Stream or JS Object) only +### Read into JS object from particular Source (File, Stream or JS Object) ```javascript import { read } from 'jy-transform'; @@ -51,11 +51,11 @@ try { const object = await read(options); console.log(JSON.stringify(object)); // Print read object. } catch (err) { - console.error(err.stack); + console.error(err); } ``` -### Write JS object to particular Destination only +### Write JS object to particular Destination ```javascript import { write } from 'jy-transform'; @@ -74,7 +74,7 @@ try { const msg = await write(object, options); console.log(msg); // Print write success message. } catch (err) { - console.error(err.stack); + console.error(err); } ``` @@ -106,26 +106,28 @@ So, what are the typical use cases for this module? In terms of _transformation_ these consists of different phases: 1. Reading from source - 2. Transforming JSON objects (`Transformer`) or apply dedicated actions on the intermediate JSON objects + 2. Transforming JSON objects or apply dedicated actions on the intermediate JSON objects 3. Writing to a destination -#### Reading From +#### Read Case -- _*.yaml_ file -- _*.js_ file -- _*.json_ file +Reading from a file: -Additionally, on API level from a: +- _*.yaml_ +- _*.js_ +- _*.json_ -- `stream.Readable` - - Contain serialized JS, JSON or YAML - - If not file stream it requires `options.origin` property set +Additionally, on API level from: + +- `stream.Readable`: + - Contains serialized JS, JSON or YAML + - If not a file stream then setting requires `options.origin` property is mandatory - Reads as UTF-8 -- JS `object` +- JS `object`: - Actually, this means the reading phase is "skipped", because object is in-memory already - Of course, this case _cannot_ not be applied to serialized JSON or YAML content -#### Transformation +#### Transformation Case The _transformation_ is usually a format change, but can also be refer to content changes on the intermediate JS object, the latter with the help of a configured `transform` callback function. @@ -150,21 +152,23 @@ while: As mentioned above the configured `transform` callback can apply particular actions on the intermediate JS object, but this is an optional part for [transformation](#transformation) phase. -#### Writing To +#### Write Case + +Writing to a file: -- _*.yaml_ file -- _*.js_ file -- _*.json_ file +- _*.yaml_ +- _*.js_ +- _*.json_ -Additionally, on API level to a: +Additionally, on API level to: -- `stream.Writable` +- `stream.Writable`: - Serialized JS, JSON and YAML - Requires `options.target` property set - Writes UTF-8 -- JS `object - - JS as simple reference - - YAML and JSON as serialized string +- JS `object`: + - JS as a simple reference + - YAML and JSON as a serialized string ### Limitations @@ -199,9 +203,6 @@ Additionally, on API level to a: and JS only, but at the moment we require that each document to transform is a _single_ one per source (or in case of JS could be identified)! This feature is planned and reflected in [#14](https://github.com/deadratfink/jy-transform/issues/14). -- Schema validation for input and output is another topic which is planned by - [#1](https://github.com/deadratfink/jy-transform/issues/1) and - [#2](https://github.com/deadratfink/jy-transform/issues/2). ### CLI Usage @@ -275,7 +276,7 @@ Then we can transform it to a JSON content as _foo.json_ file: ```json { - "foo": "bar" + "foo": "bar" } ``` @@ -307,13 +308,11 @@ Accordingly, this is also true for the `target` option. The command ```text -$ jyt foo.json -i 4 +$ jyt foo.json ``` results in _foo.js_: ```javascript -module.exports = { - foo: "bar" -} +export default {foo: 'bar'} ``` #### Example: JS ⇒ YAML diff --git a/src/jy-transform.js b/src/jy-transform.js new file mode 100755 index 0000000..e7ac8ed --- /dev/null +++ b/src/jy-transform.js @@ -0,0 +1,171 @@ +import { transform as _transform } from './transformer'; +import { read as _read } from './reader'; +import { write as _write } from './writer'; +import { + TYPE_YAML as _TYPE_YAML, + TYPE_JS as _TYPE_JS, + TYPE_JSON as _TYPE_JSON, +} from './constants'; + +/** + * @module jy-transform + * @description This module provides the _public_ interface for the _read_, _write_ and _transform_ functionality. + * @public + * @author Jens Krefeldt + */ + +/** + * The entry method for all transformations accepting a configuration object and + * an (optional) middleware function. It executes the transformation logic. + * + * 1. Input (read) + * 2. Transform [ + Middleware] + * 3. Output (write). + * + * @param {TransformOptions} options - The configuration for a transformation. + * @returns {Promise} The transformation result. + * @resolve {string} With the transformation result as message (e.g. to be logged by caller). + * @reject {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. + * @reject {ValidationError} If any `options` validation occurs. + * @reject {Error} Will throw any error if read, transform or write operation failed due to any reason. + * @public + * @example + * import { transform } from 'jy-transform'; + * const options = { + * src: 'foo/bar.yaml', // From YAML file... + * transform: async (object) => { // ...with exchanging value... + * object.foo = 'new value'; + * return object; + * }, + * target: 'foo/bar.json', // ...to a new JSON file. + * indent: 4, + * }; + * + * // ---- Promise style: + * + * transform(options) + * .then(console.log) + * .catch(console.error); + * + * + * // ---- async/await style: + * + * try { + * const msg = await transform(options); + * console.log(msg); + * } catch (err) { + * console.error(err.stack); + * }; + */ +export const transform = _transform; +/** + * Reads a particular content type from a source provided in the passed `options`. + * + * @param {ReadOptions} options - The read options. + * @returns {Promise} The result. + * @resolve {string} Resolves with JS object result. + * @reject {ValidationError} If any `options` validation occurs. + * @reject {Error} If any write error occurs. + * @public + * @example + * import { read } from 'jy-transform'; + * + * + * // --- from file path + * + * options = { + * src: 'foo.yml' + * }; + * + * read(options) + * .then(obj => console.log(JSON.stringify(obj))) + * .catch(console.error); + * + * + * // --- from Readable + * + * options = { + * src: fs.createReadStream('foo.yml') + * }; + * + * read(options) + * .then(obj => console.log(JSON.stringify(obj))) + * .catch(console.error); + */ +export const read = _read; + +/** + * Writes the passed JS object to a particular destination described by the passed `options`. + * + * @param {Object} object - The JS source object to write. + * @param {WriteOptions} options - The write options. + * @returns {Promise} The result. + * @resolve {string} With the write success message. + * @reject {Error} If any write error occurs. + * @reject {ValidationError} If any `options` validation occurs. + * @public + * @example + * import { write } from 'jy-transform'; + * + * + * // ---- write obj to file --- + * + * const obj = {...}; + * const options = { + * dest: 'result.js', + * indent: 4 + * } + * + * write(obj, options) + * .then(console.log) + * .catch(console.error); + * + * + * // ---- write obj to Writable --- + * + * options = { + * dest: fs.createWriteStream('result.json'), + * indent: 4 + * } + * + * write(obj, options) + * .then(console.log) + * .catch(console.error); + * + * + * // ---- write obj to object --- + * + * options = { + * dest: {}, + * indent: 4 + * } + * + * write(obj, options) + * .then(console.log) + * .catch(console.error); + */ +export const write = _write; + +/** + * The `'yaml'` type constant. + * @type {string} + * @constant + * @public + */ +export const TYPE_YAML = _TYPE_YAML; + +/** + * The `'js'` type constant. + * @type {string} + * @constant + * @public + */ +export const TYPE_JS = _TYPE_JS; + +/** + * The `'json'` type constant. + * @type {string} + * @constant + * @public + */ +export const TYPE_JSON = _TYPE_JSON; diff --git a/src/reader.js b/src/reader.js index 6ed060d..f021bca 100644 --- a/src/reader.js +++ b/src/reader.js @@ -80,31 +80,7 @@ async function readYaml(options) { * @resolve {string} Resolves with JS object result. * @reject {ValidationError} If any `options` validation occurs. * @reject {Error} If any write error occurs. - * @public - * @example - * import { read } from 'jy-transform'; - * - * - * // --- from file path - * - * options = { - * src: 'foo.yml' - * }; - * - * read(options) - * .then(obj => console.log(JSON.stringify(obj))) - * .catch(console.error); - * - * - * // --- from Readable - * - * options = { - * src: fs.createReadStream('foo.yml') - * }; - * - * read(options) - * .then(obj => console.log(JSON.stringify(obj))) - * .catch(console.error); + * @private */ export async function read(options) { const validatedOptions = await Joi.validate(options, readOptionsSchema); diff --git a/src/serialize-utils.js b/src/serialize-utils.js index a32026e..b837689 100644 --- a/src/serialize-utils.js +++ b/src/serialize-utils.js @@ -16,8 +16,7 @@ import serializeJs from 'serialize-js'; * @returns {Promise.} Resolves with the exports string. * @private */ -async function createExportString(es6, exportsTo) { - console.log('################## ####### es6 = ' + es6) +export async function createExportString(es6, exportsTo) { let exports = es6 ? 'export' : 'module.exports'; if (exportsTo) { exports += es6 ? ` const ${exportsTo} = ` : '.' + exportsTo + ' = '; diff --git a/src/transformer.js b/src/transformer.js index 3c41cd2..1155fc9 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -23,34 +23,7 @@ import { transformOptionsSchema } from './validation/options-schema'; * @reject {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. * @reject {ValidationError} If any `options` validation occurs. * @reject {Error} Will throw any error if read, transform or write operation failed due to any reason. - * @public - * @example - * import { transform } from 'jy-transform'; - * const options = { - * src: 'foo/bar.yaml', // From YAML file... - * transform: async (object) => { // ...with exchanging value... - * object.foo = 'new value'; - * return object; - * }, - * target: 'foo/bar.json', // ...to a new JSON file. - * indent: 4, - * }; - * - * // ---- Promise style: - * - * transform(options) - * .then(console.log) - * .catch(console.error); - * - * - * // ---- async/await style: - * - * try { - * const msg = await transform(options); - * console.log(msg); - * } catch (err) { - * console.error(err.stack); - * }; + * @private */ export async function transform(options) { const validatedOptions = await Joi.validate(options, transformOptionsSchema); diff --git a/src/writer.js b/src/writer.js index 0f96e49..45a0df7 100644 --- a/src/writer.js +++ b/src/writer.js @@ -121,45 +121,6 @@ async function writeJs(object, options) { * @reject {Error} If any write error occurs. * @reject {ValidationError} If any `options` validation occurs. * @public - * @example - * import { write } from 'jy-transform'; - * - * - * // ---- write obj to file --- - * - * const obj = {...}; - * const options = { - * dest: 'result.js', - * indent: 4 - * } - * - * write(obj, options) - * .then(console.log) - * .catch(console.error); - * - * - * // ---- write obj to Writable --- - * - * options = { - * dest: fs.createWriteStream('result.json'), - * indent: 4 - * } - * - * write(obj, options) - * .then(console.log) - * .catch(console.error); - * - * - * // ---- write obj to object --- - * - * options = { - * dest: {}, - * indent: 4 - * } - * - * write(obj, options) - * .then(console.log) - * .catch(console.error); */ export async function write(object, options) { const validatedOptions = await Joi.validate(options, writeOptionsSchema); diff --git a/test/functional/test-jyt-cli.js b/test/functional/test-jyt-cli.js index 2078645..0c17b2d 100644 --- a/test/functional/test-jyt-cli.js +++ b/test/functional/test-jyt-cli.js @@ -1,6 +1,6 @@ import jsYaml from 'js-yaml'; import promisify from 'promisify-es6'; -import { exec, execFile, spawn } from 'child_process'; +import { exec } from 'child_process'; import fsExtra from 'fs-extra'; import fs from 'fs'; import cwd from 'cwd'; @@ -11,7 +11,7 @@ import { TYPE_JS, } from '../../src/constants'; import { - TEST_SUITE_DESCRIPTION_UNIT, + TEST_SUITE_DESCRIPTION_FUNCTIONAL, TEST_DATA_DIR, EXPECTED_VALUE, } from '../helper-constants'; @@ -72,8 +72,8 @@ const CLI_OPTIONS_LONG_TO_SHORT_MAP = { imports: '-m', exports: '-x', strict: '-s', - 'no-es6': '--no-es6', - 'no-single': '--no-single', + 'no-es6': '--no-es6', // no short available + 'no-single': '--no-single', // no short available }; /** @@ -97,12 +97,11 @@ function createOptions(src, dest) { * @param {string[]} args - The source, destination CLI arguments and all CLI options. * @returns {Promise} A result, see details. * @resolve {string} The transformation success message. - * @rejects {Error} Any error occurred. + * @reject {Error} Any error occurred. * @private */ function execJyt(args) { return new Promise((resolve, reject) => { - console.log('CWD: ' + cwd()); const command = './jyt ' + args.join(' '); logger.info('executing command: ' + command); const childProcess = exec(command, { cwd: cwd() /* , encoding: 'utf8' */ }, (err, stdout, stderr) => { @@ -137,6 +136,7 @@ function optionsToArgs(options) { * Helper method which asserts the successful transformation. * * @param {TransformOptions} options - The transformation options. + * @param {boolean} [es6=true] - Whether to use ES6 syntax. * @private */ async function assertTransformSuccess(options, es6 = true) { @@ -167,7 +167,7 @@ async function assertYamlTransformSuccess(options) { expect(object.foo).toBe(EXPECTED_VALUE); } -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - ./jyt -> ./src/cli.js - ', () => { +describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - ./jyt -> ./src/cli.js - ', () => { beforeAll(() => { fsExtra.ensureDirSync(CLI_TEST_BASE_DIR); fsExtra.emptyDirSync(CLI_TEST_BASE_DIR); diff --git a/test/functional/test-reader.js b/test/functional/test-reader.js index e232f20..9dade25 100644 --- a/test/functional/test-reader.js +++ b/test/functional/test-reader.js @@ -1,7 +1,7 @@ import YAMLException from 'js-yaml/lib/js-yaml/exception'; import fs from 'fs'; import { read } from '../../src/reader'; -import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; +import { TEST_SUITE_DESCRIPTION_FUNCTIONAL } from '../helper-constants'; import { TYPE_JS, TYPE_YAML, @@ -14,7 +14,7 @@ import { * @private */ -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - reader - ', () => { +describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - reader - ', () => { /** * Assert a `Error` properties for a given reader function. * diff --git a/test/functional/test-transformer.js b/test/functional/test-transformer.js index f77647d..5c89e6c 100644 --- a/test/functional/test-transformer.js +++ b/test/functional/test-transformer.js @@ -10,7 +10,7 @@ import { TYPE_JS, } from '../../src/constants'; import { - TEST_SUITE_DESCRIPTION_UNIT, + TEST_SUITE_DESCRIPTION_FUNCTIONAL, TEST_DATA_DIR, EXPECTED_VALUE, } from '../helper-constants'; @@ -72,7 +72,8 @@ async function transformFunc(object) { /** * Helper method which asserts the successful transformation. * - * @param {Object} options - The transformation options. + * @param {Object} options - The transformation options. + * @param {boolean} [es6=true] - Whether to use ES6 syntax. */ async function assertTransformSuccess(options, es6 = true) { expect.assertions(2); @@ -119,7 +120,7 @@ function createOptions(src, dest, func = transformFunc) { }; } -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { +describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - transformer - ', () => { beforeAll(() => { fsExtra.ensureDirSync(TRANSFORMER_TEST_BASE_DIR); fsExtra.emptyDirSync(TRANSFORMER_TEST_BASE_DIR); @@ -231,13 +232,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { }); describe('Testing Transformer transforming from YAML to JS', () => { - const SRC = './test/data/test-file.yaml'; const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-yaml-js.js'; - it('should store ' + SRC + ' file to ' + DEST, async () => { + it('should store ' + SRC_YAML + ' file to ' + DEST, async () => { expect.assertions(2); const options = { - src: path.resolve(SRC), + src: path.resolve(SRC_YAML), transform: transformFunc, dest: path.resolve(DEST), }; @@ -272,13 +272,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { }); describe('Testing Transformer transforming from JS to JSON', () => { - const SRC = './test/data/test-file.js'; const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-js-json.json'; - it('should store ' + SRC + ' file to ' + DEST, async () => { + it('should store ' + SRC_JS + ' file to ' + DEST, async () => { expect.assertions(2); const options = { - src: path.resolve(SRC), + src: path.resolve(SRC_JS), transform: transformFunc, dest: path.resolve(DEST), }; @@ -288,12 +287,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { describe('Testing Transformer transforming from JS to YAML', () => { expect.assertions(2); - const SRC = './test/data/test-file.js'; const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-js-yaml.yaml'; - it('should store ' + SRC + ' file to ' + DEST, async () => { + it('should store ' + SRC_JS + ' file to ' + DEST, async () => { const options = { - src: path.resolve(SRC), + src: path.resolve(SRC_JS), transform: transformFunc, dest: path.resolve(DEST), }; @@ -302,12 +300,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { }); describe('Testing Transformer transforming from YAML to YAML', () => { - const SRC = './test/data/test-file.yaml'; const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-yaml-yaml.yaml'; - it('should store ' + SRC + ' file to ' + DEST, async () => { + it('should store ' + SRC_YAML + ' file to ' + DEST, async () => { const options = { - src: path.resolve(SRC), + src: path.resolve(SRC_YAML), transform: transformFunc, dest: path.resolve(DEST), }; @@ -316,12 +313,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { }); describe('Testing Transformer transforming from JSON to JSON', () => { - const SRC = './test/data/test-file.json'; const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-json-json.json'; - it('should store ' + SRC + ' file to ' + DEST, async () => { + it('should store ' + SRC_JSON + ' file to ' + DEST, async () => { const options = { - src: path.resolve(SRC), + src: path.resolve(SRC_JSON), transform: transformFunc, dest: path.resolve(DEST), }; @@ -330,12 +326,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { }); describe('Testing Transformer transforming from JSON to YAML', () => { - const SRC = './test/data/test-file.json'; const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-json-yaml.yaml'; - it('should store ' + SRC + ' file to ' + DEST, async () => { + it('should store ' + SRC_JSON + ' file to ' + DEST, async () => { const options = { - src: path.resolve(SRC), + src: path.resolve(SRC_JSON), transform: transformFunc, dest: path.resolve(DEST), }; @@ -344,13 +339,12 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - transformer - ', () => { }); describe('Testing Transformer transforming from JS to JS', () => { - const SRC = './test/data/test-file.js'; const DEST = TRANSFORMER_TEST_BASE_DIR + '/test-data-transform-js-js.js'; - it('should store ' + SRC + ' file to ' + DEST, async () => { + it('should store ' + SRC_JS + ' file to ' + DEST, async () => { expect.assertions(2); const options = { - src: path.resolve(SRC), + src: path.resolve(SRC_JS), transform: transformFunc, dest: path.resolve(DEST), }; diff --git a/test/functional/test-writer.js b/test/functional/test-writer.js index 12942a8..a56574d 100644 --- a/test/functional/test-writer.js +++ b/test/functional/test-writer.js @@ -5,7 +5,7 @@ import os from 'os'; import stream from 'stream'; import { write } from '../../src/writer'; import { logger } from '../logger'; -import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; +import { TEST_SUITE_DESCRIPTION_FUNCTIONAL } from '../helper-constants'; import { TYPE_YAML, TYPE_JS, @@ -20,7 +20,7 @@ const fsPromised = promisify(fs); * @private */ -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - writer - ', () => { +describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - writer - ', () => { /** * Sample JS content used in tests. * diff --git a/test/unit/test-index.js b/test/unit/test-index.js index 32e132c..ead9f60 100644 --- a/test/unit/test-index.js +++ b/test/unit/test-index.js @@ -1,6 +1,9 @@ -import index, { transform, read, write, TYPE_YAML, TYPE_JS, TYPE_JSON } from '../../index'; +import { transform, read, write, TYPE_YAML, TYPE_JS, TYPE_JSON } from '../../index'; import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; +// eslint-disable-next-line import/no-commonjs +const index = require('../../index'); + /** * @module jy-transform:test-unit:index * @description This unit test module tests the correct exporting from _./index.js_. @@ -11,10 +14,15 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - index - ', () => { describe('Exports Check Unit Tests', () => { describe('Exports', () => it('should be an existing Object', () => { - expect.assertions(3); + expect.assertions(8); expect(index).toBeDefined(); - expect(index).toBeInstanceOf(Object); expect(Object.keys(index)).toHaveLength(6); + expect(index.transform).toBeDefined(); + expect(index.transform).toBeInstanceOf(Function); + expect(index.read).toBeDefined(); + expect(index.read).toBeInstanceOf(Function); + expect(index.write).toBeDefined(); + expect(index.write).toBeInstanceOf(Function); }) ); diff --git a/test/unit/test-jy-transform.js b/test/unit/test-jy-transform.js new file mode 100644 index 0000000..7e16be8 --- /dev/null +++ b/test/unit/test-jy-transform.js @@ -0,0 +1,48 @@ +import { transform, read, write, TYPE_YAML, TYPE_JS, TYPE_JSON } from '../../src/jy-transform'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +/** + * @module jy-transform:test-unit:jy-transform + * @description This unit test module tests the correct exporting from _./src/jy-transform.js_. + * @private + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - jy-transform - ', () => { + describe('Exports Check Unit Tests', () => { + describe('Exported transform', () => + it('should be an existing function', () => { + expect.assertions(2); + expect(transform).toBeDefined(); + expect(transform).toBeInstanceOf(Function); + }) + ); + + describe('Exported read', () => + it('should be an existing function', () => { + expect.assertions(2); + expect(read).toBeDefined(); + expect(read).toBeInstanceOf(Function); + }) + ); + + describe('Exported write', () => + it('should be an existing function', () => { + expect.assertions(2); + expect(write).toBeDefined(); + expect(write).toBeInstanceOf(Function); + }) + ); + + describe('Exported constants', () => + it('should be existing string values', () => { + expect.assertions(6); + expect(TYPE_YAML).toBeDefined(); + expect(TYPE_YAML).toBe('yaml'); + expect(TYPE_JS).toBeDefined(); + expect(TYPE_JS).toBe('js'); + expect(TYPE_JSON).toBeDefined(); + expect(TYPE_JSON).toBe('json'); + }) + ); + }); +}); diff --git a/test/unit/test-serialize-js-utils.js b/test/unit/test-serialize-js-utils.js deleted file mode 100644 index b33cae3..0000000 --- a/test/unit/test-serialize-js-utils.js +++ /dev/null @@ -1,3 +0,0 @@ -/** - * Created by jens.krefeldt on 24.08.17. - */ diff --git a/test/unit/test-serialize-utils.js b/test/unit/test-serialize-utils.js new file mode 100644 index 0000000..61c19ef --- /dev/null +++ b/test/unit/test-serialize-utils.js @@ -0,0 +1,101 @@ +import os from 'os'; +import { + createExportString, + serializeJsToString, + serializeJsToJsonString, +} from '../../src/serialize-utils'; +import { TEST_SUITE_DESCRIPTION_UNIT } from '../helper-constants'; + +/** + * @module jy-transform:test-unit:serialize-utils + * @description This unit test suite checks the validity and correctness of JS serialization utility methods. + * @private + */ + +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - serialize-utils - ', () => { + const namedExport = 'foo'; + const indent = ' '; + const nl = os.EOL; + const toSerializeToJs = { + foo: 'bar', + bar: { + bar: 'bar' + }, + }; + + describe('Function createExportString', () => { + it('should create ES6 default export', async () => { + expect.assertions(1); + const result = await createExportString(true); + expect(result).toBe('export default '); + }); + + it('should create "module.exports"', async () => { + expect.assertions(1); + const result = await createExportString(false); + expect(result).toBe('module.exports = '); + }); + + it('should create ES6 default export with named export', async () => { + expect.assertions(1); + const result = await createExportString(true, 'foo'); + expect(result).toBe(`export const ${namedExport} = `); + }); + + it('should create "module.exports" with named export', async () => { + expect.assertions(1); + const result = await createExportString(false, 'foo'); + expect(result).toBe(`module.exports.${namedExport} = `); + }); + }); + + describe('Function serializeJsToString', () => { + it('should create "use strict;" if configured', async () => { + expect.assertions(1); + const result = await serializeJsToString(toSerializeToJs, { + strict: true, + indent: indent.length, + 'no-es6': false, + }); + expect(result).toBe( + `'use strict;'${nl}${nl}export default {${nl}${indent}foo: 'bar',${nl}${indent}bar: {bar: 'bar'}${nl}};${nl}` + ); + }); + + it('should not create "use strict;" if not configured', async () => { + expect.assertions(1); + const result = await serializeJsToString(toSerializeToJs, { + strict: false, + indent: indent.length, + 'no-es6': false, + }); + expect(result).toBe(`export default {${nl}${indent}foo: 'bar',${nl}${indent}bar: {bar: 'bar'}${nl}};${nl}`); + }); + + it('should serialize all with double quotes if configured', async () => { + expect.assertions(1); + const result = await serializeJsToString(toSerializeToJs, { + strict: true, + indent: indent.length, + 'no-es6': false, + 'no-single': true, + }); + expect(result).toBe( + `"use strict;"${nl}${nl}export default {${nl}${indent}foo: "bar",${nl}${indent}bar: {bar: "bar"}${nl}};${nl}` + ); + }); + }); + + describe('Function serializeJsToJsonString', () => { + it('should serialize correctly', async () => { + expect.assertions(1); + const toSerializeToJson = { + foo: { + bar: 'bar' + }, + }; + const result = await serializeJsToJsonString(toSerializeToJson, indent.length); + expect(result).toBe(`{${nl}${indent}"foo": {${nl}${indent}${indent}"bar": "bar"${nl}${indent}}${nl}}${nl}`); + }); + }); +}); diff --git a/test/unit/validation/test-joi-extensions-file-utils.js b/test/unit/validation/test-joi-extensions-file-utils.js index ac4a1dc..44d8cd9 100644 --- a/test/unit/validation/test-joi-extensions-file-utils.js +++ b/test/unit/validation/test-joi-extensions-file-utils.js @@ -7,7 +7,7 @@ import { isExistingFile } from '../../../src/validation/joi-extensions-file-util * @private */ -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-file-helper - ', () => { +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-file-utils - ', () => { describe('Method isExistingFile(pathStr) ', () => { it('should return true on relative path string with existing file', () => expect(isExistingFile('test/unit/validation/test-joi-extensions-file-utils.js')).toBe(true) diff --git a/test/unit/validation/test-joi-extensions-identifier-utils.js b/test/unit/validation/test-joi-extensions-identifier-utils.js index 6d1d483..5ebe5d1 100644 --- a/test/unit/validation/test-joi-extensions-identifier-utils.js +++ b/test/unit/validation/test-joi-extensions-identifier-utils.js @@ -7,7 +7,7 @@ import { TEST_SUITE_DESCRIPTION_UNIT } from '../../helper-constants'; * @private */ -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-identifier-helper - ', () => { +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - joi-extensions-identifier-utils - ', () => { const nonStringIdentifier = {}; it('should validate non-string identifier \'' + JSON.toString(nonStringIdentifier) + '\' to false', () => expect(isValidEs6Identifier(nonStringIdentifier)).toBe(false) diff --git a/test/unit/validation/test-options-schema-utils.js b/test/unit/validation/test-options-schema-utils.js index 13dd0c2..130b28a 100644 --- a/test/unit/validation/test-options-schema-utils.js +++ b/test/unit/validation/test-options-schema-utils.js @@ -19,7 +19,7 @@ import { * @private */ -describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { +describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-utils - ', () => { describe('Function inferOriginDefault', () => { it('should infer the correct origin from relative path string with existing file having a known file extension', () => expect(inferOriginDefault({ src: 'test/unit/validation/test-joi-extensions-file-utils.js' })).toBe(TYPE_JS) @@ -54,7 +54,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema-helper - ', () => { describe('Function inferTargetDefault', () => { it('should infer the correct target from relative path string with existing file having a known file ending', () => - expect(inferTargetDefault({ dest: 'test/unit/validation/test-joi-extensions-file-helper.yaml' })).toBe(TYPE_YAML) + expect(inferTargetDefault({ dest: 'test/unit/validation/test-joi-extensions-file-utils.yaml' })).toBe(TYPE_YAML) ); it('should infer the default target from relative path string with existing file having an unknown file type', () => From 923aa0f184aab09e2cbb67127181106fe2574cbb Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 1 Sep 2017 09:28:33 +0200 Subject: [PATCH 29/58] Add CLI example at the beginning --- README.md | 12 ++++++++++++ readme/DOCUMENTATION.md | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/README.md b/README.md index 707d5d0..69d8fb0 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,8 @@ npm install jy-transform --global - [Transform from Source to Destination](#transform-from-source-to-destination) - [Read into JS object from particular Source (File, Stream or JS Object)](#read-into-js-object-from-particular-source-file-stream-or-js-object) - [Write JS object to particular Destination](#write-js-object-to-particular-destination) +- [CLI in 3 Seconds](#cli-in-3-seconds) + - [File Transformation](#file-transformation) - [Why This Module?](#why-this-module) - [Usage](#usage) - [Usage Types](#usage-types) @@ -203,6 +205,16 @@ try { } ``` +## CLI in 3 Seconds + +### File Transformation + +E.g. transform YAML content file to a JSON file with an indention of 4: + +```text +$ jyt foo/bar.yaml -t json -i 4 +``` + ## Why This Module? After struggling with some huge YAML file and accidentally diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index 70fcdcb..f9f4f3f 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -78,6 +78,16 @@ try { } ``` +## CLI in 3 Seconds + +### File Transformation + +E.g. transform YAML content file to a JSON file with an indention of 4: + +```text +$ jyt foo/bar.yaml -t json -i 4 +``` + ## Why This Module? After struggling with some huge YAML file and accidentally From bcd6402bffb217d55b986e22d7016bf5ec655c69 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 1 Sep 2017 09:32:41 +0200 Subject: [PATCH 30/58] Remove unneeded api reference section --- README.md | 7 ------- readme/DOCUMENTATION.md | 6 ------ 2 files changed, 13 deletions(-) diff --git a/README.md b/README.md index 69d8fb0..0848f7e 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,6 @@ npm install jy-transform --global - [Examples](#examples) - [Origin and Target Type Inference](#origin-and-target-type-inference) - [API Usage](#api-usage) -- [API Reference](#api-reference) - [Contributing](#contributing) - [Further information](#further-information) @@ -780,12 +779,6 @@ you can do almost everything with the JS object, like - Validating and throwing/resolving with error if not valid - ... -## API Reference - -For more details on how to use the API, please refer to the -[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-public-v3) -wiki which describes the full API and provides more examples. - ## Contributing Pull requests and stars are always welcome. For bugs and feature requests, please create an diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index f9f4f3f..efcebab 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -653,12 +653,6 @@ you can do almost everything with the JS object, like - Validating and throwing/resolving with error if not valid - ... -## API Reference - -For more details on how to use the API, please refer to the -[API Reference](https://github.com/deadratfink/jy-transform/wiki/API-public-v3) -wiki which describes the full API and provides more examples. - ## Contributing Pull requests and stars are always welcome. For bugs and feature requests, please create an From ea468f9a15a29d278e20f82d25a35e04c624d72d Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 8 Sep 2017 08:25:31 +0200 Subject: [PATCH 31/58] Add bithound overall --- .eslintignore | 3 +-- .jestrc.js | 1 - bin/test.sh | 3 ++- readme/BADGES.md | 1 + src/debug-log.js | 29 ----------------------------- src/io-utils.js | 2 +- 6 files changed, 5 insertions(+), 34 deletions(-) delete mode 100644 src/debug-log.js diff --git a/.eslintignore b/.eslintignore index 7d53bb8..3881da2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,6 @@ coverage/** node_modules/** api-docs -coverage/ readme/ .idea lib/ @@ -10,5 +9,5 @@ test/data test/tmp test/functional/tmp -# TODO reve later when test os done +# TODO revert later when test is done test/unit/test-cli.js diff --git a/.jestrc.js b/.jestrc.js index 8bdb047..329904d 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -5,7 +5,6 @@ module.exports = { '!lib/**/*.js', 'src/**/*.js', '!src/cli.js', // TODO: maybe later! - '!src/debug-log.js', // TODO: maybe later! 'index.js', 'jyt', ], diff --git a/bin/test.sh b/bin/test.sh index 1a11dad..23f25c3 100644 --- a/bin/test.sh +++ b/bin/test.sh @@ -20,7 +20,8 @@ fi #git checkout --track origin/master #git checkout --track origin/development #export GIT_MERGE_AUTOEDIT=no -#git config gitflow.branch.develop development +#git config gi +# tflow.branch.develop development #git config gitflow.prefix.versiontag v #git flow init -fd #git flow release start $VERSION diff --git a/readme/BADGES.md b/readme/BADGES.md index 1dcc8f8..ea6fdf9 100644 --- a/readme/BADGES.md +++ b/readme/BADGES.md @@ -10,6 +10,7 @@ [![codecov.io][cc-image-master]][cc-url-master] [![coveralls.io][ca-image-master]][ca-url-master] [![inch-ci.org][inch-image-master]][inch-url-master] +[![bitHound Overall Score](https://www.bithound.io/github/deadratfink/jy-transform/badges/score.svg)](https://www.bithound.io/github/deadratfink/jy-transform) [![bitHound Code][bithound-code-image]][bithound-url] [![bitHound Dependencies][bitHound-dependencies-image]][bitHound-dependencies] [![bitHound Dev Dependencies][bitHound-dev-dependencies-image]][bitHound-dependencies] diff --git a/src/debug-log.js b/src/debug-log.js deleted file mode 100644 index 0bc339f..0000000 --- a/src/debug-log.js +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable no-console */ - -/** - * @module jy-transform:debug-log - * @description The debug logger. Can be enabled via environment variables (set to `true`): - * - `JYT_DEBUG`: (only DEBUG logging via `console.log`) - * - `JYT_DEBUG`: (only ERROR logging via `console.error`) - * @private - */ - -/** - * DEBUG function. - * - * @protected - */ -export const debug = process.env.JYT_DEBUG === 'true' ? - console.log.bind(null, '[DEBUG][jyt.js]:') : - (() => null); - -/** - * DEBUG function. - * - * @protected - */ -export const error = process.env.JYT_ERROR === 'true' ? - console.error.bind(null, '[ERROR][jyt.js]:') : - (() => null); - -/* eslint-enable no-console */ diff --git a/src/io-utils.js b/src/io-utils.js index d5ff4c3..9e97a80 100644 --- a/src/io-utils.js +++ b/src/io-utils.js @@ -79,7 +79,7 @@ export async function readYamlFromfile(file) { // load source from YAML file const yaml = await fsPromisified.readFile(file, UTF8); try { - return jsYaml.safeLoad(yaml); // TODO promisify??? + return jsYaml.safeLoad(yaml); } catch (err) { // probably a YAMLException throw err; } From cd681df6bec063b1932ffba4f941689c1c60f87d Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 8 Sep 2017 10:31:49 +0200 Subject: [PATCH 32/58] update bithound config --- .bithoundrc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.bithoundrc b/.bithoundrc index be96efc..1493b3b 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -1,12 +1,19 @@ { "critics": { - "lint": { - "engine": "eslint" - } + "lint": { + "engine": "eslint", + "configLocation": ".eslintrc.js", + "ignoreLocation": ".eslintignore" + } }, "dependencies": { "unused-ignores": [ "eslint" + ], + "mute": [ + "package-json-to-readme", + "jsdoc-to-markdown", + "inchjs" ] } "ignore": [ @@ -17,6 +24,7 @@ "**/readme/**" ], "test": [ - "**/test/unit/**" + "**/test/unit/**", + "**/test/functional/**" ] } From 0e7b4b6e97a9e334442cf70df8413e0f11b08f20 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 8 Sep 2017 10:33:51 +0200 Subject: [PATCH 33/58] update bithound config --- .bithoundrc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.bithoundrc b/.bithoundrc index 1493b3b..8a211c0 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -21,7 +21,9 @@ "**/coverage/**", "**/lib/**", "**/node_modules/**", - "**/readme/**" + "**/readme/**", + "**/test/tmp/**" + "**/test/functional/tmp/**" ], "test": [ "**/test/unit/**", From 1686f632daf93f1f0afedf7a914fffcb4ad6b42f Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 8 Sep 2017 10:39:10 +0200 Subject: [PATCH 34/58] update bithound config --- .bithoundrc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.bithoundrc b/.bithoundrc index 8a211c0..e361a40 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -4,6 +4,9 @@ "engine": "eslint", "configLocation": ".eslintrc.js", "ignoreLocation": ".eslintignore" + }, + "wc": { + "limit": 500 } }, "dependencies": { From 84bc313c098a89f3562ba766aabbc001eb69745b Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 8 Sep 2017 13:36:08 +0200 Subject: [PATCH 35/58] Add bithound, clean a test --- package.json | 4 +++- test/functional/test-reader.js | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 24d67b0..e7345a9 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "test": "jest --forceExit --expand --no-cache --coverage --config=./.jestrc.js", "eslint": "eslint .", "nsp": "nsp check", - "inch": "inchjs suggest && inchjs list --all && inchjs stats" + "inch": "inchjs suggest && inchjs list --all && inchjs stats", + "bithound": "bithound check git@github.com:deadratfink/jy-transform.git" }, "engines": { "node": ">=5.0.0" @@ -48,6 +49,7 @@ "babel-plugin-transform-flow-strip-types": "~6.22.0", "babel-plugin-transform-runtime": "~6.23.0", "babel-preset-env": "~1.6.0", + "bithound": "~1.7.0", "chalk": "~2.1.0", "codacy-coverage": "~2.0.2", "codeclimate-test-reporter": "~0.5.0", diff --git a/test/functional/test-reader.js b/test/functional/test-reader.js index 9dade25..8ff4b32 100644 --- a/test/functional/test-reader.js +++ b/test/functional/test-reader.js @@ -244,10 +244,6 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - reader - ', () => { expectReaderSuccess({ src: './test/data/test-data.yaml' }, 'myproperty', 'old value') ); - it('should read JS from object successfully', () => // TODO - expectReaderSuccess({ src: { test: 'value' } }, 'test', 'value') - ); - it('should read YAML from stream successfully', () => expectReaderSuccess({ origin: TYPE_YAML, From 05d7bc8c98323e273fc34d49ba5d8c1bf1e02827 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 29 Sep 2017 17:35:07 +0200 Subject: [PATCH 36/58] Dependency update and tests/eslint fixed for it --- .bithoundrc | 5 +-- .eslintrc.js | 1 + package.json | 18 +++++------ src/cli.js | 4 +-- test/functional/test-reader.js | 35 +++++++++++---------- test/functional/test-transformer.js | 23 +++++++++----- test/functional/test-writer.js | 30 ++++++++++-------- test/unit/validation/test-options-schema.js | 17 +++++----- 8 files changed, 73 insertions(+), 60 deletions(-) diff --git a/.bithoundrc b/.bithoundrc index e361a40..e550371 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -14,9 +14,10 @@ "eslint" ], "mute": [ - "package-json-to-readme", + "bithound", + "inchjs", "jsdoc-to-markdown", - "inchjs" + "package-json-to-readme" ] } "ignore": [ diff --git a/.eslintrc.js b/.eslintrc.js index 8a79d29..cdb5e5e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,6 +26,7 @@ module.exports = { }], 'consistent-return': 'error', 'filenames/match-regex': ['error', '^[a-z0-9-]+$'], + 'function-paren-newline': 'off', //['error', 'multiline'], 'import/no-amd': 'error', 'import/no-commonjs': 'error', 'import/prefer-default-export': 'off', diff --git a/package.json b/package.json index e7345a9..e609aca 100644 --- a/package.json +++ b/package.json @@ -37,10 +37,10 @@ "cli": "~1.0.1", "is-stream": "~1.1.0", "joi": "~10.6.0", - "js-yaml": " ~3.9.1", + "js-yaml": " ~3.10.0", "json-stringify-safe": "~5.0.1", "mkdirp-then": "~1.2.0", - "promisify-es6": "~1.0.2", + "promisify-es6": "~1.0.3", "serialize-js": "deadratfink/serialize-js#feature/single_quote_only" }, "devDependencies": { @@ -57,22 +57,22 @@ "coveralls": " ~2.13.1", "cwd": "~0.10.0", "doctoc": " ~1.3.0", - "eslint": "~4.5.0", - "eslint-config-airbnb-base": "~11.3.2", + "eslint": "~4.6.1", + "eslint-config-airbnb-base": "~12.0.0", "eslint-plugin-filenames": "1.2.0", "eslint-plugin-import": " ~2.7.0", - "eslint-plugin-jest": " ~20.0.3", + "eslint-plugin-jest": " ~21.0.2", "eslint-plugin-jest-async": " ~1.0.3", - "eslint-plugin-jsdoc": " ~3.1.1", + "eslint-plugin-jsdoc": " ~3.1.2", "fs-extra": "~4.0.1", "inchjs": "~0.4.1", - "jest": "~20.0.4", + "jest": "~21.0.2", "jsdoc-babel": " ~0.3.0", "jsdoc-parse": "~3.0.0", "jsdoc-to-markdown": "~3.0.0", - "nsp": "~2.7.0", + "nsp": "~2.8.0", "package-json-to-readme": "~2.0.0", - "winston": "~2.3.0" + "winston": "~2.3.1" }, "preferGlobal": true, "bin": { diff --git a/src/cli.js b/src/cli.js index 5906e5b..feb7ffd 100755 --- a/src/cli.js +++ b/src/cli.js @@ -104,13 +104,13 @@ function main(args, cliOptions) { if (args.length > 0) { cli.debug('input file: ' + args[0]); - cliOptions.src = args[0]; + cliOptions.src = args[0]; // eslint-disable-line prefer-destructuring } else { error('please specify an input file as first argument!'); } if (args.length > 1) { cli.debug('output file: ' + args[1]); - cliOptions.dest = args[1]; + cliOptions.dest = args[1]; // eslint-disable-line prefer-destructuring } else { cli.debug('output file not specified, using default'); } diff --git a/test/functional/test-reader.js b/test/functional/test-reader.js index 8ff4b32..2b77186 100644 --- a/test/functional/test-reader.js +++ b/test/functional/test-reader.js @@ -18,13 +18,17 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - reader - ', () => { /** * Assert a `Error` properties for a given reader function. * - * @param {Object} options - The options which potentially produce the error. - * @param {Object} [match={name:'Error'}] - The propertie(s) error should contain. + * @param {Object} options - The options which potentially produce the error. + * @param {string} [match=Error] - The error name to match. * @private */ - const expectReaderError = (options, match = { name: 'Error' }) => { + const expectReaderError = async (options, match = 'Error') => { expect.assertions(1); - return expect(read(options)).rejects.toMatchObject(match); + try { + await read(options); + } catch (err) { + expect(err.name).toMatch(match); + } }; /** @@ -64,7 +68,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - reader - ', () => { src: './test/data/test-data.js', imports: '', }; - return expectReaderError(options, { name: 'ValidationError', isJoi: true }); + return expectReaderError(options, 'ValidationError'); }); it('should read JS successfully', () => @@ -122,7 +126,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - reader - ', () => { src: './test/data/test-imports.js', imports: invalidIdentifier, }; - return expectReaderError(options, { name: 'ValidationError', isJoi: true }); + return expectReaderError(options, 'ValidationError'); }); it('should reject reading JS from file with Error on non-existent identifier for options.imports: ' @@ -178,7 +182,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - reader - ', () => { }, imports: invalidIdentifier, }; - return expectReaderError(options, { name: 'ValidationError', isJoi: true }); + return expectReaderError(options, 'ValidationError'); }); it('should reject reading JS (deeply) from file with Error on non-existent identifier for options.imports: ' @@ -231,11 +235,11 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - reader - ', () => { ); it('should fail JS(ON) read by missing options', () => - expectReaderError(null, { name: 'ValidationError', isJoi: true }) + expectReaderError(null, 'ValidationError') ); it('should fail JS(ON) read by missing options.src', () => - expectReaderError({}, { name: 'ValidationError', isJoi: true }) + expectReaderError({}, 'ValidationError') ); }); @@ -263,26 +267,23 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - reader - ', () => { ); it('should fail reading YAML by providing empty JS object as options.src', () => - expectReaderError({ src: {}, origin: TYPE_YAML }, { name: 'ValidationError', isJoi: true }) + expectReaderError({ src: {}, origin: TYPE_YAML }, 'ValidationError') ); it('should fail YAML reading by missing input options', () => - expectReaderError(null, { name: 'ValidationError', isJoi: true }) + expectReaderError(null, 'ValidationError') ); it('should fail reading YAML by missing options.src', () => - expectReaderError({}, { name: 'ValidationError', isJoi: true }) + expectReaderError({}, 'ValidationError') ); it('should fail reading YAML from configured directory source', () => - expectReaderError({ src: './test/data' }, { name: 'ValidationError', isJoi: true }) + expectReaderError({ src: './test/data' }, 'ValidationError') ); it('should fail reading YAML from non-existing file', () => - expectReaderError({ src: './test/data/non-existing.yml' }, { - name: 'ValidationError', - isJoi: true - }) + expectReaderError({ src: './test/data/non-existing.yml' }, 'ValidationError') ); }); }); diff --git a/test/functional/test-transformer.js b/test/functional/test-transformer.js index 5c89e6c..8c29617 100644 --- a/test/functional/test-transformer.js +++ b/test/functional/test-transformer.js @@ -129,25 +129,32 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - transformer - ', () => { describe('Testing transform with middleware', () => { it('should throw ValidationError if middleware passed is not a function type', async () => { expect.assertions(1); - await expect(transform(createOptions({}, {}, 'not a function'))) - .rejects.toMatchObject({ name: 'ValidationError', isJoi: true }); + try { + await transform(createOptions({}, {}, 'not a function')); + } catch (err) { + expect(err.name).toMatch('ValidationError'); + } }); it('should throw ValidationError if options.dest is not set and cannot be resolved from options.src type', async () => { expect.assertions(1); - await expect(transform(createOptions({} /* We cannot infer destination from this src type! */))) - .rejects.toMatchObject({ name: 'ValidationError', isJoi: true }); - }); + try { + await transform(createOptions({} /* We cannot infer destination from undefined src type! */)); + } catch (err) { + expect(err.name).toMatch('ValidationError'); + } + } + ); - it('should not fail if middleware passed is returning a Promise', () => { + it('should not fail if transform callback passed is returning a Promise', () => { expect.assertions(1); const returningPromise = async object => object; return expect(transform(createOptions({}, {}, returningPromise))) .resolves.toBe('Writing JS to options.dest successful.'); }); - it('should not fail if middleware passed is not returning a Promise', () => { + it('should not fail if transform callback passed is not returning a Promise', () => { expect.assertions(1); const notReturningPromise = object => object; return expect(transform(createOptions({}, {}, notReturningPromise))) @@ -155,7 +162,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - transformer - ', () => { }); }); - describe('Testing transform without middleware', () => { + describe('Testing transform without transform callback', () => { it('should not fail', () => { expect.assertions(1); return expect(transform({ src: {}, dest: {} })).resolves.toBe('Writing JS to options.dest successful.'); diff --git a/test/functional/test-writer.js b/test/functional/test-writer.js index a56574d..c3f8fa8 100644 --- a/test/functional/test-writer.js +++ b/test/functional/test-writer.js @@ -91,14 +91,18 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - writer - ', () => { /** * Assert an `Error` for a given writer function. * - * @param {Object} object - The JS object to write. - * @param {Object} options - The options which potentially produce the error. - * @param {Object} [match={name:'Error'}] - The propertie(s) an error should contain. + * @param {Object} object - The JS object to write. + * @param {Object} options - The options which potentially produce the error. + * @param {string} [match=Error] - The error name to match. * @private */ - const expectWriteError = (object, options, match = { name: 'Error' }) => { + const expectWriteError = async (object, options, match = 'Error') => { expect.assertions(1); - return expect(write(object, options)).rejects.toMatchObject(match); + try { + await write(object, options); + } catch (err) { + expect(err.name).toMatch(match); + } }; /** @@ -153,7 +157,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - writer - ', () => { dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-stream-with-invalid-exports-identifier.js', exports: '#3/-', }; - return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, 'ValidationError'); }); it('should write JS to stream and fail by invalid exports identifier (\'#3/-\')', () => { @@ -162,7 +166,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: '#3/-', }; - return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, 'ValidationError'); }); it('should write JS to stream and fail by invalid exports identifier (\'if\')', () => { @@ -171,7 +175,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - writer - ', () => { dest: fs.createWriteStream(file), exports: 'if', }; - return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, 'ValidationError'); }); it('should write JS to stream and fail by provoked error', () => { @@ -203,7 +207,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - writer - ', () => { dest: {}, exports: '', }; - return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, 'ValidationError'); }); const exports = 'foo'; @@ -227,11 +231,11 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - writer - ', () => { dest: {}, exports: invalidIdentifier, }; - return expectWriteError(JS_CONTENT, options, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, options, 'ValidationError'); }); it('should reject write JS with Error on missing destination', () => { - return expectWriteError(JS_CONTENT, {}, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, {}, 'ValidationError'); }); it('should reject write JS to file by invalid file path', (done) => { @@ -353,11 +357,11 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - writer - ', () => { }; return expectWriteError(invalidYamlJson, { dest: WRITER_TEST_BASE_DIR + '/test-data-by-js-to-file-invalid.yaml' - }, { name: 'YAMLException' }); + }, 'YAMLException'); }); it('should reject with Error on missing destination', () => { - return expectWriteError(JS_CONTENT, {}, { name: 'ValidationError', isJoi: true }); + return expectWriteError(JS_CONTENT, {}, 'ValidationError'); }); }); diff --git a/test/unit/validation/test-options-schema.js b/test/unit/validation/test-options-schema.js index 8462458..332efd2 100644 --- a/test/unit/validation/test-options-schema.js +++ b/test/unit/validation/test-options-schema.js @@ -37,12 +37,13 @@ import Joi from '../../../src/validation/joi-extensions'; * @param {Schema} schema - The validation schema. * @private */ -function expectOptionsValidationError(invalidOptions, schema) { +async function expectOptionsValidationError(invalidOptions, schema) { expect.assertions(1); - return expect(Joi.validate(invalidOptions, schema)).rejects.toMatchObject({ - name: 'ValidationError', - isJoi: true, - }); + try { + await Joi.validate(invalidOptions, schema); + } catch (err) { + expect(err.name).toMatch('ValidationError'); + } } /** @@ -60,12 +61,10 @@ function expectOptionsValidationSuccess(validOptions, schema) { describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { describe('Testing readOptionsSchema validation', () => { it('should reject when options is missing (null)', () => - expectOptionsValidationError(null, readOptionsSchema) - ); + expectOptionsValidationError(null, readOptionsSchema)); it('should reject when options is missing (undefined)', () => - expectOptionsValidationError(undefined, readOptionsSchema) - ); + expectOptionsValidationError(undefined, readOptionsSchema)); it('should set all defaults', async () => { expect.assertions(2); From e7d69941a9767e7be18ec52a26c2e3e3a753f83b Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 29 Sep 2017 17:44:33 +0200 Subject: [PATCH 37/58] Fix readme --- PACKAGE.md | 1 + README.md | 54 ++++++++++++++++++++--------------------- readme/DOCUMENTATION.md | 51 +++++++++++++++++++------------------- 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/PACKAGE.md b/PACKAGE.md index d95726f..55fe591 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -34,6 +34,7 @@ npm test - [babel-plugin-transform-flow-strip-types](https://github.com/babel/babel/tree/master/packages): Strip flow type annotations from your output code. - [babel-plugin-transform-runtime](https://github.com/babel/babel/tree/master/packages): Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals - [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. +- [bithound](https://github.com/bithound/cli.bithound.io): Commands for interacting with bitHound: https://bithound.io - [chalk](https://github.com/chalk/chalk): Terminal string styling done right - [codacy-coverage](https://github.com/codacy/node-codacy-coverage): Code Coverage reporter for Codacy.com - [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects diff --git a/README.md b/README.md index 0848f7e..b2d2e9d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ [![codecov.io][cc-image-master]][cc-url-master] [![coveralls.io][ca-image-master]][ca-url-master] [![inch-ci.org][inch-image-master]][inch-url-master] +[![bitHound Overall Score](https://www.bithound.io/github/deadratfink/jy-transform/badges/score.svg)](https://www.bithound.io/github/deadratfink/jy-transform) [![bitHound Code][bithound-code-image]][bithound-url] [![bitHound Dependencies][bitHound-dependencies-image]][bitHound-dependencies] [![bitHound Dev Dependencies][bitHound-dev-dependencies-image]][bitHound-dependencies] @@ -114,10 +115,10 @@ npm install jy-transform --global - [Usage](#usage) - [Usage Types](#usage-types) - [Use Cases](#use-cases) + - [Origin and Target Type Inference](#origin-and-target-type-inference) - [Limitations](#limitations) - [CLI Usage](#cli-usage) - [Examples](#examples) - - [Origin and Target Type Inference](#origin-and-target-type-inference) - [API Usage](#api-usage) - [Contributing](#contributing) - [Further information](#further-information) @@ -306,6 +307,19 @@ Additionally, on API level to: - JS as a simple reference - YAML and JSON as a serialized string +### Origin and Target Type Inference + +This module supports automatic type inference from file extensions as shown by the following table (from-to): + +| File Extension | Type | +| --- | --- | +| _*.yaml_ | _yaml_ | +| _*.yml_ | _yaml_ | +| _*.js_ | _js_ | +| _*.json_ | _json_ | + +> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! + ### Limitations - Since this module is build to transform from and to different type formats, any @@ -527,7 +541,7 @@ bar: foo > dest: {}, > exports: 'bar' > }; - +> > //...transform > ``` > @@ -588,20 +602,6 @@ $ jyt foo.js -i 4 -f > the `src` _foo.js_, named as _foo(1).js_; note the consecutive number! Naturally, > another run of the command would result in a file called _foo(2).js_ and so forth. -### Origin and Target Type Inference - -The examples above have shown that we have an automatic type inference from file -extensions. This is supported as shown by the following table (from-to): - -| File Extension | Type | -| --- | --- | -| _*.yaml_ | _yaml_ | -| _*.yml_ | _yaml_ | -| _*.js_ | _js_ | -| _*.json_ | _json_ | - -> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! - ### API Usage Since the usage on CLI is a 2-step process: @@ -720,22 +720,22 @@ Let's assume we have some Promise functions to apply. For simplicity reasons we simulate these for the moment by some functions, each adding a key-value to the given (initially empty) JS object. -> **NOTE:** each of them has to resolve with the `object` object! +> **NOTE:** each of them has to resolve with the `data` object! ```javascript -const key1 = async (object) => { - object.key1 = 'value1'; - return object; +const key1 = async (data) => { + data.key1 = 'value1'; + return data; }; -const key2 = async (object) => { - object.key2 = 'value2'; - return object; +const key2 = async (data) => { + data.key2 = 'value2'; + return data; }; -const key3 = async (object) => { - object.key3 = 'value3'; - return object; +const key3 = async (data) => { + data.key3 = 'value3'; + return data; }; ``` @@ -748,7 +748,7 @@ import { transform } from 'jy-transform'; const options = { src: {}, - transform: async (object) => Promise.all([key1(object), key2(object), key3(object)]) + transform: (data) => Promise.all([key1(data), key2(data), key3(data)]) .then(result => result[result.length - 1]) }; diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index efcebab..0be0c4a 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -180,6 +180,19 @@ Additionally, on API level to: - JS as a simple reference - YAML and JSON as a serialized string +### Origin and Target Type Inference + +This module supports automatic type inference from file extensions as shown by the following table (from-to): + +| File Extension | Type | +| --- | --- | +| _*.yaml_ | _yaml_ | +| _*.yml_ | _yaml_ | +| _*.js_ | _js_ | +| _*.json_ | _json_ | + +> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! + ### Limitations - Since this module is build to transform from and to different type formats, any @@ -401,7 +414,7 @@ bar: foo > dest: {}, > exports: 'bar' > }; - +> > //...transform > ``` > @@ -462,20 +475,6 @@ $ jyt foo.js -i 4 -f > the `src` _foo.js_, named as _foo(1).js_; note the consecutive number! Naturally, > another run of the command would result in a file called _foo(2).js_ and so forth. -### Origin and Target Type Inference - -The examples above have shown that we have an automatic type inference from file -extensions. This is supported as shown by the following table (from-to): - -| File Extension | Type | -| --- | --- | -| _*.yaml_ | _yaml_ | -| _*.yml_ | _yaml_ | -| _*.js_ | _js_ | -| _*.json_ | _json_ | - -> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! - ### API Usage Since the usage on CLI is a 2-step process: @@ -594,22 +593,22 @@ Let's assume we have some Promise functions to apply. For simplicity reasons we simulate these for the moment by some functions, each adding a key-value to the given (initially empty) JS object. -> **NOTE:** each of them has to resolve with the `object` object! +> **NOTE:** each of them has to resolve with the `data` object! ```javascript -const key1 = async (object) => { - object.key1 = 'value1'; - return object; +const key1 = async (data) => { + data.key1 = 'value1'; + return data; }; -const key2 = async (object) => { - object.key2 = 'value2'; - return object; +const key2 = async (data) => { + data.key2 = 'value2'; + return data; }; -const key3 = async (object) => { - object.key3 = 'value3'; - return object; +const key3 = async (data) => { + data.key3 = 'value3'; + return data; }; ``` @@ -622,7 +621,7 @@ import { transform } from 'jy-transform'; const options = { src: {}, - transform: async (object) => Promise.all([key1(object), key2(object), key3(object)]) + transform: (data) => Promise.all([key1(data), key2(data), key3(data)]) .then(result => result[result.length - 1]) }; From 981dded08a07265e160d101341d1463a6223e223 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 29 Sep 2017 17:53:16 +0200 Subject: [PATCH 38/58] Update deps --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e609aca..dcef972 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "dependencies": { "cli": "~1.0.1", "is-stream": "~1.1.0", - "joi": "~10.6.0", + "joi": "~11.1.1", "js-yaml": " ~3.10.0", "json-stringify-safe": "~5.0.1", "mkdirp-then": "~1.2.0", @@ -45,7 +45,7 @@ }, "devDependencies": { "babel-cli": "~6.26.0", - "babel-eslint": " ~7.2.3", + "babel-eslint": " ~8.0.1", "babel-plugin-transform-flow-strip-types": "~6.22.0", "babel-plugin-transform-runtime": "~6.23.0", "babel-preset-env": "~1.6.0", @@ -54,19 +54,19 @@ "codacy-coverage": "~2.0.2", "codeclimate-test-reporter": "~0.5.0", "codecov": " ~2.3.0", - "coveralls": " ~2.13.1", + "coveralls": " ~3.0.0", "cwd": "~0.10.0", "doctoc": " ~1.3.0", - "eslint": "~4.6.1", + "eslint": "~4.7.2", "eslint-config-airbnb-base": "~12.0.0", "eslint-plugin-filenames": "1.2.0", "eslint-plugin-import": " ~2.7.0", - "eslint-plugin-jest": " ~21.0.2", + "eslint-plugin-jest": " ~21.2.0", "eslint-plugin-jest-async": " ~1.0.3", "eslint-plugin-jsdoc": " ~3.1.2", "fs-extra": "~4.0.1", "inchjs": "~0.4.1", - "jest": "~21.0.2", + "jest": "~21.2.1", "jsdoc-babel": " ~0.3.0", "jsdoc-parse": "~3.0.0", "jsdoc-to-markdown": "~3.0.0", From b11ce1affd85341817730c6a5b330d03215b8eb4 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Mon, 2 Oct 2017 10:19:39 +0200 Subject: [PATCH 39/58] Some readme fixes --- README.md | 28 ++++++++++++++-------------- readme/DOCUMENTATION.md | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b2d2e9d..e8ce644 100644 --- a/README.md +++ b/README.md @@ -133,13 +133,13 @@ npm install jy-transform --global import { transform } from 'jy-transform'; const options = { - src: 'foo/bar.yaml', // E.g. read from YAML file... - transform: async (object) => { // ...with exchanging value... + src: 'foo/bar.yaml', // E.g. read from YAML file... + transform: async (object) => { // ...with exchanging value... object.foo = 'new value'; return object; }, - dest: 'foo/bar.json', // ...to a new JSON file. - indent: 4, // Ensure an indentation of 4. + dest: 'foo/bar-transformed.json', // ...to a new JSON file. + indent: 4, // Ensure an indentation of 4. }; // ---- Promise style: @@ -318,7 +318,7 @@ This module supports automatic type inference from file extensions as shown by t | _*.js_ | _js_ | | _*.json_ | _json_ | -> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! +> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the `origin` or `target` type! ### Limitations @@ -326,7 +326,7 @@ This module supports automatic type inference from file extensions as shown by t `Function`s residing in JS type objects are _not_ supported, e.g. transforming ```javascript - module.exports = { + export const foobar = { fooKey: 'foo', fooFunction: function foo() { //... @@ -508,11 +508,11 @@ from exactly the one you specify, then provide the `-m` (`--imports`) option. In this this example we have a _foo.js_ file exporting _two_ objects: ```javascript -module.exports.foo = { +export const foo = { foo: 'bar' }; -module.exports.bar = { +export const bar = { bar: 'foo' }; ``` @@ -574,8 +574,8 @@ $ jyt foo.yaml foobar.js -x foobar This generates the following output in JS file using `foobar` as identifier: ```javascript -module.exports.foobar = { - foo: "bar" +export const foobar = { + foo: 'bar' } ``` @@ -622,17 +622,17 @@ For more details about this and all the functions provided by this module please The `origin` and `target` type inference is also standard for the API level. +> **HINT:** of course, if you like you can use the `read`and `write` functionality solely besides any transformation needs. + #### API Properties -The public `transform` function (that does not mean the optional `transform `callback here!) takes +The public `transform` function (that does not mean the optional `transform` callback here!) takes the necessary `options` for the transformation: ```javascript -async function transform(options) +[async] function transform(options) ``` -> **HINT:** of course, if you like you can use the `read`and `write` functionality solely besides any transformation needs. - #### Options For a detailed description see: diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index 0be0c4a..a7a132a 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -6,13 +6,13 @@ import { transform } from 'jy-transform'; const options = { - src: 'foo/bar.yaml', // E.g. read from YAML file... - transform: async (object) => { // ...with exchanging value... + src: 'foo/bar.yaml', // E.g. read from YAML file... + transform: async (object) => { // ...with exchanging value... object.foo = 'new value'; return object; }, - dest: 'foo/bar.json', // ...to a new JSON file. - indent: 4, // Ensure an indentation of 4. + dest: 'foo/bar-transformed.json', // ...to a new JSON file. + indent: 4, // Ensure an indentation of 4. }; // ---- Promise style: @@ -191,7 +191,7 @@ This module supports automatic type inference from file extensions as shown by t | _*.js_ | _js_ | | _*.json_ | _json_ | -> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the origin or target type! +> **NOTE:** if you have files without an extension or e.g. _*.txt_ you _have_ to specify the `origin` or `target` type! ### Limitations @@ -199,7 +199,7 @@ This module supports automatic type inference from file extensions as shown by t `Function`s residing in JS type objects are _not_ supported, e.g. transforming ```javascript - module.exports = { + export const foobar = { fooKey: 'foo', fooFunction: function foo() { //... @@ -381,11 +381,11 @@ from exactly the one you specify, then provide the `-m` (`--imports`) option. In this this example we have a _foo.js_ file exporting _two_ objects: ```javascript -module.exports.foo = { +export const foo = { foo: 'bar' }; -module.exports.bar = { +export const bar = { bar: 'foo' }; ``` @@ -447,8 +447,8 @@ $ jyt foo.yaml foobar.js -x foobar This generates the following output in JS file using `foobar` as identifier: ```javascript -module.exports.foobar = { - foo: "bar" +export const foobar = { + foo: 'bar' } ``` @@ -495,17 +495,17 @@ For more details about this and all the functions provided by this module please The `origin` and `target` type inference is also standard for the API level. +> **HINT:** of course, if you like you can use the `read`and `write` functionality solely besides any transformation needs. + #### API Properties -The public `transform` function (that does not mean the optional `transform `callback here!) takes +The public `transform` function (that does not mean the optional `transform` callback here!) takes the necessary `options` for the transformation: ```javascript -async function transform(options) +[async] function transform(options) ``` -> **HINT:** of course, if you like you can use the `read`and `write` functionality solely besides any transformation needs. - #### Options For a detailed description see: From aeedf4b61407933d34ba2f7dbcc6ac1413b4607a Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 5 Oct 2017 23:19:24 +0200 Subject: [PATCH 40/58] transpile to node 5 --- .babelrc | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.babelrc b/.babelrc index 5bb615c..4862d50 100644 --- a/.babelrc +++ b/.babelrc @@ -1,15 +1,21 @@ { "presets": [ - ["env", { - "targets": { - "node": "current" + [ + "env", + { + "targets": { + "node": "5" + } } - }] + ] ], "plugins": [ - ["transform-runtime", { - "polyfill": false, - "regenerator": true - }] + [ + "transform-runtime", + { + "polyfill": false, + "regenerator": true + } + ] ] } From 195d1fa25da55bc9d97c1240f742d6546cae72a9 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 5 Oct 2017 23:45:22 +0200 Subject: [PATCH 41/58] Update some deps --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index dcef972..925a088 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "coveralls": " ~3.0.0", "cwd": "~0.10.0", "doctoc": " ~1.3.0", - "eslint": "~4.7.2", + "eslint": "~4.8.0", "eslint-config-airbnb-base": "~12.0.0", "eslint-plugin-filenames": "1.2.0", "eslint-plugin-import": " ~2.7.0", @@ -72,7 +72,7 @@ "jsdoc-to-markdown": "~3.0.0", "nsp": "~2.8.0", "package-json-to-readme": "~2.0.0", - "winston": "~2.3.1" + "winston": "~2.4.0" }, "preferGlobal": true, "bin": { From a3d5c5704ff83eaf283902de09023150181324ce Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Fri, 6 Oct 2017 00:46:43 +0200 Subject: [PATCH 42/58] Rename options to es5 and double --- .jestrc.js | 9 +-- package.json | 2 +- src/cli.js | 36 ++++++----- src/constants.js | 4 +- src/serialize-utils.js | 16 ++--- src/validation/options-schema.js | 70 +++++++++++---------- test/functional/test-jyt-cli.js | 6 +- test/functional/test-transformer.js | 4 +- test/unit/test-serialize-utils.js | 18 +++--- test/unit/validation/test-options-schema.js | 68 ++++++++++++++++++-- 10 files changed, 150 insertions(+), 83 deletions(-) diff --git a/.jestrc.js b/.jestrc.js index 329904d..4485542 100644 --- a/.jestrc.js +++ b/.jestrc.js @@ -20,16 +20,17 @@ module.exports = { }, mapCoverage: true, testMatch: [ - // '**/test/functional/test-jyt-cli.js', + // '**/test/unit/test-serialize-utils.js', + // '**/test/functional/test-jyt-cli.js', // '**/test/unit/validation/test-joi-extensions-file-helper.js', // '**/test/unit/validation/test-joi-extensions-identifier-helper.js', //'**/test/functional/test-transformer.js', // '**/test/unit/test-index.js', //'**/test/unit/test-reader.js', // '**/test/unit/test-writer.js', - // '**/test/unit/validation/test-options-schema.js', - '**/test/unit/**/*.js', - '**/test/functional/**/*.js', + //'**/test/unit/validation/test-options-schema.js', + '**/test/unit/**/*.js', + '**/test/functional/**/*.js', //'**/test/unit/validation/test-options-schema-helper.js', diff --git a/package.json b/package.json index 925a088..1576dbb 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "jsdoc-to-markdown": "~3.0.0", "nsp": "~2.8.0", "package-json-to-readme": "~2.0.0", - "winston": "~2.4.0" + "winston": "^2.4.0" }, "preferGlobal": true, "bin": { diff --git a/src/cli.js b/src/cli.js index feb7ffd..5aee982 100755 --- a/src/cli.js +++ b/src/cli.js @@ -12,8 +12,8 @@ import { TYPE_JSON, TYPE_YAML, DEFAULT_STRICT, - DEFAULT_NO_ES6, - DEFAULT_NO_SINGLE_QUOTES, + DEFAULT_ES5, + DEFAULT_DOUBLE_QUOTES, } from './constants'; import { transform } from './transformer'; @@ -53,24 +53,32 @@ const packagePath = path.join(__dirname, '../package.json'); * @private */ const cliOptionsSchema = { - origin: ['o', 'The origin type of INPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + ' ]', + origin: [ + 'o', 'The origin type of INPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + ' ]', 'string', ORIGIN_DESCRIPTION], - target: ['t', 'The target type of OUTPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + ' ]', + target: [ + 't', 'The target type of OUTPUT-FILE: [ ' + TYPE_JS + ' | ' + TYPE_JSON + ' | ' + TYPE_YAML + ' ]', 'string', TARGET_DESCRIPTION], indent: ['i', 'The indention for pretty-print: 1 - 8', 'int', DEFAULT_INDENT], - force: ['f', 'Force overwriting of existing output files on write phase: when files are not overwritten (which' + + force: [ + 'f', 'Force overwriting of existing output files on write phase: when files are not overwritten (which' + ' is default), then the next transformation with same output file name gets a consecutive number on the base' + ' file name, e.g. in case of foo.yaml it would be foo(1).yaml', 'boolean', DEFAULT_FORCE_FILE_OVERWRITE], - imports: ['m', 'Define an identifier for object (to read as "export const identifier / module.exports[.identifier]"' + - ' from JS source file only, must be a valid JS identifier!)', 'string', DEFAULT_JS_IMPORTS_IDENTIFIER], - exports: ['x', 'Define an identifier for object (write to "export const identifier / module.exports[.identifier]"' + - ' in JS destination file only, must be a valid JS identifier!)', 'string', DEFAULT_JS_EXPORTS_IDENTIFIER], - strict: ['s', 'Whether to write a "use strict;" in JS type output', + imports: [ + 'm', 'Define an identifier for object (to read as "export const identifier / module.exports[.identifier]"' + + ' from JS source file only, must be a valid JS identifier!)', 'string', DEFAULT_JS_IMPORTS_IDENTIFIER], + exports: [ + 'x', 'Define an identifier for object (write to "export const identifier / module.exports[.identifier]"' + + ' in JS destination file only, must be a valid JS identifier!)', 'string', DEFAULT_JS_EXPORTS_IDENTIFIER], + strict: [ + 's', 'Whether to write a "use strict;" in JS type output', 'boolean', DEFAULT_STRICT], - 'no-es6': [false, 'Whether not to use ECMAScript6 syntax for JS type output like "module.exports" instead of ' + - '"export default", applicable only for JS output', 'boolean', DEFAULT_NO_ES6], - 'no-single': [false, 'Whether not to use single-quotes style for values in JS type output (i.e. double-quotes)', - 'boolean', DEFAULT_NO_SINGLE_QUOTES], + es5: [ + false, 'Whether not to use ECMAScript6 syntax for JS type output like "module.exports" instead of ' + + '"export default", applicable only for JS output', 'boolean', DEFAULT_ES5], + double: [ + false, 'Whether not to use single-quotes style for values in JS type output (i.e. double-quotes)', + 'boolean', DEFAULT_DOUBLE_QUOTES], }; /** diff --git a/src/constants.js b/src/constants.js index 8c1aebc..35e198f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -120,7 +120,7 @@ export const DEFAULT_STRICT = false; * @constant * @private */ -export const DEFAULT_NO_ES6 = false; +export const DEFAULT_ES5 = false; /** * Whether _not_ to use single-quotes style for values in JS type output (i.e. double-quotes). @@ -128,7 +128,7 @@ export const DEFAULT_NO_ES6 = false; * @constant * @private */ -export const DEFAULT_NO_SINGLE_QUOTES = false; +export const DEFAULT_DOUBLE_QUOTES = false; /** * The `origin` description value. diff --git a/src/serialize-utils.js b/src/serialize-utils.js index b837689..2838af1 100644 --- a/src/serialize-utils.js +++ b/src/serialize-utils.js @@ -11,17 +11,17 @@ import serializeJs from 'serialize-js'; /** * Creates a potential named `'module.exports[.exportsTo]'` string. * - * @param {boolean} es6 - Whether to use ECMAScript6 export syntax. + * @param {boolean} es5 - Whether to use ECMAScript5 export syntax. * @param {string} [exportsTo] - The export name. * @returns {Promise.} Resolves with the exports string. * @private */ -export async function createExportString(es6, exportsTo) { - let exports = es6 ? 'export' : 'module.exports'; +export async function createExportString(es5, exportsTo) { + let exports = es5 ? 'module.exports' : 'export'; if (exportsTo) { - exports += es6 ? ` const ${exportsTo} = ` : '.' + exportsTo + ' = '; + exports += es5 ? '.' + exportsTo + ' = ' : ` const ${exportsTo} = `; } else { - exports += es6 ? ' default ' : ' = '; + exports += es5 ? ' = ' : ' default '; } return exports; } @@ -37,13 +37,13 @@ export async function createExportString(es6, exportsTo) { export async function serializeJsToString(object, options) { let useStrict = ''; if (options.strict) { - const quote = options['no-single'] ? '"' : '\''; + const quote = options.double ? '"' : '\''; useStrict = `${quote}use strict;${quote}${os.EOL}${os.EOL}`; } - const exportsStr = await createExportString(!options['no-es6'], options.exports); + const exportsStr = await createExportString(options.es5, options.exports); return `${useStrict}${exportsStr}${serializeJs.serialize(object, { indent: options.indent, - forceSingleQuotes: !options['no-single'], + forceSingleQuotes: !options.double, })};${os.EOL}`; } diff --git a/src/validation/options-schema.js b/src/validation/options-schema.js index 7a62e97..27e5113 100644 --- a/src/validation/options-schema.js +++ b/src/validation/options-schema.js @@ -15,8 +15,8 @@ import { MIN_YAML_INDENT, MAX_INDENT, DEFAULT_STRICT, - DEFAULT_NO_ES6, - DEFAULT_NO_SINGLE_QUOTES, + DEFAULT_ES5, + DEFAULT_DOUBLE_QUOTES, } from '../constants'; /** @@ -48,24 +48,24 @@ const strictSchema = Joi .description('Whether to write a "use strict;" in JS type output.'); /** - * The `no-es6` option schema. + * The `es5` option schema. * @type {external:joi.Schema} * @private */ -const noES6Schema = Joi +const es5Schema = Joi .boolean() - .default(DEFAULT_NO_ES6) + .default(DEFAULT_ES5) .description('Whether not to use ECMAScript6 syntax for JS type output like "module.exports" instead of ' + '"export default", applicable only for JS output.'); /** - * The `no-single` option schema. + * The `double` option schema. * @type {external:joi.Schema} * @private */ -const noSingleSchema = Joi +const doubleSchema = Joi .boolean() - .default(DEFAULT_NO_SINGLE_QUOTES) + .default(DEFAULT_DOUBLE_QUOTES) .description('Whether not to use single-quotes style for values in JS type output (i.e. double-quotes).'); /** @@ -193,8 +193,8 @@ export const writeOptionsSchema = Joi.object().keys({ indent: indentSchema, force: forceSchema, strict: strictSchema, - 'no-es6': noES6Schema, - 'no-single': noSingleSchema, + es5: es5Schema, + double: doubleSchema, }).default() .required() .unknown(); @@ -205,30 +205,32 @@ export const writeOptionsSchema = Joi.object().keys({ * @constant * @private */ -export const transformOptionsSchema = readOptionsSchema.concat(Joi.object().keys({ - transform: Joi - .func() - .arity(1) - .default(object => object) - .description('The transformation function to alter a read object.'), - dest: Joi - .alternatives().try( - Joi.string() - .min(1) - .label('dest - OUTPUT-FILE'), - Joi.object().type(Stream.Writable), - Joi.object().type(Object), - ) - .default(inferDestDefaultFromSrc, 'try dest resolution from src if not set') - .description('The output destination (if string type is treated as a file path).'), - target: targetSchema, - exports: exportsSchema, - indent: indentSchema, - force: forceSchema, - strict: strictSchema, - 'no-es6': noES6Schema, - 'no-single': noSingleSchema, -}).default() +export const transformOptionsSchema = readOptionsSchema.concat(Joi + .object() + .keys({ + transform: Joi + .func() + .arity(1) + .default(object => object) + .description('The transformation function to alter a read object.'), + dest: Joi + .alternatives().try( + Joi.string() + .min(1) + .label('dest - OUTPUT-FILE'), + Joi.object().type(Stream.Writable), + Joi.object().type(Object), + ) + .default(inferDestDefaultFromSrc, 'try dest resolution from src if not set') + .description('The output destination (if string type is treated as a file path).'), + target: targetSchema, + exports: exportsSchema, + indent: indentSchema, + force: forceSchema, + strict: strictSchema, + es5: es5Schema, + double: doubleSchema, + }).default() .required() ); diff --git a/test/functional/test-jyt-cli.js b/test/functional/test-jyt-cli.js index 0c17b2d..b92bae5 100644 --- a/test/functional/test-jyt-cli.js +++ b/test/functional/test-jyt-cli.js @@ -72,8 +72,8 @@ const CLI_OPTIONS_LONG_TO_SHORT_MAP = { imports: '-m', exports: '-x', strict: '-s', - 'no-es6': '--no-es6', // no short available - 'no-single': '--no-single', // no short available + es5: '--es5', // no short available + double: '--double', // no short available }; /** @@ -211,7 +211,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - ./jyt -> ./src/cli.js - ', () = src: path.resolve(CLI_TEST_BASE_DIR + '/test-data.yaml'), dest: path.resolve(DEST_NO_ES6), target: TYPE_JS, - 'no-es6': true, + es5: true, })); logger.debug(msg); const stats = fs.statSync(DEST_NO_ES6); diff --git a/test/functional/test-transformer.js b/test/functional/test-transformer.js index 8c29617..83fe9d9 100644 --- a/test/functional/test-transformer.js +++ b/test/functional/test-transformer.js @@ -209,7 +209,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - transformer - ', () => { transform: transformFunc, target: TYPE_JS, strict: true, - 'no-single': true, + double: true, }); logger.debug(msg); const stats = fs.statSync(DEST_DQ_AND_STRICT); @@ -227,7 +227,7 @@ describe(TEST_SUITE_DESCRIPTION_FUNCTIONAL + ' - transformer - ', () => { dest: path.resolve(DEST_NO_ES6), transform: transformFunc, target: TYPE_JS, - 'no-es6': true, + es5: true, }); logger.debug(msg); const stats = fs.statSync(DEST_NO_ES6); diff --git a/test/unit/test-serialize-utils.js b/test/unit/test-serialize-utils.js index 61c19ef..fe333cc 100644 --- a/test/unit/test-serialize-utils.js +++ b/test/unit/test-serialize-utils.js @@ -26,25 +26,25 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - serialize-utils - ', () => { describe('Function createExportString', () => { it('should create ES6 default export', async () => { expect.assertions(1); - const result = await createExportString(true); + const result = await createExportString(false); expect(result).toBe('export default '); }); it('should create "module.exports"', async () => { expect.assertions(1); - const result = await createExportString(false); + const result = await createExportString(true); expect(result).toBe('module.exports = '); }); it('should create ES6 default export with named export', async () => { expect.assertions(1); - const result = await createExportString(true, 'foo'); + const result = await createExportString(false, 'foo'); expect(result).toBe(`export const ${namedExport} = `); }); it('should create "module.exports" with named export', async () => { expect.assertions(1); - const result = await createExportString(false, 'foo'); + const result = await createExportString(true, 'foo'); expect(result).toBe(`module.exports.${namedExport} = `); }); }); @@ -55,7 +55,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - serialize-utils - ', () => { const result = await serializeJsToString(toSerializeToJs, { strict: true, indent: indent.length, - 'no-es6': false, + es5: false, }); expect(result).toBe( `'use strict;'${nl}${nl}export default {${nl}${indent}foo: 'bar',${nl}${indent}bar: {bar: 'bar'}${nl}};${nl}` @@ -67,7 +67,7 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - serialize-utils - ', () => { const result = await serializeJsToString(toSerializeToJs, { strict: false, indent: indent.length, - 'no-es6': false, + // es5: false, }); expect(result).toBe(`export default {${nl}${indent}foo: 'bar',${nl}${indent}bar: {bar: 'bar'}${nl}};${nl}`); }); @@ -77,11 +77,11 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - serialize-utils - ', () => { const result = await serializeJsToString(toSerializeToJs, { strict: true, indent: indent.length, - 'no-es6': false, - 'no-single': true, + es5: true, + double: true, }); expect(result).toBe( - `"use strict;"${nl}${nl}export default {${nl}${indent}foo: "bar",${nl}${indent}bar: {bar: "bar"}${nl}};${nl}` + `"use strict;"${nl}${nl}module.exports = {${nl}${indent}foo: "bar",${nl}${indent}bar: {bar: "bar"}${nl}};${nl}` ); }); }); diff --git a/test/unit/validation/test-options-schema.js b/test/unit/validation/test-options-schema.js index 332efd2..3e7b79b 100644 --- a/test/unit/validation/test-options-schema.js +++ b/test/unit/validation/test-options-schema.js @@ -15,8 +15,8 @@ import { MIN_INDENT, MAX_INDENT, DEFAULT_STRICT, - DEFAULT_NO_ES6, - DEFAULT_NO_SINGLE_QUOTES, + DEFAULT_ES5, + DEFAULT_DOUBLE_QUOTES, } from '../../../src/constants'; import { readOptionsSchema, @@ -61,10 +61,12 @@ function expectOptionsValidationSuccess(validOptions, schema) { describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { describe('Testing readOptionsSchema validation', () => { it('should reject when options is missing (null)', () => - expectOptionsValidationError(null, readOptionsSchema)); + expectOptionsValidationError(null, readOptionsSchema) + ); it('should reject when options is missing (undefined)', () => - expectOptionsValidationError(undefined, readOptionsSchema)); + expectOptionsValidationError(undefined, readOptionsSchema) + ); it('should set all defaults', async () => { expect.assertions(2); @@ -241,8 +243,8 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { expect(validatedOptions.exports).toBe(DEFAULT_JS_EXPORTS_IDENTIFIER); expect(validatedOptions.force).toBe(DEFAULT_FORCE_FILE_OVERWRITE); expect(validatedOptions.strict).toBe(DEFAULT_STRICT); - expect(validatedOptions['no-es6']).toBe(DEFAULT_NO_ES6); - expect(validatedOptions['no-single']).toBe(DEFAULT_NO_SINGLE_QUOTES); + expect(validatedOptions.es5).toBe(DEFAULT_ES5); + expect(validatedOptions.double).toBe(DEFAULT_DOUBLE_QUOTES); }); it('should infer options.target from file type', async () => { @@ -464,5 +466,59 @@ describe(TEST_SUITE_DESCRIPTION_UNIT + ' - options-schema - ', () => { }, writeOptionsSchema) ); }); + + describe('Testing options.es5 schema validation', () => { + const notBoolean = {}; + it('should reject non-boolean value \'' + stringify(notBoolean) + '\'', () => + expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + es5: notBoolean, + }, writeOptionsSchema) + ); + + it('should accept valid value \'false\'', () => + expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + es5: false, + }, writeOptionsSchema) + ); + + it('should accept valid value \'true\'', () => + expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + es5: true, + }, writeOptionsSchema) + ); + }); + + describe('Testing options.double schema validation', () => { + const notBoolean = {}; + it('should reject non-boolean value \'' + stringify(notBoolean) + '\'', () => + expectOptionsValidationError({ + src: './test/data/test-data.js', + dest: 'some-file', + double: notBoolean, + }, writeOptionsSchema) + ); + + it('should accept valid value \'false\'', () => + expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + double: false, + }, writeOptionsSchema) + ); + + it('should accept valid value \'true\'', () => + expectOptionsValidationSuccess({ + src: './test/data/test-data.js', + dest: 'some-file', + double: true, + }, writeOptionsSchema) + ); + }); }); }); From 38423d17d20176985114a88f2d77d1c52983d8d0 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 23 Nov 2017 19:09:36 +0100 Subject: [PATCH 43/58] some improvements --- .gitignore | 1 + API-PUBLIC.md | 9 ++- README.md | 8 +-- index.js | 2 +- package.json | 2 +- readme/DOCUMENTATION.md | 8 +-- src/io-utils.js | 99 ++++++++++++++++++-------------- src/jy-transform.js | 9 ++- src/transformer.js | 5 +- src/validation/joi-extensions.js | 6 +- src/writer.js | 12 ++-- 11 files changed, 85 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index 8918b9c..f7bcff8 100755 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ coverage nohup.out /jy-transform.wiki/ /lib +/package-lock.json diff --git a/API-PUBLIC.md b/API-PUBLIC.md index 721eb98..fb821c0 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -53,17 +53,16 @@ This module provides the _public_ interface for the _read_, _write_ and _transfo ### jy-transform~transform ⇒ Promise The entry method for all transformations accepting a configuration object and -an (optional) middleware function. It executes the transformation logic. +an (optional) callback function. It executes the transformation logic. 1. Input (read) -2. Transform [ + Middleware] +2. Transform [ + callback] 3. Output (write). **Kind**: inner constant of [jy-transform](#module_jy-transform) **Returns**: Promise - The transformation result. **Access**: public **Resolve**: string With the transformation result as message (e.g. to be logged by caller). -**Reject**: TypeError Will throw this error when the passed `middleware` is not type of `Function`. **Reject**: ValidationError If any `options` validation occurs. **Reject**: Error Will throw any error if read, transform or write operation failed due to any reason. @@ -76,11 +75,11 @@ an (optional) middleware function. It executes the transformation logic. import { transform } from 'jy-transform'; const options = { src: 'foo/bar.yaml', // From YAML file... - transform: async (object) => { // ...with exchanging value... + transform: async (object) => { // ...callback with exchanging value... object.foo = 'new value'; return object; }, - target: 'foo/bar.json', // ...to a new JSON file. + target: 'foo/bar-transformed.json', // ...to a new JSON file. indent: 4, }; diff --git a/README.md b/README.md index e8ce644..b1c1af5 100644 --- a/README.md +++ b/README.md @@ -256,11 +256,11 @@ Reading from a file: Additionally, on API level from: -- `stream.Readable`: +- `stream.Readable` (stream2): - Contains serialized JS, JSON or YAML - If not a file stream then setting requires `options.origin` property is mandatory - Reads as UTF-8 -- JS `object`: +- JS `Object`: - Actually, this means the reading phase is "skipped", because object is in-memory already - Of course, this case _cannot_ not be applied to serialized JSON or YAML content @@ -299,11 +299,11 @@ Writing to a file: Additionally, on API level to: -- `stream.Writable`: +- `stream.Writable` (stream2) implementations: - Serialized JS, JSON and YAML - Requires `options.target` property set - Writes UTF-8 -- JS `object`: +- JS `Object`: - JS as a simple reference - YAML and JSON as a serialized string diff --git a/index.js b/index.js index 3a2685e..20f9117 100755 --- a/index.js +++ b/index.js @@ -1 +1 @@ -module.exports = require('./src/jy-transform'); +module.exports = require('./lib/jy-transform'); diff --git a/package.json b/package.json index dcef972..9d4d4d4 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "dependencies": { "cli": "~1.0.1", "is-stream": "~1.1.0", - "joi": "~11.1.1", + "joi": "~13.0.2", "js-yaml": " ~3.10.0", "json-stringify-safe": "~5.0.1", "mkdirp-then": "~1.2.0", diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index a7a132a..7956e7d 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -129,11 +129,11 @@ Reading from a file: Additionally, on API level from: -- `stream.Readable`: +- `stream.Readable` (stream2): - Contains serialized JS, JSON or YAML - If not a file stream then setting requires `options.origin` property is mandatory - Reads as UTF-8 -- JS `object`: +- JS `Object`: - Actually, this means the reading phase is "skipped", because object is in-memory already - Of course, this case _cannot_ not be applied to serialized JSON or YAML content @@ -172,11 +172,11 @@ Writing to a file: Additionally, on API level to: -- `stream.Writable`: +- `stream.Writable` (stream2) implementations: - Serialized JS, JSON and YAML - Requires `options.target` property set - Writes UTF-8 -- JS `object`: +- JS `Object`: - JS as a simple reference - YAML and JSON as a serialized string diff --git a/src/io-utils.js b/src/io-utils.js index 9e97a80..da9be57 100644 --- a/src/io-utils.js +++ b/src/io-utils.js @@ -18,16 +18,16 @@ const fsPromisified = promisify(fs); /** * @module jy-transform:io-utils - * @description This module provides an I/O interface for files, streams or `Object`. + * @description This module provides a helper I/O interface for files, streams or `Object`. * @private */ /** - * Reads JS or JSON from file. + * Reads JS or JSON from file, optionally choosing a property given by `imports`. * * @param {string} file - The file path. * @param {string} [imports] - An object which is exported in the file. - * @returns {Object} The read object. + * @returns {Object} The read object from `file`. * @throws {Error} When an `imports` is given but the declared object key is not exported by the file. * @private */ @@ -37,7 +37,7 @@ export function readJsOrJsonFromFile(file, imports) { // eslint-disable-next-line import/no-dynamic-require, global-require const object = require(resolvedPath)[imports]; if (!object) { - throw new Error('an identifier string \'' + imports + '\' was specified for JS object ' + + throw new Error(`An identifier string '${imports}' was specified for JS object ` + 'but could not find this object, pls ensure that file ' + file + ' exports it.'); } return object; @@ -47,11 +47,11 @@ export function readJsOrJsonFromFile(file, imports) { } /** - * Reads JS from JS object. + * Reads JS from JS object, optionally choosing a property given by `imports`. * * @param {string} object - The JS object source. - * @param {string} [imports] - An object which is is a sub-object in `object`. - * @returns {Object} The given `object` object or any sub-object specified by `imports`. + * @param {string} [imports] - An object which is is a property/sub-object in `object`. + * @returns {Object} The given but cloned `object` or any property/sub-object specified by `imports`. * @throws {Error} When an `imports` is given but the declared object key is not contained in the source object. * @private */ @@ -59,8 +59,8 @@ export function readJsFromObject(object, imports) { if (imports) { const subObject = object[imports]; if (!subObject) { - throw new Error('an identifier string \'' + imports + '\' was specified for JS sub-object ' + - 'but could not find this sub-object, pls ensure that object source contains it.'); + throw new Error(`An identifier string '${imports}' was specified for property/JS sub-object ` + + 'but could not be found, pls ensure that object source contains it.'); } return Object.assign({}, subObject); // clone, do not alter original object! } @@ -70,19 +70,14 @@ export function readJsFromObject(object, imports) { /** * Reads YAML from file. * - * @param {string} file - The YAML file source. + * @param {string} file - The YAML file source. * @returns {Object} The read JS object from YAML file. * @throws {Error} When any I/O error occurs while the source file. * @private */ export async function readYamlFromfile(file) { - // load source from YAML file const yaml = await fsPromisified.readFile(file, UTF8); - try { - return jsYaml.safeLoad(yaml); - } catch (err) { // probably a YAMLException - throw err; - } + return jsYaml.safeLoad(yaml); } /** @@ -141,7 +136,7 @@ function getConsecutiveDestName(dest) { /** * Ensures that all dirs exists for file type `dest` and writes the JS object to file. * - * @param {string} object - The object to write into file. + * @param {string} data - The object to write into file. * @param {string} dest - The file destination path. * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. * @param {boolean} [forceOverwrite=false] - Forces overwriting the destination file if `true`. @@ -150,21 +145,21 @@ function getConsecutiveDestName(dest) { * @see {@link TYPE_JS} * @private */ -async function mkdirAndWrite(object, dest, target, forceOverwrite = false) { +async function mkdirAndWrite(data, dest, target, forceOverwrite = false) { const destDir = path.dirname(dest); await mkdirp(destDir); let finalDestination = dest; if (!forceOverwrite) { finalDestination = getConsecutiveDestName(dest); } - await fsPromisified.writeFile(finalDestination, object, UTF8); + await fsPromisified.writeFile(finalDestination, data, UTF8); return 'Writing \'' + target + '\' file \'' + finalDestination + '\' successful.'; } /** * Writes a serialized object to file. * - * @param {string} object - The object to write into file. + * @param {string} data - The object data to write into file. * @param {string} dest - The file destination path. * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. * @param {boolean} [forceOverwrite] - Forces overwriting the destination file if `true`. @@ -175,30 +170,50 @@ async function mkdirAndWrite(object, dest, target, forceOverwrite = false) { * @throws {Error} If serialized JSON file could not be written due to any reason. * @private */ -export function writeToFile(object, dest, target, forceOverwrite) { +export async function writeToFile(data, dest, target, forceOverwrite) { + let stats; + try { + stats = await fsPromisified.stat(dest); + } catch (_) { + // ignore error (because file could possibly not exist at this point of time) + } + if (stats && stats.isDirectory()) { + throw new Error(`Destination file '${dest}' is a directory, pls specify a valid file resource!`); + } + return mkdirAndWrite(data, dest, target, forceOverwrite); +} + +/** + * Writes a string serialized data object to a `stream.Transform`. + * + * @param {string} data - The data to write into stream. + * @param {stream.Transform} writable - The stream destination. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @see {@link TYPE_YAML} + * @see {@link TYPE_JSON} + * @see {@link TYPE_JS} + * @returns {Promise.} Containing the write success message to handle by caller (e.g. for logging). + * @throws {Error} If serialized JS object could not be written due to any reason. + * @private + */ +export function writeToStreamTransform(data, writable, target) { return new Promise((resolve, reject) => { - fsPromisified.stat(dest) - .then((stats) => { - if (stats.isDirectory()) { - reject(new Error('Destination file is a directory, pls specify a valid file resource!')); - return; - } - // file exists - resolve(mkdirAndWrite(object, dest, target, forceOverwrite)); - }) - .catch(() => { - // ignore error (because file could possibly not exist at this point of time) - resolve(mkdirAndWrite(object, dest, target, forceOverwrite)); - }); + writable + .on('error', reject) + .on('finish', () => resolve('Writing ' + target + ' to stream successful.')); + + // write stringified data + writable.write(data); + writable.end(); }); } /** - * Writes a string serialized data object to a stream. + * Writes a string serialized data object to a `stream.Writable`. * - * @param {string} object - The data to write into stream. - * @param {string} dest - The stream destination. - * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. + * @param {string} data - The data to write into stream. + * @param {stream.Writable|stream.Duplex} writable - The stream destination. + * @param {string} target - The target type, one of [ 'yaml' | 'json' | 'js' ]. * @see {@link TYPE_YAML} * @see {@link TYPE_JSON} * @see {@link TYPE_JS} @@ -206,14 +221,14 @@ export function writeToFile(object, dest, target, forceOverwrite) { * @throws {Error} If serialized JS object could not be written due to any reason. * @private */ -export function writeToStream(object, dest, target) { +export function writeToStreamWritable(data, writable, target) { return new Promise((resolve, reject) => { - dest + writable .on('error', reject) .on('finish', () => resolve('Writing ' + target + ' to stream successful.')); // write stringified data - dest.write(object); - dest.end(); + writable.write(data); + writable.end(); }); } diff --git a/src/jy-transform.js b/src/jy-transform.js index e7ac8ed..6d2b11a 100755 --- a/src/jy-transform.js +++ b/src/jy-transform.js @@ -16,16 +16,15 @@ import { /** * The entry method for all transformations accepting a configuration object and - * an (optional) middleware function. It executes the transformation logic. + * an (optional) callback function. It executes the transformation logic. * * 1. Input (read) - * 2. Transform [ + Middleware] + * 2. Transform [ + callback] * 3. Output (write). * * @param {TransformOptions} options - The configuration for a transformation. * @returns {Promise} The transformation result. * @resolve {string} With the transformation result as message (e.g. to be logged by caller). - * @reject {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. * @reject {ValidationError} If any `options` validation occurs. * @reject {Error} Will throw any error if read, transform or write operation failed due to any reason. * @public @@ -33,11 +32,11 @@ import { * import { transform } from 'jy-transform'; * const options = { * src: 'foo/bar.yaml', // From YAML file... - * transform: async (object) => { // ...with exchanging value... + * transform: async (object) => { // ...callback with exchanging value... * object.foo = 'new value'; * return object; * }, - * target: 'foo/bar.json', // ...to a new JSON file. + * target: 'foo/bar-transformed.json', // ...to a new JSON file. * indent: 4, * }; * diff --git a/src/transformer.js b/src/transformer.js index 1155fc9..44d0204 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -11,16 +11,15 @@ import { transformOptionsSchema } from './validation/options-schema'; /** * The entry method for all transformations accepting a configuration object and - * an (optional) middleware function. It executes the transformation logic. + * an (optional) callback function. It executes the transformation logic. * * 1. Input (read) - * 2. Transform [ + Middleware] + * 2. Transform [ + callback] * 3. Output (write). * * @param {TransformOptions} options - The configuration for a transformation. * @returns {Promise} The transformation result. * @resolve {string} With the transformation result as message (e.g. to be logged by caller). - * @reject {TypeError} Will throw this error when the passed `middleware` is not type of `Function`. * @reject {ValidationError} If any `options` validation occurs. * @reject {Error} Will throw any error if read, transform or write operation failed due to any reason. * @private diff --git a/src/validation/joi-extensions.js b/src/validation/joi-extensions.js index 005eea2..2134f00 100644 --- a/src/validation/joi-extensions.js +++ b/src/validation/joi-extensions.js @@ -1,5 +1,4 @@ import JoiBase from 'joi'; -import promisify from 'promisify-es6'; import { isExistingFile } from './joi-extensions-file-utils'; import { isValidEs6Identifier } from './joi-extensions-identifier-utils'; @@ -48,7 +47,4 @@ export const EXTENSIONS = { ] }; -const JoiExtended = JoiBase.extend(EXTENSIONS); -JoiExtended.validate = promisify(JoiExtended.validate); - -export default JoiExtended; +export default JoiBase.extend(EXTENSIONS); diff --git a/src/writer.js b/src/writer.js index 45a0df7..d99de2b 100644 --- a/src/writer.js +++ b/src/writer.js @@ -13,7 +13,7 @@ import { } from './serialize-utils'; import { writeToFile, - writeToStream, + writeToStreamWritable, } from './io-utils'; /** @@ -50,8 +50,8 @@ async function writeYaml(object, options) { if (typeof options.dest === 'string') { // file return writeToFile(yaml, options.dest, TYPE_YAML, options.force); - } else if (isStream.writable(options.dest)) { // stream - return writeToStream(yaml, options.dest, TYPE_YAML); + } else if (isStream.writable(options.dest)) { // stream.Writable | stream.Duplex + return writeToStreamWritable(yaml, options.dest, TYPE_YAML); } // object options.dest = yaml; @@ -74,7 +74,7 @@ async function writeJson(object, options) { if (typeof options.dest === 'string') { // file return writeToFile(jsonString, options.dest, TYPE_JSON, options.force); } else if (isStream.writable(options.dest)) { // stream - return writeToStream(jsonString, options.dest, TYPE_JSON); + return writeToStreamWritable(jsonString, options.dest, TYPE_JSON); } // object options.dest = jsonString; @@ -97,7 +97,7 @@ async function writeJs(object, options) { if (typeof options.dest === 'string') { // file return writeToFile(data, options.dest, TYPE_JS, options.force); } else if (isStream.writable(options.dest)) { // stream - return writeToStream(data, options.dest, TYPE_JS); + return writeToStreamWritable(data, options.dest, TYPE_JS); } // object let msg; @@ -116,7 +116,7 @@ async function writeJs(object, options) { * * @param {Object} object - The JS source object to write. * @param {WriteOptions} options - The write options. - * @returns {Promise} The result. + * @returns {Promise.} The result message. * @resolve {string} With the write success message. * @reject {Error} If any write error occurs. * @reject {ValidationError} If any `options` validation occurs. From 3cdadf7410d8f6a31bdc6e3f526a7edb68f99afa Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 23 Nov 2017 19:22:31 +0100 Subject: [PATCH 44/58] Update deps --- API-PUBLIC.md | 6 +++--- PACKAGE.md | 2 +- package.json | 22 +++++++++++----------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/API-PUBLIC.md b/API-PUBLIC.md index fb821c0..ddfedac 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -59,7 +59,7 @@ an (optional) callback function. It executes the transformation logic. 2. Transform [ + callback] 3. Output (write). -**Kind**: inner constant of [jy-transform](#module_jy-transform) +**Kind**: inner property of [jy-transform](#module_jy-transform) **Returns**: Promise - The transformation result. **Access**: public **Resolve**: string With the transformation result as message (e.g. to be logged by caller). @@ -104,7 +104,7 @@ try { ### jy-transform~read ⇒ Promise Reads a particular content type from a source provided in the passed `options`. -**Kind**: inner constant of [jy-transform](#module_jy-transform) +**Kind**: inner property of [jy-transform](#module_jy-transform) **Returns**: Promise - The result. **Access**: public **Resolve**: string Resolves with JS object result. @@ -146,7 +146,7 @@ read(options) ### jy-transform~write ⇒ Promise Writes the passed JS object to a particular destination described by the passed `options`. -**Kind**: inner constant of [jy-transform](#module_jy-transform) +**Kind**: inner property of [jy-transform](#module_jy-transform) **Returns**: Promise - The result. **Access**: public **Resolve**: string With the write success message. diff --git a/PACKAGE.md b/PACKAGE.md index 55fe591..0e78081 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -46,7 +46,7 @@ npm test - [eslint-config-airbnb-base](https://github.com/airbnb/javascript): Airbnb's base JS ESLint config, following our styleguide - [eslint-plugin-filenames](https://github.com/selaux/eslint-plugin-filenames): Eslint rule for consistent filenames. - [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import): Import with sanity. -- [eslint-plugin-jest](https://github.com/facebook/jest): Eslint rules for Jest +- [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest): Eslint rules for Jest - [eslint-plugin-jest-async](https://github.com/deadratfink/jy-transform.git): ESLint plugin to detect improper Jest test assertions for asynchronous (Promise-based) actions - [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc): JSDoc linting rules for ESLint. - [fs-extra](https://github.com/jprichardson/node-fs-extra): fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf. diff --git a/package.json b/package.json index 7298196..974de51 100644 --- a/package.json +++ b/package.json @@ -45,32 +45,32 @@ }, "devDependencies": { "babel-cli": "~6.26.0", - "babel-eslint": " ~8.0.1", + "babel-eslint": " ~8.0.2", "babel-plugin-transform-flow-strip-types": "~6.22.0", "babel-plugin-transform-runtime": "~6.23.0", - "babel-preset-env": "~1.6.0", + "babel-preset-env": "~1.6.1", "bithound": "~1.7.0", - "chalk": "~2.1.0", + "chalk": "~2.3.0", "codacy-coverage": "~2.0.2", "codeclimate-test-reporter": "~0.5.0", - "codecov": " ~2.3.0", + "codecov": " ~3.0.0", "coveralls": " ~3.0.0", "cwd": "~0.10.0", "doctoc": " ~1.3.0", - "eslint": "~4.8.0", - "eslint-config-airbnb-base": "~12.0.0", + "eslint": "~4.11.0", + "eslint-config-airbnb-base": "~12.1.0", "eslint-plugin-filenames": "1.2.0", - "eslint-plugin-import": " ~2.7.0", - "eslint-plugin-jest": " ~21.2.0", + "eslint-plugin-import": " ~2.8.0", + "eslint-plugin-jest": " ~21.3.2", "eslint-plugin-jest-async": " ~1.0.3", - "eslint-plugin-jsdoc": " ~3.1.2", + "eslint-plugin-jsdoc": " ~3.2.0", "fs-extra": "~4.0.1", "inchjs": "~0.4.1", "jest": "~21.2.1", "jsdoc-babel": " ~0.3.0", "jsdoc-parse": "~3.0.0", - "jsdoc-to-markdown": "~3.0.0", - "nsp": "~2.8.0", + "jsdoc-to-markdown": "~3.0.2", + "nsp": "~3.1.0", "package-json-to-readme": "~2.0.0", "winston": "^2.4.0" }, From d7d44225516a63bae134578bb09af3cec4eef5e2 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Sun, 18 Feb 2018 11:05:25 +0100 Subject: [PATCH 45/58] Add eslint json plugin --- .eslintrc.js | 3 ++- package.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index cdb5e5e..3984b54 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,6 +7,7 @@ module.exports = { plugins: [ 'import', 'jsdoc', + 'json', 'filenames', 'jest', 'jest-async' @@ -71,6 +72,6 @@ module.exports = { additionalTagNames: { customTags: ['resolve', 'reject'] }, - } + } } }; diff --git a/package.json b/package.json index 974de51..e87c50d 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "wiki": "jsdoc2md ./jyt lib/*.js index.js > docs/API.md && doctoc docs/API.md --github --title '### TOC' --maxlevel 2 && cat docs/API.md > '../jy-transform.wiki/API-v2.md' && cat docs/CONTRIBUTING.md > ../jy-transform.wiki/Contributing.md && cat docs/CHANGELOG.md > ../jy-transform.wiki/Changelog.md && doctoc ../jy-transform.wiki/Changelog.md --github --title '### TOC' --maxlevel 3", "pretest": "mkdir -pv test/tmp", "test": "jest --forceExit --expand --no-cache --coverage --config=./.jestrc.js", - "eslint": "eslint .", + "eslint": "eslint . --ext .json --ext .js", "nsp": "nsp check", "inch": "inchjs suggest && inchjs list --all && inchjs stats", "bithound": "bithound check git@github.com:deadratfink/jy-transform.git" @@ -64,6 +64,7 @@ "eslint-plugin-jest": " ~21.3.2", "eslint-plugin-jest-async": " ~1.0.3", "eslint-plugin-jsdoc": " ~3.2.0", + "eslint-plugin-json": "^1.2.0", "fs-extra": "~4.0.1", "inchjs": "~0.4.1", "jest": "~21.2.1", From 819de55d5dcaf9200401ea15ee165a361b8ac2ab Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Sun, 18 Feb 2018 11:10:45 +0100 Subject: [PATCH 46/58] correct some docs --- API-PUBLIC.md | 42 +++++++++++++++++++++--------------------- PACKAGE.md | 31 ++++++++++++++++--------------- src/jy-transform.js | 6 +++--- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/API-PUBLIC.md b/API-PUBLIC.md index ddfedac..3f7613d 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -74,12 +74,12 @@ an (optional) callback function. It executes the transformation logic. ```js import { transform } from 'jy-transform'; const options = { - src: 'foo/bar.yaml', // From YAML file... - transform: async (object) => { // ...callback with exchanging value... + src: 'foo/bar.yaml', // From YAML file... + transform: async (object) => { // ...callback with exchanging value... object.foo = 'new value'; return object; }, - target: 'foo/bar-transformed.json', // ...to a new JSON file. + target: 'foo/bar-transformed.json', // ...to a new JSON file. indent: 4, }; @@ -232,8 +232,8 @@ The configuration properties provided to the `read` function. | Name | Type | Default | Description | | --- | --- | --- | --- | | src | string \| Stream.Readable \| object | | The source (if `string` type it is treated as a file path). | -| origin | string | "yaml" | The source origin type. | -| imports | string | | The exports name for reading from JS source files or objects only. | +| [origin] | string | "yaml" | The source origin type. | +| [imports] | string | | The exports name for reading from JS source files or objects only. | @@ -247,12 +247,12 @@ The configuration properties provided to the `write` function. | Name | Type | Default | Description | | --- | --- | --- | --- | | dest | string \| Stream.Writable \| object | | The destination (if `string` type it is treated as a file path). | -| target | string | "js" | The destination target type. | -| indent | number | 2 | The indentation value for pretty-print of output. | -| exports | string | | The exports name for usage in JS destination files only. | -| force | string | false | Force overwriting of existing output files on write phase. | -| no-es6 | boolean | false | Whether not to use ECMAScript6 syntax for JS type output like `module.exports` instead of `export default`, applicable only for JS output. | -| no-single | boolean | false | Whether _not_ to use single-quotes style for values in JS type output (i.e. double-quotes). | +| [target] | string | "js" | The destination target type. | +| [indent] | number | 2 | The indentation value for pretty-print of output. | +| [exports] | string | | The exports name for usage in JS destination files only. | +| [force] | string | false | Force overwriting of existing output files on write phase. | +| [no-es6] | boolean | false | Whether not to use ECMAScript6 syntax for JS type output like `module.exports` instead of `export default`, applicable only for JS output. | +| [no-single] | boolean | false | Whether _not_ to use single-quotes style for values in JS type output (i.e. double-quotes). | @@ -266,14 +266,14 @@ The configuration properties provided to the `transform` function. | Name | Type | Default | Description | | --- | --- | --- | --- | | src | string \| Stream.Readable \| object | | The _read_ source (if `string` type it is treated as a file path). | -| origin | string | "yaml" | The _read_ source origin type. | -| imports | string | | The _read_ exports name for reading from JS source files or objects only. | -| transform | function | | The option is a _transformation_ function with the following signature:

      ``` [async|Promise] function(object) ``` | -| dest | string \| Stream.Writable \| object | | The _write_ destination (if `string` type it is treated as a file path). This property could be optional in case we infer a value from `src` which is then either a string or a file stream where can get the file path from. If this detection process cannot be fulfilled then the property is `undefined` and the transform process will fail with a `ValidationError` on write phase. | -| target | string | "js" | The _write_ target type. | -| indent | number | 2 | The _write_ indentation value for pretty-print of output. | -| exports | string | | The _write_ exports name for usage in JS destination files only. | -| force | string | false | Force overwriting of existing output files on write phase. | -| no-es6 | boolean | false | Whether not to use ECMAScript6 syntax for JS type output like `module.exports` instead of `export default`, applicable only for JS output. | -| no-single | boolean | false | Whether _not_ to use single-quotes style for values in JS type output (i.e. double-quotes). | +| [origin] | string | "yaml" | The _read_ source origin type. | +| [imports] | string | | The _read_ exports name for reading from JS source files or objects only. | +| [transform] | function | | The option is a _transformation_ function with the following signature:

      ``` [async|Promise] function(object) ``` | +| [dest] | string \| Stream.Writable \| object | | The _write_ destination (if `string` type it is treated as a file path). This property could be optional in case we infer a value from `src` which is then either a string or a file stream where can get the file path from. If this detection process cannot be fulfilled then the property is `undefined` and the transform process will fail with a `ValidationError` on write phase. | +| [target] | string | "js" | The _write_ target type. | +| [indent] | number | 2 | The _write_ indentation value for pretty-print of output. | +| [exports] | string | | The _write_ exports name for usage in JS destination files only. | +| [force] | string | false | Force overwriting of existing output files on write phase. | +| [no-es6] | boolean | false | Whether not to use ECMAScript6 syntax for JS type output like `module.exports` instead of `export default`, applicable only for JS output. | +| [no-single] | boolean | false | Whether _not_ to use single-quotes style for values in JS type output (i.e. double-quotes). | diff --git a/PACKAGE.md b/PACKAGE.md index 0e78081..d908d96 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -19,44 +19,45 @@ npm test ## Dependencies - [cli](https://github.com/node-js-libs/cli): A tool for rapidly building command line apps -- [is-stream](https://github.com/sindresorhus/is-stream): Check if something is a Node.js stream -- [joi](https://github.com/hapijs/joi): Object schema validation -- [js-yaml](https://github.com/nodeca/js-yaml): YAML 1.2 parser and serializer +- [is-stream](): Check if something is a Node.js stream +- [joi](): Object schema validation +- [js-yaml](): YAML 1.2 parser and serializer - [json-stringify-safe](https://github.com/isaacs/json-stringify-safe): Like JSON.stringify, but doesn't blow up on circular refs. -- [mkdirp-then](https://github.com/fs-utils/mkdirp-then): mkdirp as promised +- [mkdirp-then](): mkdirp as promised - [promisify-es6](https://github.com/manuel-di-iorio/promisify-es6): Promisify callback-style functions to ES6 promises - [serialize-js](https://github.com/RReverser/serialize-js): User-readable object serialization for JavaScript. ## Dev Dependencies -- [babel-cli](https://github.com/babel/babel/tree/master/packages): Babel command line. +- [babel-cli](): Babel command line. - [babel-eslint](https://github.com/babel/babel-eslint): Custom parser for ESLint -- [babel-plugin-transform-flow-strip-types](https://github.com/babel/babel/tree/master/packages): Strip flow type annotations from your output code. -- [babel-plugin-transform-runtime](https://github.com/babel/babel/tree/master/packages): Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals -- [babel-preset-env](https://github.com/babel/babel-preset-env): A Babel preset for each environment. +- [babel-plugin-transform-flow-strip-types](): Strip flow type annotations from your output code. +- [babel-plugin-transform-runtime](): Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals +- [babel-preset-env](): A Babel preset for each environment. - [bithound](https://github.com/bithound/cli.bithound.io): Commands for interacting with bitHound: https://bithound.io -- [chalk](https://github.com/chalk/chalk): Terminal string styling done right +- [chalk](): Terminal string styling done right - [codacy-coverage](https://github.com/codacy/node-codacy-coverage): Code Coverage reporter for Codacy.com - [codeclimate-test-reporter](https://github.com/codeclimate/javascript-test-reporter): Code Climate test reporter client for javascript projects - [codecov](https://github.com/codecov/codecov-node): Uploading report to Codecov: https://codecov.io - [coveralls](https://github.com/nickmerwin/node-coveralls): takes json-cov output into stdin and POSTs to coveralls.io -- [cwd](https://github.com/jonschlinkert/cwd): Easily get the CWD (current working directory) of a project based on package.json, optionally starting from a given path. (node.js/javascript util) +- [cwd](): Easily get the CWD (current working directory) of a project based on package.json, optionally starting from a given path. (node.js/javascript util) - [doctoc](https://github.com/thlorenz/doctoc): Generates TOC for markdown files of local git repo. -- [eslint](https://github.com/eslint/eslint): An AST-based pattern checker for JavaScript. +- [eslint](): An AST-based pattern checker for JavaScript. - [eslint-config-airbnb-base](https://github.com/airbnb/javascript): Airbnb's base JS ESLint config, following our styleguide - [eslint-plugin-filenames](https://github.com/selaux/eslint-plugin-filenames): Eslint rule for consistent filenames. - [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import): Import with sanity. -- [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest): Eslint rules for Jest +- [eslint-plugin-jest](): Eslint rules for Jest - [eslint-plugin-jest-async](https://github.com/deadratfink/jy-transform.git): ESLint plugin to detect improper Jest test assertions for asynchronous (Promise-based) actions - [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc): JSDoc linting rules for ESLint. +- [eslint-plugin-json](https://github.com/azeemba/eslint-plugin-json): Lint JSON files - [fs-extra](https://github.com/jprichardson/node-fs-extra): fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf. - [inchjs](https://github.com/rrrene/inchjs): JS Wrapper for Inch for JavaScript - [jest](https://github.com/facebook/jest): Delightful JavaScript Testing. - [jsdoc-babel](https://github.com/ctumolosus/jsdoc-babel): A JSDoc plugin that transforms ES6 source files with Babel before they are processsed. -- [jsdoc-parse](https://github.com/jsdoc2md/jsdoc-parse): Transforms jsdoc data into something more suitable for use as template input -- [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown): Generates markdown API documentation from jsdoc annotated source code +- [jsdoc-parse](): Transforms jsdoc data into something more suitable for use as template input +- [jsdoc-to-markdown](): Generates markdown API documentation from jsdoc annotated source code - [nsp](https://github.com/nodesecurity/nsp): The Node Security (nodesecurity.io) command line interface -- [package-json-to-readme](https://github.com/zeke/package-json-to-readme): Generate a README.md from package.json contents +- [package-json-to-readme](): Generate a README.md from package.json contents - [winston](https://github.com/winstonjs/winston): A multi-transport async logging library for Node.js diff --git a/src/jy-transform.js b/src/jy-transform.js index 6d2b11a..8209a78 100755 --- a/src/jy-transform.js +++ b/src/jy-transform.js @@ -31,12 +31,12 @@ import { * @example * import { transform } from 'jy-transform'; * const options = { - * src: 'foo/bar.yaml', // From YAML file... - * transform: async (object) => { // ...callback with exchanging value... + * src: 'foo/bar.yaml', // From YAML file... + * transform: async (object) => { // ...callback with exchanging value... * object.foo = 'new value'; * return object; * }, - * target: 'foo/bar-transformed.json', // ...to a new JSON file. + * target: 'foo/bar-transformed.json', // ...to a new JSON file. * indent: 4, * }; * From 37129cc97a0693283b1b5e4ad2b17ed3a51a68a9 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 20 Feb 2018 07:45:17 +0100 Subject: [PATCH 47/58] Fix deps and some doc error --- .babelrc | 2 ++ API-PUBLIC.md | 4 ++-- package.json | 3 ++- src/type-definitions.js | 10 +++++----- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.babelrc b/.babelrc index 4862d50..ad586b8 100644 --- a/.babelrc +++ b/.babelrc @@ -10,9 +10,11 @@ ] ], "plugins": [ + "transform-es2015-spread", [ "transform-runtime", { + "helpers": true, "polyfill": false, "regenerator": true } diff --git a/API-PUBLIC.md b/API-PUBLIC.md index 3f7613d..07f7923 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -250,7 +250,7 @@ The configuration properties provided to the `write` function. | [target] | string | "js" | The destination target type. | | [indent] | number | 2 | The indentation value for pretty-print of output. | | [exports] | string | | The exports name for usage in JS destination files only. | -| [force] | string | false | Force overwriting of existing output files on write phase. | +| [force] | boolean | false | Force overwriting of existing output files on write phase. | | [no-es6] | boolean | false | Whether not to use ECMAScript6 syntax for JS type output like `module.exports` instead of `export default`, applicable only for JS output. | | [no-single] | boolean | false | Whether _not_ to use single-quotes style for values in JS type output (i.e. double-quotes). | @@ -273,7 +273,7 @@ The configuration properties provided to the `transform` function. | [target] | string | "js" | The _write_ target type. | | [indent] | number | 2 | The _write_ indentation value for pretty-print of output. | | [exports] | string | | The _write_ exports name for usage in JS destination files only. | -| [force] | string | false | Force overwriting of existing output files on write phase. | +| [force] | boolean | false | Force overwriting of existing output files on write phase. | | [no-es6] | boolean | false | Whether not to use ECMAScript6 syntax for JS type output like `module.exports` instead of `export default`, applicable only for JS output. | | [no-single] | boolean | false | Whether _not_ to use single-quotes style for values in JS type output (i.e. double-quotes). | diff --git a/package.json b/package.json index e87c50d..39ae55c 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,10 @@ "node": ">=5.0.0" }, "dependencies": { + "babel-runtime": "^6.26.0", "cli": "~1.0.1", "is-stream": "~1.1.0", - "joi": "~13.0.2", + "joi": "~12.0.0", "js-yaml": " ~3.10.0", "json-stringify-safe": "~5.0.1", "mkdirp-then": "~1.2.0", diff --git a/src/type-definitions.js b/src/type-definitions.js index d15fbb1..85c091e 100644 --- a/src/type-definitions.js +++ b/src/type-definitions.js @@ -49,9 +49,9 @@ /** * The configuration properties provided to the `read` function. * @typedef {object} ReadOptions - * @property {(string|Stream.Readable|object)} src - The source (if `string` type it is treated as a file path). - * @property {string} [origin=yaml] - The source origin type. - * @property {string} [imports=undefined] - The exports name for reading from JS source files or objects only. + * @property {(string|Stream.Readable|object)} src - The source (if `string` type it is treated as a file path). + * @property {string} [origin=yaml] - The source origin type. + * @property {string} [imports=undefined] - The exports name for reading from JS source files or objects only. * @public */ @@ -62,7 +62,7 @@ * @property {string} [target=js] - The destination target type. * @property {number} [indent=2] - The indentation value for pretty-print of output. * @property {string} [exports=undefined] - The exports name for usage in JS destination files only. - * @property {string} [force=false] - Force overwriting of existing output files on write phase. + * @property {boolean} [force=false] - Force overwriting of existing output files on write phase. * @property {boolean} [no-es6=false] - Whether not to use ECMAScript6 syntax for JS type output like * `module.exports` instead of `export default`, applicable only * for JS output. @@ -95,7 +95,7 @@ * @property {string} [target=js] - The _write_ target type. * @property {number} [indent=2] - The _write_ indentation value for pretty-print of output. * @property {string} [exports=undefined] - The _write_ exports name for usage in JS destination files only. - * @property {string} [force=false] - Force overwriting of existing output files on write phase. + * @property {boolean} [force=false] - Force overwriting of existing output files on write phase. * @property {boolean} [no-es6=false] - Whether not to use ECMAScript6 syntax for JS type output like * `module.exports` instead of `export default`, applicable only * for JS output. From a5084d43c999365fd35f70f456c1c61e4608d5cd Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 20 Feb 2018 07:46:04 +0100 Subject: [PATCH 48/58] Update doc --- PACKAGE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PACKAGE.md b/PACKAGE.md index d908d96..82ef1ce 100644 --- a/PACKAGE.md +++ b/PACKAGE.md @@ -18,6 +18,7 @@ npm test ## Dependencies +- [babel-runtime](): babel selfContained runtime - [cli](https://github.com/node-js-libs/cli): A tool for rapidly building command line apps - [is-stream](): Check if something is a Node.js stream - [joi](): Object schema validation From 271c0005ffbc51cc050db98e29be0a24fac77acd Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 20 Feb 2018 07:56:41 +0100 Subject: [PATCH 49/58] Fix bithound for joi --- .bithoundrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.bithoundrc b/.bithoundrc index e550371..1778260 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -16,6 +16,7 @@ "mute": [ "bithound", "inchjs", + "joi", "jsdoc-to-markdown", "package-json-to-readme" ] From f07a8e226f820710e313ef3fbb78cb98e744563e Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 20 Feb 2018 08:01:38 +0100 Subject: [PATCH 50/58] Fix bithound for joi --- .bithoundrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.bithoundrc b/.bithoundrc index 1778260..f9bab83 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -15,6 +15,7 @@ ], "mute": [ "bithound", + "eslint-plugin-json", "inchjs", "joi", "jsdoc-to-markdown", From 6cce066863e1f7c971c2bbb429abef8c9377ad43 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 20 Feb 2018 08:03:13 +0100 Subject: [PATCH 51/58] Fix bithound for babel-runtime --- .bithoundrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.bithoundrc b/.bithoundrc index f9bab83..5fff4a2 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -11,6 +11,7 @@ }, "dependencies": { "unused-ignores": [ + "babel-runtime", "eslint" ], "mute": [ From b7dbd55d2a65f99a789911a1411ef8121592cab2 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 20 Feb 2018 12:49:40 +0100 Subject: [PATCH 52/58] Fix bithound config --- .bithoundrc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.bithoundrc b/.bithoundrc index 5fff4a2..65b152c 100644 --- a/.bithoundrc +++ b/.bithoundrc @@ -22,14 +22,14 @@ "jsdoc-to-markdown", "package-json-to-readme" ] - } + }, "ignore": [ "**/bin/**", "**/coverage/**", "**/lib/**", "**/node_modules/**", "**/readme/**", - "**/test/tmp/**" + "**/test/tmp/**", "**/test/functional/tmp/**" ], "test": [ From 46f291ad50bb6d0278ed73dd9f3a831053645a2f Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 20 Feb 2018 14:52:21 +0100 Subject: [PATCH 53/58] Fix stuff --- .babelrc | 3 +-- .travis.yml | 2 +- CHANGELOG.md | 12 +++++++----- package.json | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.babelrc b/.babelrc index ad586b8..c6961e7 100644 --- a/.babelrc +++ b/.babelrc @@ -4,13 +4,12 @@ "env", { "targets": { - "node": "5" + "node": "6" } } ] ], "plugins": [ - "transform-es2015-spread", [ "transform-runtime", { diff --git a/.travis.yml b/.travis.yml index acc4075..52d0368 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: node_js node_js: +- "9" - "8" - "7" - "6" -- "5" os: - linux diff --git a/CHANGELOG.md b/CHANGELOG.md index cd47d25..ebfcff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,14 @@ new interface: - [README.md](https://github.com/deadratfink/jy-transform/blob/master/README.md) - [API-PUBLIC.md](https://github.com/deadratfink/jy-transform/blob/master/API-PUBLIC.md) +> **IMPORTANT NOTE: Backwards Incompatible - Node.js version >= v6.0.0 required!** + ### New Features: - [[#64](https://github.com/deadratfink/jy-transform/issues/64)] Support ES6 for JS output. - **CLI & API Backwards Incompatible Change!** - This is the default now: - - Usage of `export default` instead of `module.exports`. - - Usage of `export const foo` instead of `module.exports.foo`. + - Generation of `export default` instead of `module.exports`. + - Generation of `export const foo` instead of `module.exports.foo`. - Can be suppressed by `options[no-es6] = true` (default `false`). - [[#62](https://github.com/deadratfink/jy-transform/issues/62)] The `options.transform` function (formerly aka _middleware_ function) is no longer necessary to be a Promise/`async` one. @@ -33,7 +35,7 @@ new interface: validated for 2 now and throws a `ValidationError` if < 2 (for others 0 is still valid). - [[#56](https://github.com/deadratfink/jy-transform/issues/56)] If _destination_ is not given on transformation process but the _target_ is, then the destination's file extension - is adapted to the proper type, e.g. `$ ./jyt inch.json -t yaml` results in a file _inch.yaml_ (formerly: + is adapted to the proper type, e.g. `$ jyt inch.json -t yaml` results in a file _inch.yaml_ (formerly: _inch.json_ with YAML content or respectively _inch(1).json_ with YAML, the latter if `options.force` was `true`). ### Public Interface Changes & Improvements: @@ -70,9 +72,9 @@ new interface: - Usage of _native_ Promises instead of [bluebird](http://bluebirdjs.com/docs/getting-started.html). - Test dependencies reduced. - [[#53](https://github.com/deadratfink/jy-transform/issues/53)] Update supported node versions: + - Add travis build for Node.js v8.x and v9.x. - **CLI & API Backwards Incompatible Change!** - - Add travis build for Node.js v8.x. - - Remove travis build for Node.js < v5.x. + - Remove travis build for Node.js < v6.x. - [[#52](https://github.com/deadratfink/jy-transform/issues/52)] Leverage modern ES6 features: - Integrated by [babel](https://babeljs.io/). - Update of dependencies and amount reduced. diff --git a/package.json b/package.json index 39ae55c..82e87a1 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "bithound": "bithound check git@github.com:deadratfink/jy-transform.git" }, "engines": { - "node": ">=5.0.0" + "node": ">=6.0.0" }, "dependencies": { "babel-runtime": "^6.26.0", From eef8e5941eb83e6960b80ff042bea41a9bd88095 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 22 Feb 2018 07:23:25 +0100 Subject: [PATCH 54/58] Deactivate unreliable bithound check --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 52d0368..427d7fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,8 @@ os: script: - make test -- npm install bithound --save-dev -- bithound check git@github.com:deadratfink/jy-transform.git +# - npm install bithound --save-dev +# - bithound check git@github.com:deadratfink/jy-transform.git after_success: - ./node_modules/codecov/bin/codecov -e TRAVIS_NODE_VERSION From 468eb3df53ab7b306ac8fe7eedd263d00f0ffbb0 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 22 Feb 2018 07:25:50 +0100 Subject: [PATCH 55/58] Add bithound check to Makefile --- API-PUBLIC.md | 6 +++--- MAKE.md | 1 + Makefile | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/API-PUBLIC.md b/API-PUBLIC.md index 07f7923..074ecba 100644 --- a/API-PUBLIC.md +++ b/API-PUBLIC.md @@ -59,7 +59,7 @@ an (optional) callback function. It executes the transformation logic. 2. Transform [ + callback] 3. Output (write). -**Kind**: inner property of [jy-transform](#module_jy-transform) +**Kind**: inner constant of [jy-transform](#module_jy-transform) **Returns**: Promise - The transformation result. **Access**: public **Resolve**: string With the transformation result as message (e.g. to be logged by caller). @@ -104,7 +104,7 @@ try { ### jy-transform~read ⇒ Promise Reads a particular content type from a source provided in the passed `options`. -**Kind**: inner property of [jy-transform](#module_jy-transform) +**Kind**: inner constant of [jy-transform](#module_jy-transform) **Returns**: Promise - The result. **Access**: public **Resolve**: string Resolves with JS object result. @@ -146,7 +146,7 @@ read(options) ### jy-transform~write ⇒ Promise Writes the passed JS object to a particular destination described by the passed `options`. -**Kind**: inner property of [jy-transform](#module_jy-transform) +**Kind**: inner constant of [jy-transform](#module_jy-transform) **Returns**: Promise - The result. **Access**: public **Resolve**: string With the write success message. diff --git a/MAKE.md b/MAKE.md index cc19d42..7ed89d0 100644 --- a/MAKE.md +++ b/MAKE.md @@ -1,6 +1,7 @@ Target Call | Description | Dependencies ---|---|--- `$ make` | This calls the default target `help`. | +`$ make bithound` | Runs bithound check. | `$ make build` | Babel transpiles files from _./src_ to _./lib_. | `$ make clean` | Removes generated files in folders ./node_modules, ./lib and ./coverage" | `$ make eslint` | Runs ESLint. | diff --git a/Makefile b/Makefile index 66b6a05..a9081cb 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,10 @@ eslint: ## Runs ESLint. @printf "Running ESLint...\n" npm run eslint +bithound: ## Runs bithound check. + @printf "Running bithound check...\n" + npm run bithound + help: ## Prints the help about targets. @printf "Usage: make [\033[34mtarget\033[0m]\n" @printf "Default: \033[34m%s\033[0m\n" $(.DEFAULT_GOAL) From 96a7cd739776a0e8ddf3f860fa99af33394479c0 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Thu, 22 Feb 2018 08:23:48 +0100 Subject: [PATCH 56/58] Improve docs --- README.md | 71 +++++++++++++++++++++-------------------- readme/BADGES.md | 3 +- readme/DOCUMENTATION.md | 60 +++++++++++++++++----------------- src/transformer.js | 4 +-- 4 files changed, 70 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index b1c1af5..59afd97 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ [![Codacy Badge](https://img.shields.io/codacy/coverage/c2ebaac0f9874062ba468ff6bd7edc4e.svg?style=flat)](https://www.codacy.com/app/deadratfink/jy-transform?utm_source=github.com&utm_medium=referral&utm_content=deadratfink/jy-transform&utm_campaign=Badge_Coverage) [![Greenkeeper badge](https://badges.greenkeeper.io/deadratfink/jy-transform.svg?style=flat)](https://greenkeeper.io/) [![NSP Status][nsp-image-master]][nsp-url-master] +[![HitCount](http://hits.dwyl.io/deadratfink/jy-transform.svg?style=flat)](http://hits.dwyl.io/deadratfink/jy-transform) [gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat [gh-license-url]: https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md @@ -105,13 +106,13 @@ npm install jy-transform --global ## TOC +- [Why This Module?](#why-this-module) +- [CLI in 3 Seconds](#cli-in-3-seconds) + - [File Transformation](#file-transformation) - [API in a Minute](#api-in-a-minute) - - [Transform from Source to Destination](#transform-from-source-to-destination) + - [Transformation from Source to Destination](#transformation-from-source-to-destination) - [Read into JS object from particular Source (File, Stream or JS Object)](#read-into-js-object-from-particular-source-file-stream-or-js-object) - [Write JS object to particular Destination](#write-js-object-to-particular-destination) -- [CLI in 3 Seconds](#cli-in-3-seconds) - - [File Transformation](#file-transformation) -- [Why This Module?](#why-this-module) - [Usage](#usage) - [Usage Types](#usage-types) - [Use Cases](#use-cases) @@ -125,28 +126,46 @@ npm install jy-transform --global +## Why This Module? + +After struggling with some huge YAML file and accidentally +occurring wrong indentations which results in an annoying investigation hell, +I decided to get rid of the YAML file and therefore, create a module which +should be aimed as the swiss army knife for transforming YAML, JS and JSON +types into each other format. + +## CLI in 3 Seconds + +### File Transformation + +E.g. transform YAML content file to a JSON file with an indention of 4: + +```text +$ jyt foo/bar.yaml -t json -i 4 +``` + ## API in a Minute -### Transform from Source to Destination +### Transformation from Source to Destination ```javascript import { transform } from 'jy-transform'; const options = { - src: 'foo/bar.yaml', // E.g. read from YAML file... - transform: async (object) => { // ...with exchanging value... + src: 'foo/bar.yaml', // E.g. read from YAML file... + transform: async (object) => { // ...with exchanging value... object.foo = 'new value'; return object; }, - dest: 'foo/bar-transformed.json', // ...to a new JSON file. - indent: 4, // Ensure an indentation of 4. + dest: 'foo/bar-transformed.json', // ...to a new JSON file. + indent: 4, // Ensure an indentation of 4. }; // ---- Promise style: -transform(options) - .then(console.log) - .catch(console.error); +transform(options) // Transform, of course, inside an async. + .then(console.log) // Success message! + .catch(console.error); // Oops! // ---- async/await style: @@ -164,7 +183,7 @@ try { ```javascript import { read } from 'jy-transform'; -const options = { src: 'foo/bar.yaml' }; // E.g. read from file. +const options = { src: 'foo/bar.yaml' }; // E.g. read from file. // ---- Promise style: @@ -176,7 +195,7 @@ read(options) try { const object = await read(options); - console.log(JSON.stringify(object)); // Print read object. + console.log(JSON.stringify(object)); // Print read object. } catch (err) { console.error(err); } @@ -205,24 +224,6 @@ try { } ``` -## CLI in 3 Seconds - -### File Transformation - -E.g. transform YAML content file to a JSON file with an indention of 4: - -```text -$ jyt foo/bar.yaml -t json -i 4 -``` - -## Why This Module? - -After struggling with some huge YAML file and accidentally -occurring wrong indentations which results in an annoying investigation hell, -I decided to get rid of the YAML file and therefore, create a module which -should be aimed as the swiss army knife for transforming YAML, JS and JSON -types into each other format. - ## Usage The module can be used on CLI or as API (the latter is fully @@ -267,7 +268,7 @@ Additionally, on API level from: #### Transformation Case The _transformation_ is usually a format change, but can also be refer to content changes on the -intermediate JS object, the latter with the help of a configured `transform` callback function. +intermediate JS object, the latter with the help of a configurable `transform` callback function. All possible directions are: - YAML ⇒ JS @@ -286,7 +287,7 @@ while: - [JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript) = _*.js_ (JS object) - [JSON](http://json.org) = _*.json_ (JS object serialized as JSON) -As mentioned above the configured `transform` callback can apply particular actions on the intermediate JS object, but +As mentioned above a configurable `transform` callback can apply particular actions on the intermediate JS object, but this is an optional part for [transformation](#transformation) phase. #### Write Case diff --git a/readme/BADGES.md b/readme/BADGES.md index ea6fdf9..5610584 100644 --- a/readme/BADGES.md +++ b/readme/BADGES.md @@ -18,6 +18,7 @@ [![Codacy Badge](https://img.shields.io/codacy/coverage/c2ebaac0f9874062ba468ff6bd7edc4e.svg?style=flat)](https://www.codacy.com/app/deadratfink/jy-transform?utm_source=github.com&utm_medium=referral&utm_content=deadratfink/jy-transform&utm_campaign=Badge_Coverage) [![Greenkeeper badge](https://badges.greenkeeper.io/deadratfink/jy-transform.svg?style=flat)](https://greenkeeper.io/) [![NSP Status][nsp-image-master]][nsp-url-master] +[![HitCount](http://hits.dwyl.io/deadratfink/jy-transform.svg?style=flat)](http://hits.dwyl.io/deadratfink/jy-transform) [gh-license-image]: https://img.shields.io/github/license/deadratfink/jy-transform.svg?style=flat [gh-license-url]: https://github.com/deadratfink/jy-transform/blob/master/LICENSE.md diff --git a/readme/DOCUMENTATION.md b/readme/DOCUMENTATION.md index 7956e7d..c397522 100644 --- a/readme/DOCUMENTATION.md +++ b/readme/DOCUMENTATION.md @@ -1,25 +1,43 @@ +## Why This Module? + +After struggling with some huge YAML file and accidentally +occurring wrong indentations which results in an annoying investigation hell, +I decided to get rid of the YAML file and therefore, create a module which +should be aimed as the swiss army knife for transforming YAML, JS and JSON +types into each other format. + +## CLI in 3 Seconds + +### File Transformation + +E.g. transform YAML content file to a JSON file with an indention of 4: + +```text +$ jyt foo/bar.yaml -t json -i 4 +``` + ## API in a Minute -### Transform from Source to Destination +### Transformation from Source to Destination ```javascript import { transform } from 'jy-transform'; const options = { - src: 'foo/bar.yaml', // E.g. read from YAML file... - transform: async (object) => { // ...with exchanging value... + src: 'foo/bar.yaml', // E.g. read from YAML file... + transform: async (object) => { // ...with exchanging value... object.foo = 'new value'; return object; }, - dest: 'foo/bar-transformed.json', // ...to a new JSON file. - indent: 4, // Ensure an indentation of 4. + dest: 'foo/bar-transformed.json', // ...to a new JSON file. + indent: 4, // Ensure an indentation of 4. }; // ---- Promise style: -transform(options) - .then(console.log) - .catch(console.error); +transform(options) // Transform, of course, inside an async. + .then(console.log) // Success message! + .catch(console.error); // Oops! // ---- async/await style: @@ -37,7 +55,7 @@ try { ```javascript import { read } from 'jy-transform'; -const options = { src: 'foo/bar.yaml' }; // E.g. read from file. +const options = { src: 'foo/bar.yaml' }; // E.g. read from file. // ---- Promise style: @@ -49,7 +67,7 @@ read(options) try { const object = await read(options); - console.log(JSON.stringify(object)); // Print read object. + console.log(JSON.stringify(object)); // Print read object. } catch (err) { console.error(err); } @@ -78,24 +96,6 @@ try { } ``` -## CLI in 3 Seconds - -### File Transformation - -E.g. transform YAML content file to a JSON file with an indention of 4: - -```text -$ jyt foo/bar.yaml -t json -i 4 -``` - -## Why This Module? - -After struggling with some huge YAML file and accidentally -occurring wrong indentations which results in an annoying investigation hell, -I decided to get rid of the YAML file and therefore, create a module which -should be aimed as the swiss army knife for transforming YAML, JS and JSON -types into each other format. - ## Usage The module can be used on CLI or as API (the latter is fully @@ -140,7 +140,7 @@ Additionally, on API level from: #### Transformation Case The _transformation_ is usually a format change, but can also be refer to content changes on the -intermediate JS object, the latter with the help of a configured `transform` callback function. +intermediate JS object, the latter with the help of a configurable `transform` callback function. All possible directions are: - YAML ⇒ JS @@ -159,7 +159,7 @@ while: - [JS](https://developer.mozilla.org/en-US/docs/Web/JavaScript) = _*.js_ (JS object) - [JSON](http://json.org) = _*.json_ (JS object serialized as JSON) -As mentioned above the configured `transform` callback can apply particular actions on the intermediate JS object, but +As mentioned above a configurable `transform` callback can apply particular actions on the intermediate JS object, but this is an optional part for [transformation](#transformation) phase. #### Write Case diff --git a/src/transformer.js b/src/transformer.js index 44d0204..465934c 100644 --- a/src/transformer.js +++ b/src/transformer.js @@ -11,7 +11,7 @@ import { transformOptionsSchema } from './validation/options-schema'; /** * The entry method for all transformations accepting a configuration object and - * an (optional) callback function. It executes the transformation logic. + * a (configurable) transformation callback function. * * 1. Input (read) * 2. Transform [ + callback] @@ -21,7 +21,7 @@ import { transformOptionsSchema } from './validation/options-schema'; * @returns {Promise} The transformation result. * @resolve {string} With the transformation result as message (e.g. to be logged by caller). * @reject {ValidationError} If any `options` validation occurs. - * @reject {Error} Will throw any error if read, transform or write operation failed due to any reason. + * @reject {Error} Will throw any error if read, transform or write operation has failed due to any reason. * @private */ export async function transform(options) { From 081c41d235c6be2e8c7e2218405fd61202070de1 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 23 Oct 2018 22:18:50 +0200 Subject: [PATCH 57/58] Delete bithound --- .bithoundrc | 39 --------------------------------------- .circleci/config.yml | 5 ----- .travis.yml | 2 -- CHANGELOG.md | 2 -- package.json | 4 +--- 5 files changed, 1 insertion(+), 51 deletions(-) delete mode 100644 .bithoundrc diff --git a/.bithoundrc b/.bithoundrc deleted file mode 100644 index 65b152c..0000000 --- a/.bithoundrc +++ /dev/null @@ -1,39 +0,0 @@ -{ - "critics": { - "lint": { - "engine": "eslint", - "configLocation": ".eslintrc.js", - "ignoreLocation": ".eslintignore" - }, - "wc": { - "limit": 500 - } - }, - "dependencies": { - "unused-ignores": [ - "babel-runtime", - "eslint" - ], - "mute": [ - "bithound", - "eslint-plugin-json", - "inchjs", - "joi", - "jsdoc-to-markdown", - "package-json-to-readme" - ] - }, - "ignore": [ - "**/bin/**", - "**/coverage/**", - "**/lib/**", - "**/node_modules/**", - "**/readme/**", - "**/test/tmp/**", - "**/test/functional/tmp/**" - ], - "test": [ - "**/test/unit/**", - "**/test/functional/**" - ] -} diff --git a/.circleci/config.yml b/.circleci/config.yml index 8654dee..cb77ff6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,11 +16,6 @@ jobs: - run: name: Running tests command: make test - - run: - name: Bithound - command: | - npm install bithound --save-dev - bithound check git@github.com:deadratfink/jy-transform.git - run: name: Codecov Coverage command: ./node_modules/codecov/bin/codecov diff --git a/.travis.yml b/.travis.yml index 427d7fa..83d0cb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,6 @@ os: script: - make test -# - npm install bithound --save-dev -# - bithound check git@github.com:deadratfink/jy-transform.git after_success: - ./node_modules/codecov/bin/codecov -e TRAVIS_NODE_VERSION diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfcff8..3ff572b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,8 +88,6 @@ new interface: - [[#48](https://github.com/deadratfink/jy-transform/issues/48)] Using [Joi](https://github.com/hapijs/joi) for consistent options validation: - Removal of `OptionsHandler` and `Validator`. -- [[#47](https://github.com/deadratfink/jy-transform/issues/47)] Integration of - [bithound.io](https://www.bithound.io/github/deadratfink/jy-transform) - [[#46](https://github.com/deadratfink/jy-transform/issues/46)] Use Make as an abstraction to npm scripts. - [[#45](https://github.com/deadratfink/jy-transform/issues/45)] [Node Security Plattform](https://nodesecurity.io/orgs/deadratfink/projects/7ac99a62-a8c4-4321-8d57-8a5e542f04f0) integrated. - [[#43](https://github.com/deadratfink/jy-transform/issues/43)] Documentation restructured. diff --git a/package.json b/package.json index 82e87a1..e6b4d41 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,7 @@ "test": "jest --forceExit --expand --no-cache --coverage --config=./.jestrc.js", "eslint": "eslint . --ext .json --ext .js", "nsp": "nsp check", - "inch": "inchjs suggest && inchjs list --all && inchjs stats", - "bithound": "bithound check git@github.com:deadratfink/jy-transform.git" + "inch": "inchjs suggest && inchjs list --all && inchjs stats" }, "engines": { "node": ">=6.0.0" @@ -50,7 +49,6 @@ "babel-plugin-transform-flow-strip-types": "~6.22.0", "babel-plugin-transform-runtime": "~6.23.0", "babel-preset-env": "~1.6.1", - "bithound": "~1.7.0", "chalk": "~2.3.0", "codacy-coverage": "~2.0.2", "codeclimate-test-reporter": "~0.5.0", From 0d3729198ef74a7c128f7ec68543663ed806d3b2 Mon Sep 17 00:00:00 2001 From: Jens Krefeldt Date: Tue, 23 Oct 2018 22:28:31 +0200 Subject: [PATCH 58/58] Update nsp --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6b4d41..721f6c6 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "jsdoc-babel": " ~0.3.0", "jsdoc-parse": "~3.0.0", "jsdoc-to-markdown": "~3.0.2", - "nsp": "~3.1.0", + "nsp": "^3.2.1", "package-json-to-readme": "~2.0.0", "winston": "^2.4.0" },