diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..79b0e7e --- /dev/null +++ b/.npmignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* + +# Dependency directory +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# Typescript stuff +typings +build +docs +coverage +test +tslint.json +tsconfig + +*.ts +!*.d.ts +*.js.map +*.spec.js +*.spec.ts +*.spec.d.ts +*.spec.js.map diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7af56ba --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: node_js +node_js: +- '6' +- '7' + +script: npm test + +after_script: npm install coveralls@^2.11.9 && cat ./coverage/lcov-mapped.info | coveralls + +# before_deploy: +# - npm run bootstrap +# - tsc --outDir . diff --git a/Resolver.ts b/Resolver.ts new file mode 100644 index 0000000..d98c7b4 --- /dev/null +++ b/Resolver.ts @@ -0,0 +1,73 @@ +import { DuplicateTypeRegistration } from './errors'; +import { SerializableType } from './SerializableType'; + +/** + * TODO + * + * @export + * @class Resolver + */ +export class Resolver { + private static _instance: Resolver; + + public static get instance(): Resolver { + if (!Resolver._instance) { + Resolver._instance = new Resolver(); + } + return Resolver._instance; + } + + private types: { [name: string]: SerializableType } = {}; + + private constructor() { } + + /** + * TODO + * + * @memberOf Resolver + */ + public reset(): void { + this.types = {}; + } + + /** + * TODO + * + * @param {SerializableType} model + * + * @memberOf Resolver + */ + public addType(type: SerializableType): void { + if (this.types[type.name]) { + throw new DuplicateTypeRegistration(type.name); + } + + this.types[type.name] = type; + } + + /** + * TODO + * + * @param {string} name + * @returns {(SerializableType | undefined)} + * + * @memberOf Resolver + */ + public getType(name: string): SerializableType | undefined { + return this.types[name]; + } + + /** + * + * + * @param {*} obj + * @returns {(SerializableType | undefined)} + * + * @memberOf Resolver + */ + public getTypeByObject(obj: any): SerializableType | undefined { + return Object.keys(this.types) + .map(key => this.types[key]) + .find(o => obj.constructor === o.ctor); + } +} diff --git a/Serializable.ts b/Serializable.ts new file mode 100644 index 0000000..a762c1a --- /dev/null +++ b/Serializable.ts @@ -0,0 +1,35 @@ +import { SerializableType } from './SerializableType'; +import { Resolver } from './Resolver'; +import { TransportObject } from './TransportObject'; +import { NoFactoryProvidedError, NoNameProvided } from './errors'; + +/** + * + * + * @export + * @interface SerializableOptions + */ +export interface SerializableOptions { + name?: string; + factory?: (json: TransportObject) => T; +}; + +/** + * TODO + * + * @export + * @param {SerializableOptions} [options] + * @returns {ClassDecorator} + */ +export function Serializable(options?: SerializableOptions): ClassDecorator { + return (type: Function) => { + const name = options && options.name ? options.name : (type as any).name; + if (type.length > 0 && (!options || !options.factory)) { + throw new NoFactoryProvidedError(name); + } + if (!name) { + throw new NoNameProvided(); + } + Resolver.instance.addType(new SerializableType(name, type, options ? options.factory : undefined)); + }; +} diff --git a/SerializableType.ts b/SerializableType.ts new file mode 100644 index 0000000..dc7e859 --- /dev/null +++ b/SerializableType.ts @@ -0,0 +1,9 @@ +/** + * TODO + * + * @export + * @class SerializableType + */ +export class SerializableType { + constructor(public name: string, public ctor: Function, public factory?: (json: any) => T) { } +} diff --git a/TransportObject.ts b/TransportObject.ts new file mode 100644 index 0000000..1be53fa --- /dev/null +++ b/TransportObject.ts @@ -0,0 +1,4 @@ +export type TransportObject = { + __type: string; + __value: any; +}; diff --git a/TsSerializer.ts b/TsSerializer.ts new file mode 100644 index 0000000..bdd0cc4 --- /dev/null +++ b/TsSerializer.ts @@ -0,0 +1,202 @@ +import { ReferenceObjectNotFoundError, TypeNotRegisteredError } from './errors'; +import { Resolver } from './Resolver'; +import { TransportObject } from './TransportObject'; + +type RerializationReferences = { + [type: string]: any[]; +}; + +/** + * + * + * @class ReferencedObject + */ +class ReferencedObject { + public type: string; + public index: number; + + constructor(referenceInfo: { type: string, index: number }) { + this.type = referenceInfo.type; + this.index = referenceInfo.index; + } +} + +/** + * TODO + * + * @export + * @class TsSerializer + */ +export class TsSerializer { + private references: RerializationReferences; + + /** + * TODO + * + * @readonly + * @type {Resolver} + * @memberOf TsSerializer + */ + public get resolver(): Resolver { + return Resolver.instance; + } + + /** + * + * + * @param {*} objectOrArray + * @returns {string} + * + * @memberOf TsSerializer + */ + public serialize(objectOrArray: any): string { + this.references = {}; + let serialized: any; + if (objectOrArray.constructor === Array) { + serialized = objectOrArray.map(o => this.serializeObject(o)); + } else { + serialized = this.serializeObject(objectOrArray); + } + // this.resolveObjectReferences(serialized); + return JSON.stringify(serialized); + } + + /** + * + * + * @template T + * @param {string} json + * @returns {T} + * + * @memberOf TsSerializer + */ + public deserialize(json: string): T { + this.references = {}; + const parsed = JSON.parse(json); + let deserialized: any; + if (parsed.constructor === Array) { + deserialized = parsed.map(o => this.deserializeObject(o)); + } else { + deserialized = this.deserializeObject(parsed); + } + this.resolveReferences(deserialized); + return deserialized; + } + + /** + */ + private serializeObject(obj: any): TransportObject { + if (obj.constructor === Date) { + return { + __type: 'Date', + __value: obj + }; + } else if (typeof obj === 'object') { + const type = this.resolver.getTypeByObject(obj), + transformedObj: any = {}; + if (!type) { + throw new TypeNotRegisteredError(obj); + } + + if (!this.references[type.name]) { + this.references[type.name] = []; + } + + const alreadyIndexed = this.references[type.name].find(o => o === obj); + + if (alreadyIndexed) { + return { + __type: 'ref', + __value: { + type: type.name, + index: this.references[type.name].indexOf(obj) + } + }; + } else { + this.references[type.name].push(obj); + } + + for (let property of Object.keys(obj).filter(o => typeof obj[o] !== 'function')) { + transformedObj[property] = this.serializeObject(obj[property]); + } + return { + __type: type.name, + __value: transformedObj + }; + } else { + return { + __type: obj.constructor.name, + __value: obj + }; + } + } + + /** + * + * + * @private + * @param {TransportObject} obj + * @returns {*} + * + * @memberOf TsSerializer + */ + private deserializeObject(obj: TransportObject): any { + switch (obj.__type) { + case 'Date': + return new Date(obj.__value); + case 'Number': + return Number(obj.__value); + case 'String': + return String(obj.__value); + case 'Boolean': + return Boolean(obj.__value); + case 'ref': + return new ReferencedObject(obj.__value); + default: + const type = this.resolver.getType(obj.__type), + transformedObj: any = {}; + + if (!type) { + throw new TypeNotRegisteredError(obj); + } + + for (let property of Object.keys(obj.__value)) { + transformedObj[property] = this.deserializeObject(obj.__value[property]); + } + + const createdObj = type.factory ? + type.factory(transformedObj) : + Object.assign(new (type as any).ctor(), transformedObj); + + if (!this.references[type.name]) { + this.references[type.name] = []; + } + this.references[type.name].push(createdObj); + + return createdObj; + } + } + + /** + * + * + * @private + * @param {*} obj + * @returns {void} + * + * @memberOf TsSerializer + */ + private resolveReferences(obj: any): void { + for (let property of Object.keys(obj)) { + const prop = obj[property]; + if (prop instanceof ReferencedObject) { + if (!this.references[prop.type]) { + throw new ReferenceObjectNotFoundError(); + } + obj[property] = this.references[prop.type][prop.index]; + } else if (typeof prop === 'object') { + this.resolveReferences(prop); + } + } + } +} diff --git a/errors.ts b/errors.ts new file mode 100644 index 0000000..1ad3e80 --- /dev/null +++ b/errors.ts @@ -0,0 +1,71 @@ +/** + * TODO + * + * @export + * @class NoFactoryProvidedError + */ +export class NoFactoryProvidedError { + public message: string; + + constructor(typeName: string) { + this.message = `The constructor of "${typeName}" is not parameterless, please provide a factory function.`; + } +} + +/** + * TODO + * + * @export + * @class DuplicateTypeRegistration + */ +export class DuplicateTypeRegistration { + public message: string; + + constructor(typeName: string) { + this.message = `The type "${typeName}" is duplicated.`; + } +} + +/** + * TODO + * + * @export + * @class NoNameProvided + */ +export class NoNameProvided { + public message: string; + + constructor() { + this.message = `A type has no name provided, either function.name is not possible (ES5? IE?)` + + ` or no name parameter was provided.`; + } +} + +/** + * TODO + * + * @export + * @class TypeNotRegisteredError + */ +export class TypeNotRegisteredError { + public message: string; + + constructor(obj: any) { + this.message = `The object "${obj}" is not found in the type registration. Did you forget the @Serializable` + + ` decorator?`; + } +} + +/** + * + * + * @export + * @class ReferenceObjectNotFoundError + */ +export class ReferenceObjectNotFoundError { + public message: string; + + constructor() { + this.message = 'The reference object was not found in the previous deserialized objects'; + } +} diff --git a/index.ts b/index.ts index e69de29..fa05c41 100644 --- a/index.ts +++ b/index.ts @@ -0,0 +1,5 @@ +import 'reflect-metadata'; + +export * from './Serializable'; +export * from './errors'; +export * from './TsSerializer'; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json new file mode 100644 index 0000000..ce8c762 --- /dev/null +++ b/npm-shrinkwrap.json @@ -0,0 +1,1569 @@ +{ + "name": "ts-serializer", + "version": "0.1.0", + "dependencies": { + "@types/chai": { + "version": "3.4.34", + "from": "@types/chai@latest", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-3.4.34.tgz", + "dev": true + }, + "@types/mocha": { + "version": "2.2.39", + "from": "@types/mocha@latest", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.39.tgz", + "dev": true + }, + "@types/node": { + "version": "7.0.5", + "from": "@types/node@latest", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.5.tgz", + "dev": true + }, + "@types/reflect-metadata": { + "version": "0.0.5", + "from": "@types/reflect-metadata@latest", + "resolved": "https://registry.npmjs.org/@types/reflect-metadata/-/reflect-metadata-0.0.5.tgz", + "dev": true + }, + "abbrev": { + "version": "1.0.9", + "from": "abbrev@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "dev": true + }, + "align-text": { + "version": "0.1.4", + "from": "align-text@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "dev": true + }, + "ansi-align": { + "version": "1.1.0", + "from": "ansi-align@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "from": "ansi-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "from": "ansi-styles@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "dev": true + }, + "argparse": { + "version": "1.0.9", + "from": "argparse@>=1.0.7 <2.0.0", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "from": "array-differ@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "from": "array-find-index@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "from": "array-union@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "from": "array-uniq@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "from": "arrify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "dev": true + }, + "assertion-error": { + "version": "1.0.2", + "from": "assertion-error@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", + "dev": true + }, + "async": { + "version": "1.5.2", + "from": "async@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "dev": true + }, + "babel-code-frame": { + "version": "6.22.0", + "from": "babel-code-frame@>=6.20.0 <7.0.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "dev": true + }, + "balanced-match": { + "version": "0.4.2", + "from": "balanced-match@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "dev": true + }, + "beeper": { + "version": "1.1.1", + "from": "beeper@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "dev": true + }, + "boxen": { + "version": "0.6.0", + "from": "boxen@>=0.6.0 <0.7.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz", + "dev": true + }, + "brace-expansion": { + "version": "1.1.6", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", + "dev": true + }, + "browser-stdout": { + "version": "1.3.0", + "from": "browser-stdout@1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "dev": true + }, + "buffer-shims": { + "version": "1.0.0", + "from": "buffer-shims@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "from": "builtin-modules@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "dev": true + }, + "camelcase": { + "version": "2.1.1", + "from": "camelcase@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "from": "camelcase-keys@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "dev": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "from": "capture-stack-trace@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "dev": true + }, + "center-align": { + "version": "0.1.3", + "from": "center-align@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "dev": true, + "optional": true + }, + "chai": { + "version": "3.5.0", + "from": "chai@latest", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "from": "chalk@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "dev": true + }, + "cli-boxes": { + "version": "1.0.0", + "from": "cli-boxes@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "from": "cliui@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "dev": true, + "optional": true, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "from": "wordwrap@0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "dev": true, + "optional": true + } + } + }, + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "from": "code-point-at@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "dev": true + }, + "colors": { + "version": "1.1.2", + "from": "colors@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "dev": true + }, + "commander": { + "version": "2.9.0", + "from": "commander@2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "dev": true + }, + "configstore": { + "version": "2.1.0", + "from": "configstore@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "dev": true + }, + "create-error-class": { + "version": "3.0.2", + "from": "create-error-class@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "from": "currently-unhandled@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "dev": true + }, + "dateformat": { + "version": "1.0.12", + "from": "dateformat@>=1.0.11 <2.0.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "dev": true + }, + "debug": { + "version": "2.2.0", + "from": "debug@2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "from": "decamelize@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "dev": true + }, + "deep-eql": { + "version": "0.1.3", + "from": "deep-eql@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "dev": true, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "from": "type-detect@0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "dev": true + } + } + }, + "deep-extend": { + "version": "0.4.1", + "from": "deep-extend@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "from": "deep-is@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "dev": true + }, + "del": { + "version": "2.2.2", + "from": "del@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "dev": true + }, + "del-cli": { + "version": "0.2.1", + "from": "del-cli@latest", + "resolved": "https://registry.npmjs.org/del-cli/-/del-cli-0.2.1.tgz", + "dev": true + }, + "diff": { + "version": "1.4.0", + "from": "diff@1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "dev": true + }, + "dot-prop": { + "version": "3.0.0", + "from": "dot-prop@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "dev": true + }, + "duplexer2": { + "version": "0.1.4", + "from": "duplexer2@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "dev": true + }, + "error-ex": { + "version": "1.3.0", + "from": "error-ex@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.0.tgz", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "dev": true + }, + "escodegen": { + "version": "1.8.1", + "from": "escodegen@>=1.8.0 <1.9.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "dev": true + }, + "esprima": { + "version": "2.7.3", + "from": "esprima@>=2.7.0 <2.8.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "dev": true + }, + "estraverse": { + "version": "1.9.3", + "from": "estraverse@>=1.9.1 <2.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "from": "esutils@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "dev": true + }, + "fancy-log": { + "version": "1.3.0", + "from": "fancy-log@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.0.tgz", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "from": "fast-levenshtein@>=2.0.4 <2.1.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "dev": true + }, + "filled-array": { + "version": "1.1.0", + "from": "filled-array@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "from": "find-up@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "dev": true + }, + "findup-sync": { + "version": "0.3.0", + "from": "findup-sync@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", + "dev": true, + "dependencies": { + "glob": { + "version": "5.0.15", + "from": "glob@>=5.0.0 <5.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "dev": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "from": "fs.realpath@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "dev": true + }, + "glob": { + "version": "7.1.1", + "from": "glob@>=7.0.3 <8.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "dev": true + }, + "globby": { + "version": "5.0.0", + "from": "globby@>=5.0.0 <6.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "dev": true + }, + "glogg": { + "version": "1.0.0", + "from": "glogg@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", + "dev": true + }, + "got": { + "version": "5.7.1", + "from": "got@>=5.0.0 <6.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "from": "graceful-fs@>=4.1.2 <5.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "dev": true + }, + "graceful-readlink": { + "version": "1.0.1", + "from": "graceful-readlink@>=1.0.0", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "dev": true + }, + "growl": { + "version": "1.9.2", + "from": "growl@1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "dev": true + }, + "gulp-util": { + "version": "3.0.7", + "from": "gulp-util@3.0.7", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.7.tgz", + "dev": true, + "dependencies": { + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "dev": true + } + } + }, + "gulplog": { + "version": "1.0.0", + "from": "gulplog@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "dev": true + }, + "handlebars": { + "version": "4.0.6", + "from": "handlebars@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.6.tgz", + "dev": true, + "dependencies": { + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.4 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dev": true + } + } + }, + "has-ansi": { + "version": "2.0.0", + "from": "has-ansi@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "dev": true + }, + "has-flag": { + "version": "1.0.0", + "from": "has-flag@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "dev": true + }, + "has-gulplog": { + "version": "0.1.0", + "from": "has-gulplog@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "dev": true + }, + "hosted-git-info": { + "version": "2.2.0", + "from": "hosted-git-info@>=2.1.4 <3.0.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.2.0.tgz", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "from": "imurmurhash@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "from": "indent-string@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "dev": true + }, + "ini": { + "version": "1.3.4", + "from": "ini@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "from": "is-arrayish@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "dev": true + }, + "is-buffer": { + "version": "1.1.4", + "from": "is-buffer@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "from": "is-builtin-module@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "dev": true + }, + "is-npm": { + "version": "1.0.0", + "from": "is-npm@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "from": "is-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "from": "is-path-cwd@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "from": "is-path-in-cwd@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "dev": true + }, + "is-path-inside": { + "version": "1.0.0", + "from": "is-path-inside@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", + "dev": true + }, + "is-redirect": { + "version": "1.0.0", + "from": "is-redirect@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "dev": true + }, + "is-retry-allowed": { + "version": "1.1.0", + "from": "is-retry-allowed@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "from": "is-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "from": "is-utf8@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "from": "isarray@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "dev": true + }, + "isexe": { + "version": "1.1.2", + "from": "isexe@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-1.1.2.tgz", + "dev": true + }, + "istanbul": { + "version": "0.4.5", + "from": "istanbul@latest", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "dev": true, + "dependencies": { + "glob": { + "version": "5.0.15", + "from": "glob@>=5.0.15 <6.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "from": "supports-color@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "dev": true + } + } + }, + "js-tokens": { + "version": "3.0.1", + "from": "js-tokens@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", + "dev": true + }, + "js-yaml": { + "version": "3.8.1", + "from": "js-yaml@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.1.tgz", + "dev": true, + "dependencies": { + "esprima": { + "version": "3.1.3", + "from": "esprima@>=3.1.1 <4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "dev": true + } + } + }, + "json3": { + "version": "3.3.2", + "from": "json3@3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "dev": true + }, + "kind-of": { + "version": "3.1.0", + "from": "kind-of@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.1.0.tgz", + "dev": true + }, + "latest-version": { + "version": "2.0.0", + "from": "latest-version@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz", + "dev": true + }, + "lazy-cache": { + "version": "1.0.4", + "from": "lazy-cache@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "dev": true, + "optional": true + }, + "lazy-req": { + "version": "1.1.0", + "from": "lazy-req@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz", + "dev": true + }, + "levn": { + "version": "0.3.0", + "from": "levn@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "dev": true + }, + "load-json-file": { + "version": "1.1.0", + "from": "load-json-file@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "dev": true + }, + "lodash._baseassign": { + "version": "3.2.0", + "from": "lodash._baseassign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "dev": true + }, + "lodash._basecopy": { + "version": "3.0.1", + "from": "lodash._basecopy@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "dev": true + }, + "lodash._basecreate": { + "version": "3.0.3", + "from": "lodash._basecreate@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "from": "lodash._basetostring@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "from": "lodash._basevalues@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "from": "lodash._getnative@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "from": "lodash._isiterateecall@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "from": "lodash._reescape@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "from": "lodash._reevaluate@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "from": "lodash._reinterpolate@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "dev": true + }, + "lodash._root": { + "version": "3.0.1", + "from": "lodash._root@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "dev": true + }, + "lodash.create": { + "version": "3.1.1", + "from": "lodash.create@3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "dev": true + }, + "lodash.escape": { + "version": "3.2.0", + "from": "lodash.escape@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "dev": true + }, + "lodash.isarguments": { + "version": "3.1.0", + "from": "lodash.isarguments@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "from": "lodash.isarray@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "from": "lodash.keys@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "from": "lodash.restparam@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "from": "lodash.template@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "dev": true + }, + "lodash.templatesettings": { + "version": "3.1.1", + "from": "lodash.templatesettings@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "dev": true + }, + "longest": { + "version": "1.0.1", + "from": "longest@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "from": "loud-rejection@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "dev": true + }, + "lowercase-keys": { + "version": "1.0.0", + "from": "lowercase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "dev": true + }, + "meow": { + "version": "3.7.0", + "from": "meow@>=3.6.0 <4.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "dev": true + }, + "minimatch": { + "version": "3.0.3", + "from": "minimatch@>=3.0.2 <4.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dev": true, + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "dev": true + } + } + }, + "mocha": { + "version": "3.2.0", + "from": "mocha@latest", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.2.0.tgz", + "dev": true, + "dependencies": { + "glob": { + "version": "7.0.5", + "from": "glob@7.0.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", + "dev": true + }, + "supports-color": { + "version": "3.1.2", + "from": "supports-color@3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "dev": true + } + } + }, + "mocha-lcov-reporter": { + "version": "1.2.0", + "from": "mocha-lcov-reporter@latest", + "resolved": "https://registry.npmjs.org/mocha-lcov-reporter/-/mocha-lcov-reporter-1.2.0.tgz", + "dev": true + }, + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "dev": true + }, + "multipipe": { + "version": "0.1.2", + "from": "multipipe@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "dev": true, + "dependencies": { + "duplexer2": { + "version": "0.0.2", + "from": "duplexer2@0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "from": "readable-stream@>=1.1.9 <1.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "dev": true + } + } + }, + "node-status-codes": { + "version": "1.0.0", + "from": "node-status-codes@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", + "dev": true + }, + "nopt": { + "version": "3.0.6", + "from": "nopt@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "dev": true + }, + "normalize-package-data": { + "version": "2.3.5", + "from": "normalize-package-data@>=2.3.4 <3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.5.tgz", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "from": "object-assign@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "dev": true + }, + "once": { + "version": "1.4.0", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "dev": true + }, + "optimist": { + "version": "0.6.1", + "from": "optimist@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "dev": true, + "dependencies": { + "minimist": { + "version": "0.0.10", + "from": "minimist@>=0.0.1 <0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "from": "optionator@>=0.8.1 <0.9.0", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "from": "os-homedir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "from": "os-tmpdir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "dev": true + }, + "osenv": { + "version": "0.1.4", + "from": "osenv@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "dev": true + }, + "package-json": { + "version": "2.4.0", + "from": "package-json@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", + "dev": true + }, + "parse-json": { + "version": "2.2.0", + "from": "parse-json@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "from": "path-exists@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "from": "path-is-inside@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "from": "path-type@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "dev": true + }, + "pify": { + "version": "2.3.0", + "from": "pify@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "from": "pinkie@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "from": "pinkie-promise@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "from": "prelude-ls@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "from": "prepend-http@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "from": "process-nextick-args@>=1.0.6 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "dev": true + }, + "rc": { + "version": "1.1.6", + "from": "rc@>=1.1.6 <2.0.0", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz", + "dev": true + }, + "read-all-stream": { + "version": "3.1.0", + "from": "read-all-stream@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "from": "read-pkg@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "dev": true + }, + "read-pkg-up": { + "version": "1.0.1", + "from": "read-pkg-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "dev": true + }, + "readable-stream": { + "version": "2.2.2", + "from": "readable-stream@>=2.0.5 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz", + "dev": true + }, + "redent": { + "version": "1.0.0", + "from": "redent@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "dev": true + }, + "reflect-metadata": { + "version": "0.1.9", + "from": "reflect-metadata@latest", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.9.tgz" + }, + "registry-auth-token": { + "version": "3.1.0", + "from": "registry-auth-token@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.1.0.tgz", + "dev": true + }, + "registry-url": { + "version": "3.1.0", + "from": "registry-url@>=3.0.3 <4.0.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "dev": true + }, + "remap-istanbul": { + "version": "0.9.1", + "from": "remap-istanbul@latest", + "resolved": "https://registry.npmjs.org/remap-istanbul/-/remap-istanbul-0.9.1.tgz", + "dev": true, + "dependencies": { + "source-map": { + "version": "0.5.6", + "from": "source-map@>=0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "dev": true + } + } + }, + "repeat-string": { + "version": "1.6.1", + "from": "repeat-string@>=1.5.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "from": "repeating@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "dev": true + }, + "resolve": { + "version": "1.1.7", + "from": "resolve@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "from": "right-align@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "dev": true, + "optional": true + }, + "rimraf": { + "version": "2.5.4", + "from": "rimraf@>=2.2.8 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", + "dev": true + }, + "semver": { + "version": "5.3.0", + "from": "semver@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0||>=4.0.0 <5.0.0||>=5.0.0 <6.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "dev": true + }, + "semver-diff": { + "version": "2.1.0", + "from": "semver-diff@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "from": "signal-exit@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "dev": true + }, + "slide": { + "version": "1.1.6", + "from": "slide@>=1.1.5 <2.0.0", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "dev": true + }, + "source-map": { + "version": "0.2.0", + "from": "source-map@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "dev": true, + "optional": true + }, + "sparkles": { + "version": "1.0.0", + "from": "sparkles@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", + "dev": true + }, + "spdx-correct": { + "version": "1.0.2", + "from": "spdx-correct@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "dev": true + }, + "spdx-expression-parse": { + "version": "1.0.4", + "from": "spdx-expression-parse@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "dev": true + }, + "spdx-license-ids": { + "version": "1.2.2", + "from": "spdx-license-ids@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "from": "sprintf-js@>=1.0.2 <1.1.0", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "from": "string-width@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "from": "strip-ansi@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "from": "strip-bom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "from": "strip-indent@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "dev": true + }, + "strip-json-comments": { + "version": "1.0.4", + "from": "strip-json-comments@>=1.0.4 <1.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "from": "supports-color@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "dev": true + }, + "through2": { + "version": "2.0.1", + "from": "through2@2.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.1.tgz", + "dev": true, + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "from": "readable-stream@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "dev": true + } + } + }, + "time-stamp": { + "version": "1.0.1", + "from": "time-stamp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.0.1.tgz", + "dev": true + }, + "timed-out": { + "version": "3.1.3", + "from": "timed-out@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "from": "trim-newlines@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "dev": true + }, + "tslib": { + "version": "1.5.0", + "from": "tslib@latest", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.5.0.tgz" + }, + "tslint": { + "version": "4.4.2", + "from": "tslint@latest", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-4.4.2.tgz", + "dev": true, + "dependencies": { + "diff": { + "version": "3.2.0", + "from": "diff@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "dev": true + } + } + }, + "tslint-jsdoc-rules": { + "version": "0.1.2", + "from": "tslint-jsdoc-rules@latest", + "resolved": "https://registry.npmjs.org/tslint-jsdoc-rules/-/tslint-jsdoc-rules-0.1.2.tgz", + "dev": true, + "dependencies": { + "diff": { + "version": "2.2.3", + "from": "diff@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", + "dev": true + }, + "tslint": { + "version": "3.15.1", + "from": "tslint@>=3.2.2 <4.0.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-3.15.1.tgz", + "dev": true + } + } + }, + "type-check": { + "version": "0.3.2", + "from": "type-check@>=0.3.2 <0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "dev": true + }, + "type-detect": { + "version": "1.0.0", + "from": "type-detect@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "dev": true + }, + "typescript": { + "version": "2.1.6", + "from": "typescript@latest", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.1.6.tgz", + "dev": true + }, + "uglify-js": { + "version": "2.7.5", + "from": "uglify-js@>=2.6.0 <3.0.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", + "dev": true, + "optional": true, + "dependencies": { + "async": { + "version": "0.2.10", + "from": "async@>=0.2.6 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.5.6", + "from": "source-map@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "dev": true, + "optional": true + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "from": "uglify-to-browserify@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "dev": true, + "optional": true + }, + "underscore.string": { + "version": "3.3.4", + "from": "underscore.string@>=3.3.4 <4.0.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz", + "dev": true + }, + "unzip-response": { + "version": "1.0.2", + "from": "unzip-response@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", + "dev": true + }, + "update-notifier": { + "version": "1.0.3", + "from": "update-notifier@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz", + "dev": true + }, + "url-parse-lax": { + "version": "1.0.0", + "from": "url-parse-lax@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "dev": true + }, + "uuid": { + "version": "2.0.3", + "from": "uuid@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.1", + "from": "validate-npm-package-license@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "dev": true + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dev": true + }, + "which": { + "version": "1.2.12", + "from": "which@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.2.12.tgz", + "dev": true + }, + "widest-line": { + "version": "1.0.0", + "from": "widest-line@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "from": "window-size@0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "dev": true, + "optional": true + }, + "wordwrap": { + "version": "1.0.0", + "from": "wordwrap@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "dev": true + }, + "write-file-atomic": { + "version": "1.3.1", + "from": "write-file-atomic@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.1.tgz", + "dev": true + }, + "xdg-basedir": { + "version": "2.0.0", + "from": "xdg-basedir@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "from": "xtend@>=4.0.0 <4.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "from": "yargs@>=3.10.0 <3.11.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "dev": true, + "optional": true, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "dev": true, + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json index 8068de0..4a50c7e 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,16 @@ "version": "0.1.0", "description": "Object serialization made easy with decorators.", "main": "index.js", + "typings": "index.d.ts", "scripts": { "clean": "del build coverage", - "pretest": "echo 'stuff'", - "test": "echo \"Error: no test specified\" && exit 1", + "pretest": "npm run clean && tsc -p ./tsconfig/test.json", + "test": "istanbul cover -x \"**/*.spec.*\" _mocha --report lcovonly -- --ui bdd \"./build/test/**/*.spec.js\"", + "posttest": "remap-istanbul -i coverage/coverage.json -o coverage/lcov-mapped.info -t lcovonly", "predevelop": "npm run clean", - "develop": "tsc --sourceMap", + "develop": "tsc -p ./tsconfig/develop.json", "prebuild": "npm run clean", - "build": "tsc --outDir ." + "build": "tsc -p ./tsconfig/build.json" }, "keywords": [ "typescript", @@ -21,9 +23,17 @@ ], "author": "Christoph Bühler ", "license": "MIT", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/buehler/ts-serializer.git" + }, + "bugs": { + "url": "https://github.com/buehler/ts-serializer/issues" + }, "devDependencies": { "@types/chai": "^3.4.34", "@types/mocha": "^2.2.39", + "@types/node": "^7.0.5", "@types/reflect-metadata": "0.0.5", "chai": "^3.5.0", "del-cli": "^0.2.1", @@ -36,6 +46,7 @@ "typescript": "^2.1.6" }, "dependencies": { + "reflect-metadata": "^0.1.9", "tslib": "^1.5.0" } } diff --git a/test/Serializable.spec.ts b/test/Serializable.spec.ts new file mode 100644 index 0000000..c7dfb72 --- /dev/null +++ b/test/Serializable.spec.ts @@ -0,0 +1,76 @@ +import { DuplicateTypeRegistration, NoFactoryProvidedError, NoNameProvided } from '../errors'; +import { Serializable } from '../'; +import { Resolver } from '../Resolver'; +import 'reflect-metadata'; +import chai = require('chai'); + +const should = chai.should(); + +describe('Serializable decorator', () => { + + const resolver = Resolver.instance, + anyResolver: any = resolver; + + afterEach(() => { + resolver.reset(); + }); + + it('should add a type reference to the resolver.', () => { + @Serializable() + class Model { + } + + should.exist(anyResolver.types); + should.exist(anyResolver.types['Model']); + should.not.exist(anyResolver.types['Model'].factory); + }); + + it('should add a named type reference to the resolver.', () => { + @Serializable({ name: 'foobar' }) + class Model { + } + + should.exist(anyResolver.types); + should.exist(anyResolver.types['foobar']); + should.not.exist(anyResolver.types['foobar'].factory); + }); + + it('should add a type reference with factory to the resolver.', () => { + @Serializable({ factory: body => new Model(body) }) + class Model { + constructor(body: any) { } + } + + should.exist(anyResolver.types); + should.exist(anyResolver.types['Model']); + should.exist(anyResolver.types['Model'].factory); + }); + + it('should throw if a non standard constructor is used without factory.', () => { + const fn = () => { + @Serializable() + class Model { + constructor(foobar: string) { } + } + }; + + fn.should.throw(NoFactoryProvidedError); + }); + + it('should throw on a duplicate registration.', () => { + const fn = () => { + @Serializable() + class Model { + constructor() { } + } + + @Serializable({name: 'Model'}) + class Model2 { + constructor() { } + } + }; + + fn.should.throw(DuplicateTypeRegistration); + }); + +}); diff --git a/test/TsSerializer.spec.ts b/test/TsSerializer.spec.ts new file mode 100644 index 0000000..07a2b15 --- /dev/null +++ b/test/TsSerializer.spec.ts @@ -0,0 +1,932 @@ +import 'reflect-metadata'; +import { Serializable } from '../'; +import { TypeNotRegisteredError } from '../errors'; +import { Resolver } from '../Resolver'; +import { TsSerializer } from '../TsSerializer'; +import chai = require('chai'); + +const should = chai.should(); + +describe('TsSerializer', () => { + + const resolver = Resolver.instance, + serializer = new TsSerializer(); + + afterEach(() => { + resolver.reset(); + }); + + describe('serialize', () => { + + it('should serialize a primitive type normally', () => { + serializer.serialize(1337).should.equal('{"__type":"Number","__value":1337}'); + serializer.serialize('1337').should.equal('{"__type":"String","__value":"1337"}'); + serializer.serialize(true).should.equal('{"__type":"Boolean","__value":true}'); + }); + + it('should serialize a simple date to string', () => { + const date = new Date(2017, 1, 1, 15, 0, 0, 50); + serializer.serialize(date).should.equal('{"__type":"Date","__value":"2017-02-01T14:00:00.050Z"}'); + }); + + it('should serialize a simple model', () => { + @Serializable() + class Model { + public name: string; + } + + const obj = new Model(); + obj.name = 'foobar'; + + serializer.serialize(obj).should.equal( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}}' + ); + }); + + it('should serialize a model within a model', () => { + @Serializable() + class Model { + public name: string; + public submodel: Model; + } + + const obj = new Model(); + obj.name = 'foobar'; + obj.submodel = new Model(); + obj.submodel.name = 'submodel'; + + serializer.serialize(obj).should.equal( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"},' + + '"submodel":{"__type":"Model","__value":{"name":{"__type":"String","__value":"submodel"}}}}}' + ); + }); + + it('should serialize another model in a model', () => { + @Serializable() + class Model { + public name: string; + public submodel: Submodel; + } + + @Serializable() + class Submodel { + public name: string; + } + + const obj = new Model(); + obj.name = 'the model'; + obj.submodel = new Submodel(); + obj.submodel.name = 'the sub model'; + + serializer.serialize(obj).should.equal( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"the model"},"submodel":' + + '{"__type":"Submodel","__value":{"name":{"__type":"String","__value":"the sub model"}}}}}' + ); + }); + + it('should serialize primitive objects in an array', () => { + serializer + .serialize([1337, 'foobar', true]) + .should.equal( + '[{"__type":"Number","__value":1337},{"__type":"String"' + + ',"__value":"foobar"},{"__type":"Boolean","__value":true}]' + ); + }); + + it('should serialize a model in an array', () => { + @Serializable() + class Model { + public name: string; + } + + const obj = new Model(); + obj.name = 'foobar'; + + serializer.serialize([obj]).should.equal( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}}]' + ); + }); + + it('should serialize a model in a model in an array', () => { + @Serializable() + class Model { + public name: string; + public submodel: Model; + } + + const obj = new Model(); + obj.name = 'foobar'; + obj.submodel = new Model(); + obj.submodel.name = 'submodel'; + + serializer.serialize([obj]).should.equal( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"},' + + '"submodel":{"__type":"Model","__value":{"name":{"__type":"String","__value":"submodel"}}}}}]' + ); + }); + + it('should serialize multiple models in an array', () => { + @Serializable() + class Model { + public name: string; + } + + const obj = new Model(); + obj.name = 'foobar'; + + const obj2 = new Model(); + obj2.name = 'foobar2'; + + serializer.serialize([obj, obj2]).should.equal( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}},' + + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar2"}}}]' + ); + }); + + it('should serialize multiple models with submodels in an array', () => { + @Serializable() + class Model { + public name: string; + public submodel: Submodel; + } + + @Serializable() + class Submodel { + public name: string; + } + + const obj = new Model(); + obj.name = 'the model'; + obj.submodel = new Submodel(); + obj.submodel.name = 'the sub model'; + + const obj2 = new Model(); + obj2.name = 'the model 2'; + obj2.submodel = new Submodel(); + obj2.submodel.name = 'the sub model 2'; + + serializer.serialize([obj, obj2]).should.equal( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"the model"},"submodel":' + + '{"__type":"Submodel","__value":{"name":{"__type":"String","__value":"the sub model"}}}}},' + + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"the model 2"},"submodel":' + + '{"__type":"Submodel","__value":{"name":{"__type":"String","__value":"the sub model 2"}}}}}]' + ); + }); + + it('should serialize the same object as the same reference in an array', () => { + @Serializable() + class Model { + public name: string; + } + + const obj = new Model(); + obj.name = 'foobar'; + + serializer.serialize([obj, obj]).should.equal( + '[{"__type":"Model","__value":{"name":{"__type":"String",' + + '"__value":"foobar"}}},{"__type":"ref","__value":{"type":"Model","index":0}}]' + ); + }); + + it('should serialize the same object as the same reference in a submodule (recursive)', () => { + @Serializable() + class Model { + public name: string; + public mod: Model; + } + + const obj = new Model(); + obj.name = 'foobar'; + obj.mod = obj; + + serializer.serialize(obj).should.equal( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"},' + + '"mod":{"__type":"ref","__value":{"type":"Model","index":0}}}}' + ); + }); + + it('should serialize an object as a reference in another object (when in an array)', () => { + @Serializable() + class Model { + public name: string; + } + + @Serializable() + class OtherModel { + public model: Model; + } + + const obj = new Model(); + obj.name = 'foobar'; + const otherObj = new OtherModel(); + otherObj.model = obj; + + serializer.serialize([obj, otherObj]).should.equal( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}},' + + '{"__type":"OtherModel","__value":{"model":{"__type":"ref","__value":{"type":"Model","index":0}}}}]' + ); + }); + + it('should serialize an object as a reference in another object (when in an array reversed)', () => { + @Serializable() + class Model { + public name: string; + } + + @Serializable() + class OtherModel { + public model: Model; + } + + const obj = new Model(); + obj.name = 'foobar'; + const otherObj = new OtherModel(); + otherObj.model = obj; + + serializer.serialize([otherObj, obj]).should.equal( + '[{"__type":"OtherModel","__value":{"model":{"__type":"Model","__value":{"name":' + + '{"__type":"String","__value":"foobar"}}}}},{"__type":"ref","__value":{"type":"Model","index":0}}]' + ); + }); + + it('should serialize mutliple referenced models', () => { + @Serializable() + class A { + public b: B; + } + + @Serializable() + class B { + public c: C; + } + + @Serializable() + class C { + public a: A; + public d: D; + } + + @Serializable() + class D { + public b: B; + } + + const a = new A(); + a.b = new B(); + a.b.c = new C(); + a.b.c.a = a; + a.b.c.d = new D(); + a.b.c.d.b = a.b; + + serializer.serialize(a).should.equal( + '{"__type":"A","__value":{"b":{"__type":"B","__value":{"c":{"__type":"C","__value":{"a":' + + '{"__type":"ref","__value":{"type":"A","index":0}},"d":{"__type":"D",' + + '"__value":{"b":{"__type":"ref","__value":{"type":"B","index":0}}}}}}}}}}' + ); + }); + + it('should serialize a model with a constructor', () => { + @Serializable({ + factory: data => new Model(data.name) + }) + class Model { + constructor(public name: string) { } + } + + serializer.serialize(new Model('ctor model')).should.equal( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"ctor model"}}}' + ); + }); + + it('should serialize a model in a model with a constructor', () => { + @Serializable({ + factory: data => new Model() + }) + class Model { + constructor(public name: string = 'model', public sub: SubModel = new SubModel()) { } + } + + @Serializable({ + factory: data => new Model(data.name) + }) + class SubModel { + constructor(public name: string = 'sub model') { } + } + + serializer.serialize(new Model()).should.equal( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"model"},"sub":' + + '{"__type":"SubModel","__value":{"name":{"__type":"String","__value":"sub model"}}}}}' + ); + }); + + it('should throw when a model is not registered', () => { + class Model { + public name: string; + } + + const obj = new Model(), + fn = () => serializer.serialize(obj); + + fn.should.throw(TypeNotRegisteredError); + }); + + it('should throw when a submodel in a model is not registered', () => { + @Serializable() + class Model { + public name: string; + public mod: Submodel + } + + class Submodel { + public name: string; + } + + const obj = new Model(); + obj.name = 'foobar'; + obj.mod = new Submodel(); + obj.mod.name = 'submodel'; + + const fn = () => serializer.serialize(obj); + + fn.should.throw(TypeNotRegisteredError); + }); + + it('should throw when a model in an array is not registered', () => { + class Model { + public name: string; + } + + const obj = new Model(), + fn = () => serializer.serialize([obj]); + + fn.should.throw(TypeNotRegisteredError); + }); + + it('should throw when a submodel in a model in an array is not registered', () => { + @Serializable() + class Model { + public name: string; + public mod: Submodel + } + + class Submodel { + public name: string; + } + + const obj = new Model(); + obj.name = 'foobar'; + obj.mod = new Submodel(); + obj.mod.name = 'submodel'; + + const fn = () => serializer.serialize([obj]); + + fn.should.throw(TypeNotRegisteredError); + }); + + }); + + describe('deserialize', () => { + + it('should deserialize a primitive type', () => { + serializer.deserialize('{"__type":"Number","__value":1337}').should.equal(1337); + serializer.deserialize('{"__type":"String","__value":"1337"}').should.equal('1337'); + serializer.deserialize('{"__type":"Boolean","__value":true}').should.equal(true); + }); + + it('should deserialize a simple date', () => { + serializer + .deserialize('{"__type":"Date","__value":"2017-02-01T14:00:00.050Z"}') + .getMilliseconds() + .should.equal(50); + }); + + it('should deserialize a simple model', () => { + @Serializable() + class Model { + public name: string; + } + + const deserialized = serializer.deserialize( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}}' + ); + + deserialized.should.be.an.instanceof(Model); + deserialized.name.should.equal('foobar'); + }); + + it('should deserialize a model within a model', () => { + @Serializable() + class Model { + public name: string; + public submodel: Model; + } + + const deserialized = serializer.deserialize( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"},' + + '"submodel":{"__type":"Model","__value":{"name":{"__type":"String","__value":"submodel"}}}}}' + ); + + deserialized.should.be.an.instanceof(Model); + deserialized.submodel.should.be.an.instanceof(Model); + deserialized.submodel.name.should.equal('submodel'); + }); + + it('should deserialize another model within a model', () => { + @Serializable() + class Model { + public name: string; + public submodel: Submodel; + } + + @Serializable() + class Submodel { + public name: string; + } + + const deserialized = serializer.deserialize( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"the model"},"submodel":' + + '{"__type":"Submodel","__value":{"name":{"__type":"String","__value":"the sub model"}}}}}' + ); + + deserialized.should.be.an.instanceof(Model); + deserialized.submodel.should.be.an.instanceof(Submodel); + deserialized.submodel.name.should.equal('the sub model'); + }); + + it('should deserialize primitive objects in an array', () => { + const deserialized = serializer.deserialize( + '[{"__type":"Number","__value":1337},{"__type":"String"' + + ',"__value":"foobar"},{"__type":"Boolean","__value":true}]' + ); + + deserialized.should.be.an('array').and.have.lengthOf(3); + should.equal(deserialized[0], 1337); + should.equal(deserialized[1], 'foobar'); + should.equal(deserialized[2], true); + }); + + it('should deserialize a model in an array', () => { + @Serializable() + class Model { + public name: string; + } + + const deserialized = serializer.deserialize( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}}]' + ); + + deserialized.should.have.lengthOf(1); + deserialized[0].should.be.an.instanceof(Model); + deserialized[0].name.should.equal('foobar'); + }); + + it('should deserialize a model in a model in an array', () => { + @Serializable() + class Model { + public name: string; + public submodel: Model; + } + + const deserialized = serializer.deserialize( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"},' + + '"submodel":{"__type":"Model","__value":{"name":{"__type":"String","__value":"submodel"}}}}}]' + ); + + deserialized[0].should.be.an.instanceof(Model); + deserialized[0].name.should.equal('foobar'); + deserialized[0].submodel.should.be.an.instanceof(Model); + deserialized[0].submodel.name.should.equal('submodel'); + }); + + it('should deserialize multiple models in an array', () => { + @Serializable() + class Model { + public name: string; + } + + const deserialized = serializer.deserialize( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}},' + + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar2"}}}]' + ); + + deserialized.should.have.lengthOf(2); + deserialized[0].name.should.equal('foobar'); + deserialized[1].name.should.equal('foobar2'); + }); + + it('should deserialize multiple models with submodels in an array', () => { + @Serializable() + class Model { + public name: string; + public submodel: Submodel; + } + + @Serializable() + class Submodel { + public name: string; + } + + const deserialized = serializer.deserialize( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"the model"},"submodel":' + + '{"__type":"Submodel","__value":{"name":{"__type":"String","__value":"the sub model"}}}}},' + + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"the model 2"},"submodel":' + + '{"__type":"Submodel","__value":{"name":{"__type":"String","__value":"the sub model 2"}}}}}]' + ); + + deserialized[0].should.be.an.instanceof(Model); + deserialized[0].name.should.equal('the model'); + deserialized[0].submodel.should.be.an.instanceof(Submodel); + deserialized[0].submodel.name.should.equal('the sub model'); + + deserialized[1].should.be.an.instanceof(Model); + deserialized[1].name.should.equal('the model 2'); + deserialized[1].submodel.should.be.an.instanceof(Submodel); + deserialized[1].submodel.name.should.equal('the sub model 2'); + }); + + it('should deserialize the same object as the same reference in an array', () => { + @Serializable() + class Model { + public name: string; + } + + const deserialized = serializer.deserialize( + '[{"__type":"Model","__value":{"name":{"__type":"String",' + + '"__value":"foobar"}}},{"__type":"ref","__value":{"type":"Model","index":0}}]' + ); + + deserialized[0].should.equal(deserialized[1]); + }); + + it('should deserialize the same object as the same reference in a submodule (recursive)', () => { + @Serializable() + class Model { + public name: string; + public mod: Model; + } + + const deserialized = serializer.deserialize( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"},' + + '"mod":{"__type":"ref","__value":{"type":"Model","index":0}}}}' + ); + + deserialized.mod.should.equal(deserialized); + }); + + it('should deserialize an object as a reference in another object (when in an array)', () => { + @Serializable() + class Model { + public name: string; + } + + @Serializable() + class OtherModel { + public model: Model; + } + + const deserialized = serializer.deserialize( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}},' + + '{"__type":"OtherModel","__value":{"model":{"__type":"ref","__value":{"type":"Model","index":0}}}}]' + ); + + deserialized[0].should.be.an.instanceof(Model); + deserialized[0].name.should.equal('foobar'); + + deserialized[1].should.be.an.instanceof(OtherModel); + deserialized[1].model.should.equal(deserialized[0]); + }); + + it('should deserialize an object as a reference in another object (when in an array reversed)', () => { + @Serializable() + class Model { + public name: string; + } + + @Serializable() + class OtherModel { + public model: Model; + } + + const deserialized = serializer.deserialize( + '[{"__type":"OtherModel","__value":{"model":{"__type":"Model","__value":{"name":' + + '{"__type":"String","__value":"foobar"}}}}},{"__type":"ref","__value":{"type":"Model","index":0}}]' + ); + + deserialized[0].should.be.an.instanceof(OtherModel); + deserialized[0].model.should.be.an.instanceof(Model); + + deserialized[1].should.equal(deserialized[0].model); + }); + + it('should deserialize multiple referenced objects', () => { + @Serializable() + class A { + public b: B; + } + + @Serializable() + class B { + public c: C; + } + + @Serializable() + class C { + public a: A; + public d: D; + } + + @Serializable() + class D { + public b: B; + } + + const deserialized = serializer.deserialize( + '{"__type":"A","__value":{"b":{"__type":"B","__value":{"c":{"__type":"C","__value":{"a":' + + '{"__type":"ref","__value":{"type":"A","index":0}},"d":{"__type":"D",' + + '"__value":{"b":{"__type":"ref","__value":{"type":"B","index":0}}}}}}}}}}' + ); + + deserialized.should.be.an.instanceof(A); + deserialized.b.should.be.an.instanceof(B); + deserialized.b.c.should.be.an.instanceof(C); + deserialized.b.c.a.should.equal(deserialized); + deserialized.b.c.d.should.be.an.instanceof(D); + deserialized.b.c.d.b.should.equal(deserialized.b); + }); + + it('should deserialize a model with a constructor', () => { + @Serializable({ + factory: data => new Model(data.name) + }) + class Model { + constructor(public name: string) { } + } + + const deserialized = serializer.deserialize( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"ctor model"}}}' + ); + + deserialized.name.should.equal('ctor model'); + }); + + it('should deserialize a model in a model with a constructor', () => { + @Serializable({ + factory: data => new Model() + }) + class Model { + constructor(public name: string = 'model', public sub: SubModel = new SubModel()) { } + } + + @Serializable({ + factory: data => new Model(data.name) + }) + class SubModel { + constructor(public name: string = 'sub model') { } + } + + const deserialized = serializer.deserialize( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"model"},"sub":' + + '{"__type":"SubModel","__value":{"name":{"__type":"String","__value":"sub model"}}}}}' + ); + + deserialized.name.should.equal('model'); + deserialized.sub.should.be.an.instanceof(SubModel); + deserialized.sub.name.should.equal('sub model'); + }); + + it('should throw when a model is not registered', () => { + const fn = () => { + serializer.deserialize( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}}' + ); + }; + + fn.should.throw(TypeNotRegisteredError); + }); + + it('should throw when a submodel in a model is not registered', () => { + @Serializable() + class Model { + public name: string; + } + + const fn = () => { + serializer.deserialize( + '{"__type":"Model","__value":{"name":{"__type":"String","__value":"the model"},"submodel":' + + '{"__type":"Submodel","__value":{"name":{"__type":"String","__value":"the sub model"}}}}}' + ); + }; + + fn.should.throw(TypeNotRegisteredError); + }); + + it('should throw when a model in an array is not registered', () => { + const fn = () => { + serializer.deserialize( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"foobar"}}}]' + ); + }; + + fn.should.throw(TypeNotRegisteredError); + }); + + it('should throw when a submodel in a model in an array is not registered', () => { + @Serializable() + class Model { + public name: string; + } + + const fn = () => { + serializer.deserialize( + '[{"__type":"Model","__value":{"name":{"__type":"String","__value":"the model"},"submodel":' + + '{"__type":"Submodel","__value":{"name":{"__type":"String","__value":"the sub model"}}}}}]' + ); + }; + + fn.should.throw(TypeNotRegisteredError); + }); + + }); + + describe('serialize / deserialize own function and input', () => { + + it('should work with an object that has a later object in it', () => { + @Serializable() + class Model { + public name: string; + } + + @Serializable() + class OtherModel { + public model: Model; + } + + const obj = new Model(); + obj.name = 'foobar'; + const otherObj = new OtherModel(); + otherObj.model = obj; + + const json = serializer.serialize([otherObj, obj]); + + const deserialized = serializer.deserialize(json); + + deserialized[1].should.be.an.instanceof(Model); + deserialized[1].name.should.equal('foobar'); + + deserialized[0].should.be.an.instanceof(OtherModel); + deserialized[0].model.should.equal(deserialized[1]); + }); + + it('should work with an object that has more later objects in it', () => { + @Serializable() + class Model { + public name: string; + } + + @Serializable() + class OtherModel { + public model: Model; + } + + const obj = new Model(); + obj.name = 'foobar'; + const otherObj = new OtherModel(); + otherObj.model = obj; + + const json = serializer.serialize([otherObj, obj, obj]); + + const deserialized = serializer.deserialize(json); + + deserialized[1].should.be.an.instanceof(Model); + deserialized[1].name.should.equal('foobar'); + + deserialized[0].should.be.an.instanceof(OtherModel); + deserialized[0].model.should.equal(deserialized[1]); + + deserialized[2].should.equal(deserialized[1]); + }); + + it('should work with an object that has a previous object reference in it', () => { + @Serializable() + class Model { + public name: string; + } + + @Serializable() + class OtherModel { + public model: Model; + } + + const obj = new Model(); + obj.name = 'foobar'; + const otherObj = new OtherModel(); + otherObj.model = obj; + + const json = serializer.serialize([obj, otherObj]); + + const deserialized = serializer.deserialize(json); + + deserialized[0].should.be.an.instanceof(Model); + deserialized[0].name.should.equal('foobar'); + + deserialized[1].should.be.an.instanceof(OtherModel); + deserialized[1].model.should.equal(deserialized[0]); + }); + + it('should work with an object that has a previous object reference in it and a normal ref', () => { + @Serializable() + class Model { + public name: string; + } + + @Serializable() + class OtherModel { + public model: Model; + } + + const obj = new Model(); + obj.name = 'foobar'; + const otherObj = new OtherModel(); + otherObj.model = obj; + + const json = serializer.serialize([obj, otherObj, obj]); + + const deserialized = serializer.deserialize(json); + + deserialized[0].should.be.an.instanceof(Model); + deserialized[0].name.should.equal('foobar'); + + deserialized[1].should.be.an.instanceof(OtherModel); + deserialized[1].model.should.equal(deserialized[0]); + + deserialized[2].should.equal(deserialized[0]); + }); + + it('should work with complex class structures', () => { + @Serializable() + class A { + public b: B; + } + + @Serializable() + class B { + public c: C; + } + + @Serializable() + class C { + public a: A; + public d: D; + } + + @Serializable() + class D { + public b: B; + } + + const a = new A(); + a.b = new B(); + a.b.c = new C(); + a.b.c.a = a; + a.b.c.d = new D(); + a.b.c.d.b = a.b; + + const json = serializer.serialize(a); + + const deserialized = serializer.deserialize(json); + + deserialized.should.be.an.instanceof(A); + deserialized.b.should.be.an.instanceof(B); + deserialized.b.c.should.be.an.instanceof(C); + deserialized.b.c.a.should.equal(deserialized); + deserialized.b.c.d.should.be.an.instanceof(D); + deserialized.b.c.d.b.should.equal(deserialized.b); + }); + + it('should work with models that have constructors', () => { + @Serializable({ + factory: data => new Model() + }) + class Model { + constructor(public name: string = 'model', public sub: SubModel = new SubModel()) { } + } + + @Serializable({ + factory: data => new Model(data.name) + }) + class SubModel { + constructor(public name: string = 'sub model') { } + } + + const json = serializer.serialize(new Model()); + + const deserialized = serializer.deserialize(json); + + deserialized.name.should.equal('model'); + deserialized.sub.should.be.an.instanceof(SubModel); + deserialized.sub.name.should.equal('sub model'); + }); + + }); + +}); diff --git a/tsconfig.json b/tsconfig/base.json similarity index 74% rename from tsconfig.json rename to tsconfig/base.json index b2981a4..d08787d 100644 --- a/tsconfig.json +++ b/tsconfig/base.json @@ -4,9 +4,6 @@ "module": "commonjs", "moduleResolution": "node", "removeComments": false, - "outDir": "./build", - "declaration": true, - "sourceMap": false, "experimentalDecorators": true, "emitDecoratorMetadata": true, "importHelpers": true, @@ -17,8 +14,7 @@ "es2015" ] }, - "exclude": [ - "node_modules", - "build" + "include": [ + "../**/*" ] } diff --git a/tsconfig/build.json b/tsconfig/build.json new file mode 100644 index 0000000..9a8f5b3 --- /dev/null +++ b/tsconfig/build.json @@ -0,0 +1,13 @@ +{ + "extends": "./base.json", + "compilerOptions": { + "outDir": "../", + "declaration": true, + "sourceMap": false + }, + "exclude": [ + "../node_modules", + "../build", + "../test" + ] +} diff --git a/tsconfig/develop.json b/tsconfig/develop.json new file mode 100644 index 0000000..cd3e2aa --- /dev/null +++ b/tsconfig/develop.json @@ -0,0 +1,13 @@ +{ + "extends": "./base.json", + "compilerOptions": { + "outDir": "../build", + "declaration": false, + "sourceMap": true, + "watch": true + }, + "exclude": [ + "../node_modules", + "../build" + ] +} diff --git a/tsconfig/test.json b/tsconfig/test.json new file mode 100644 index 0000000..c523bf1 --- /dev/null +++ b/tsconfig/test.json @@ -0,0 +1,14 @@ +{ + "extends": "./base.json", + "compilerOptions": { + "outDir": "../build", + "declaration": false, + "sourceMap": true, + "noUnusedLocals": false, + "noUnusedParameters": false + }, + "exclude": [ + "../node_modules", + "../build" + ] +}