diff --git a/config/rollup-external.json b/config/rollup-external.json index f2fab57..02d9402 100644 --- a/config/rollup-external.json +++ b/config/rollup-external.json @@ -2,6 +2,7 @@ "names": [ "async_hooks", "chai", + "js-yaml", "sinon" ] } diff --git a/config/rollup-named.json b/config/rollup-named.json index b31da20..8591319 100644 --- a/config/rollup-named.json +++ b/config/rollup-named.json @@ -2,5 +2,8 @@ "node_modules/chai/index.js": [ "expect", "use" + ], + "node_modules/lodash/lodash.js": [ + "isNil" ] } \ No newline at end of file diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..8dccd65 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,12 @@ + + +[Home](./index.md) + +## API Reference + +## Packages + +| Package | Description | +| --- | --- | +| [@apextoaster/js-yaml-schema](./js-yaml-schema.md) | | + diff --git a/docs/api/js-yaml-schema.md b/docs/api/js-yaml-schema.md new file mode 100644 index 0000000..c59e68c --- /dev/null +++ b/docs/api/js-yaml-schema.md @@ -0,0 +1,6 @@ + + +[Home](./index.md) > [@apextoaster/js-yaml-schema](./js-yaml-schema.md) + +## js-yaml-schema package + diff --git a/package.json b/package.json index d4ce753..9f7b09c 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,22 @@ { + "name": "@apextoaster/js-yaml-schema", + "version": "0.1.0", + "description": "Extended JS-YAML schema", + "keywords": [ + "js-yaml", + "schema" + ], + "main": "out/main.js", + "author": "ssube", + "license": "MIT", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^0.1.3", "@microsoft/api-documenter": "^7.5.8", "@microsoft/api-extractor": "^7.5.6", "@types/chai": "^4.2.5", "@types/chai-as-promised": "^7.1.2", + "@types/js-yaml": "^3.12.1", + "@types/lodash": "^4.14.146", "@types/mocha": "^5.2.7", "@types/sinon-chai": "^3.2.3", "@types/source-map-support": "^0.5.0", @@ -20,7 +32,10 @@ "eslint-plugin-mocha": "^6.2.1", "eslint-plugin-no-null": "^1.0.2", "eslint-plugin-sonarjs": "^0.5.0", + "js-yaml": "^3.13.1", + "lodash": "^4.17.15", "mocha": "^6.2.2", + "noicejs": "^3.0.0", "nyc": "^14.1.1", "rollup": "^1.27.0", "rollup-plugin-commonjs": "^10.1.0", @@ -47,5 +62,11 @@ }, "peerDependencies": { "js-yaml": "^3.13.1" + }, + "nyc": { + "extends": "@istanbuljs/nyc-config-typescript" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org" } } diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..5de7868 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,7 @@ +import { VERSION_INFO } from './version'; + +export async function main(argv: Array): Promise { + /* eslint-disable-next-line no-console */ + console.log('Please use this schema with js-yaml.', VERSION_INFO, argv); + return 1; +} diff --git a/src/error/InvalidArgumentError.ts b/src/error/InvalidArgumentError.ts new file mode 100644 index 0000000..463fe29 --- /dev/null +++ b/src/error/InvalidArgumentError.ts @@ -0,0 +1,7 @@ +import { BaseError } from 'noicejs'; + +export class InvalidArgumentError extends BaseError { + constructor(msg = 'invalid argument passed', ...nested: Array) { + super(msg, ...nested); + } +} diff --git a/src/error/NotFoundError.ts b/src/error/NotFoundError.ts new file mode 100644 index 0000000..d5b179c --- /dev/null +++ b/src/error/NotFoundError.ts @@ -0,0 +1,7 @@ +import { BaseError } from 'noicejs'; + +export class NotFoundError extends BaseError { + constructor(msg = 'value not found', ...nested: Array) { + super(msg, ...nested); + } +} diff --git a/src/lib.ts b/src/lib.ts index c59d8d0..81d0c4f 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -1,5 +1,7 @@ +import { CONFIG_SCHEMA } from './schema'; import { VERSION_INFO } from './version'; export default { + CONFIG_SCHEMA, VERSION_INFO, }; diff --git a/src/schema.ts b/src/schema.ts index 2e11bba..283cb19 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -5,7 +5,6 @@ import { includeType } from './type/Include'; import { regexpType } from './type/Regexp'; import { streamType } from './type/Stream'; -export const CONFIG_ENV = 'SALTY_HOME'; export const CONFIG_SCHEMA = Schema.create([DEFAULT_SAFE_SCHEMA], [ envType, includeType, diff --git a/src/type/Env.ts b/src/type/Env.ts index ee281e4..d984a12 100644 --- a/src/type/Env.ts +++ b/src/type/Env.ts @@ -1,6 +1,6 @@ import { Type as YamlType } from 'js-yaml'; -import { NotFoundError } from '../../error/NotFoundError'; +import { NotFoundError } from '../error/NotFoundError'; export const envType = new YamlType('!env', { kind: 'scalar', diff --git a/src/type/Include.ts b/src/type/Include.ts index 753db30..a989a8d 100644 --- a/src/type/Include.ts +++ b/src/type/Include.ts @@ -1,9 +1,9 @@ import { existsSync, readFileSync, realpathSync } from 'fs'; import { SAFE_SCHEMA, safeLoad, Type as YamlType } from 'js-yaml'; -import { BaseError } from 'noicejs'; import { join } from 'path'; -import { NotFoundError } from '../../error/NotFoundError'; +import { InvalidArgumentError } from '../error/InvalidArgumentError'; +import { NotFoundError } from '../error/NotFoundError'; // work around the circular dependency by setting the schema later export const includeSchema = { @@ -33,7 +33,7 @@ export const includeType = new YamlType('!include', { schema: includeSchema.schema, }); } catch (err) { - throw new BaseError('error including file', err); + throw new InvalidArgumentError('error including file', err); } }, }); diff --git a/src/type/Regexp.ts b/src/type/Regexp.ts index 6879556..c3c71f1 100644 --- a/src/type/Regexp.ts +++ b/src/type/Regexp.ts @@ -1,7 +1,7 @@ import { Type as YamlType } from 'js-yaml'; import { isNil } from 'lodash'; -import { InvalidArgumentError } from '../../error/InvalidArgumentError'; +import { InvalidArgumentError } from '../error/InvalidArgumentError'; export const REGEXP_REGEXP = /^\/(.+)\/([gimsuy]*)$/; diff --git a/src/type/Stream.ts b/src/type/Stream.ts index 884a256..bb079d0 100644 --- a/src/type/Stream.ts +++ b/src/type/Stream.ts @@ -1,6 +1,6 @@ import { Type as YamlType } from 'js-yaml'; -import { NotFoundError } from '../../error/NotFoundError'; +import { NotFoundError } from '../error/NotFoundError'; const ALLOWED_STREAMS = new Set([ 'stdout', diff --git a/test/type/TestEnv.ts b/test/type/TestEnv.ts new file mode 100644 index 0000000..2e978e8 --- /dev/null +++ b/test/type/TestEnv.ts @@ -0,0 +1,22 @@ +import { expect } from 'chai'; + +import { NotFoundError } from '../../src/error/NotFoundError'; +import { envType } from '../../src/type/Env'; +import { VERSION_INFO } from '../../src/version'; +import { describeLeaks, itLeaks } from '../helpers/async'; + +describeLeaks('env config type', async () => { + itLeaks('should throw on missing variables', async () => { + expect(() => { + envType.resolve('DOES_NOT_EXIST_'); + }).to.throw(NotFoundError); + }); + + itLeaks('should resolve existing variables', async () => { + expect(envType.resolve('CI_COMMIT_SHA')).to.equal(true); + }); + + itLeaks('should construct a value from variables', async () => { + expect(envType.construct('CI_COMMIT_SHA')).to.equal(VERSION_INFO.git.commit); + }); +}); diff --git a/test/type/TestInclude.ts b/test/type/TestInclude.ts new file mode 100644 index 0000000..bebc357 --- /dev/null +++ b/test/type/TestInclude.ts @@ -0,0 +1,31 @@ +import { expect } from 'chai'; +import { join } from 'path'; + +import { InvalidArgumentError } from '../../src/error/InvalidArgumentError'; +import { NotFoundError } from '../../src/error/NotFoundError'; +import { includeType } from '../../src/type/Include'; +import { describeLeaks, itLeaks } from '../helpers/async'; + +const TEST_ROOT = '../test/type'; + +describeLeaks('include config type', async () => { + itLeaks('should resolve existing files', async () => { + expect(includeType.resolve(join(TEST_ROOT, 'include.yml'))).to.equal(true); + }); + + itLeaks('should throw when resolving missing files', async () => { + expect(() => { + includeType.resolve(join(TEST_ROOT, 'missing.yml')); + }).to.throw(NotFoundError); + }); + + itLeaks('should construct data from file', async () => { + expect(includeType.construct(join(TEST_ROOT, 'include.yml'))).to.equal('test'); + }); + + itLeaks('should throw when constructing missing files', async () => { + expect(() => { + includeType.construct(join(TEST_ROOT, 'missing.yml')); + }).to.throw(InvalidArgumentError); + }); +}); diff --git a/test/type/TestRegexp.ts b/test/type/TestRegexp.ts new file mode 100644 index 0000000..5a1c1b0 --- /dev/null +++ b/test/type/TestRegexp.ts @@ -0,0 +1,27 @@ +import { expect } from 'chai'; + +import { regexpType } from '../../src/type/Regexp'; +import { describeLeaks, itLeaks } from '../helpers/async'; + +describeLeaks('regexp config type', async () => { + itLeaks('match slashed strings', async () => { + expect(regexpType.resolve('/foo/')).to.equal(true); + }); + + itLeaks('should match flags', async () => { + const regexp: RegExp = regexpType.construct('/foo/g'); + expect(regexp.flags).to.equal('g'); + }); + + itLeaks('should not match bare strings', async () => { + expect(regexpType.resolve('foo')).to.equal(false); + }); + + itLeaks('should not match invalid flags', async () => { + expect(regexpType.resolve('/foo/notrealflags')).to.equal(false); + }); + + itLeaks('should not match regex embedded in a longer string', async () => { + expect(regexpType.resolve('some/regex/with-padding')).to.equal(false); + }); +}); diff --git a/test/type/include.yml b/test/type/include.yml new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/test/type/include.yml @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 525fc5f..05a1ff4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -239,11 +239,21 @@ resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@types/js-yaml@^3.12.1": + version "3.12.1" + resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/js-yaml/-/js-yaml-3.12.1.tgz#5c6f4a1eabca84792fbd916f0cb40847f123c656" + integrity sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA== + "@types/json-schema@^7.0.3": version "7.0.3" resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A== +"@types/lodash@^4.14.146": + version "4.14.146" + resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/lodash/-/lodash-4.14.146.tgz#de0d2c8610012f12a6a796455054cbc654f8fecf" + integrity sha512-JzJcmQ/ikHSv7pbvrVNKJU5j9jL9VLf3/gqs048CEnBVVVEv4kve3vLxoPHGvclutS+Il4SBIuQQ087m1eHffw== + "@types/mocha@^5.2.7": version "5.2.7" resolved "https://artifacts.apextoaster.com/repository/group-npm/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" @@ -2281,6 +2291,11 @@ node-environment-flags@1.0.5: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" +noicejs@^3.0.0: + version "3.0.0" + resolved "https://artifacts.apextoaster.com/repository/group-npm/noicejs/-/noicejs-3.0.0.tgz#7fca4008c6c9c78ee4ca4b04bb179b424c23cb4c" + integrity sha512-DZFqSAgsh3nxDfHi/ncgLFtN2G2iWTJdOZQU6LQILAVcs7RX57OSR1+FS0WVL8FAZfd47WC4xhN8EGrNzKuSvQ== + normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5: version "2.5.0" resolved "https://artifacts.apextoaster.com/repository/group-npm/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"