js-reflector

js-reflector for Typescript

Usage no npm install needed!

<script type="module">
  import jsReflector from 'https://cdn.skypack.dev/js-reflector';
</script>

README

js-reflector

js-reflector for Typescript

An engineer built an airplane, and he recorded its structure.

A pilot comes, starts the motor, flies to the sky.

Why is Reflection

  • First-Class-Value
  • Know more at runtime
  • Useful for increasing the flexibility

Get started

Give a definite name to the class

import { metadata, util } from "js-reflector";

@metadata.className("namespace.BaseClass")
class BaseClass {

}

@metadata.className("namespace.SampleClass")
@metadata.superClass(BaseClass)
class SampleClass extends BaseClass {
    constructor(id: string) {
        this.id = id;
    }
    id: string;
}

console.log(util.getClassName(SampleClass));
// namespace.SampleClass
console.log(util.getClassByName('namespace.SampleClass'));
// [Function: SampleClass]
console.log(util.newInstance('namespace.SampleClass', "xxxxxxx").id);
// xxxxxxx
console.log(util.getSuperClass('namespace.SampleClass'));
// [Function: BaseClass]
console.log(util.getSuperClassName(SampleClass));
// namespace.BaseClass

Add more metadata

import { metadata, util } from "js-reflector";

@metadata('description', 'This is a SampleClass')
class SampleClass {

}

console.log(util.getClassSchema(SampleClass).description);
// This is a SampleClass

Class metadata schema

import { metadata, util } from "js-reflector";

@metadata.className("namespace.BaseClass")
class BaseClass {

}

@metadata.className("namespace.SampleClass")
@metadata.superClass(BaseClass)
class SampleClass {
    @metadata('meta', 'value')
    static staticSampleProperty: string;
    @metadata('meta', 'value')
    static staticSampleMethod() { }
    @metadata('meta', 'value')
    sampleProperty: string;
    @metadata('meta', 'value')
    sampleMethod() { }
}
console.log(util.getClassSchema(SampleClass));
// {
//     className: "namespace.SampleClass",
//     superClass: "namespace.BaseClass",
//     staticProperties: {
//         staticSampleProperty: {
//             name: "staticSampleProperty",
//             meta: "value"
//         },
//         staticSampleMethod: {
//             name: "staticSampleMethod",
//             isMethod: true,
//             meta: "value"
//         }
//     },
//     properties: {
//         sampleProperty: {
//             name: "sampleProperty",
//             meta: "value"
//         },
//         sampleMethod: {
//             name: "sampleMethod",
//             isMethod: true,
//             meta: "value"
//         }
//     }
// }

Declared metadata

@metadata("meta", "value")
@metadata.className("namespace.Base")
class Base {

}

@metadata.superClass(Base)
@metadata.className("namespace.Child")
class Child extends Base {

}

console.log(util.getClassSchema(Child));
// {
//     className: "namespace.Child",
//     superClass: "namespace.Base",
//     staticProperties: {},
//     properties: {}
// }
console.log(util.getClassSchema(Child, true));
// {
//     className: "namespace.Child",
//     superClass: "namespace.Base",
//     meta: "value",
//     staticProperties: {},
//     properties: {}
// }

Inheritable & Mergeable metadata

@metadata("will_be_inherited", "value")
@metadata("will_be_overwritten", "value")
class Base {
    @metadata("will_be_inherited", "value")
    @metadata("will_be_overwritten" , "value")
    prop;
}
@metadata("will_be_overwritten", "ChildValue")
@metadata.superClass(Base)
class Child extends Base {
    @metadata("will_be_overwritten" , "ChildValue")
    prop;
}
console.log(util.getClassSchema(Child, true).will_be_inherited);
// value
console.log(util.getClassSchema(Child, true).will_be_overwritten);
// ChildValue
console.log(util.getClassSchema(Child, true).properties['prop'].will_be_inherited);
// value
console.log(util.getClassSchema(Child, true).properties['prop'].will_be_overwritten);
// ChildValue

Dynamic object reflection

@metadata("sampleMeta", "value")
@metadata.className("descripbe.SampleClass")
class SampleClass {
    
}
var dynamicObject = {
    stringValue: "text",
    numberValue: 1,
    booleanValue: true,
    dateValue: new Date(),
    undefinedValue: undefined,
    nullValue: null,
    NaNValue: NaN,
    arrayValue: [],
    objectValue: {},
    functionValue: function () { },
    sampleValue: new SampleClass()
}
console.log(util.describe(dynamicObject, true));
// {
//     "type": "Object",
//     "properties": {
//         "stringValue": {
//             "name": "stringValue",
//             "type": "String"
//         },
//         "numberValue": {
//             "name": "numberValue",
//             "type": "Number"
//         },
//         "booleanValue": {
//             "name": "booleanValue",
//             "type": "Boolean"
//         },
//         "dateValue": {
//             "name": "dateValue",
//             "type": "Date"
//         },
//         "undefinedValue": {
//             "name": "undefinedValue",
//             "type": "Undefined"
//         },
//         "nullValue": {
//             "name": "nullValue",
//             "type": "Null"
//         },
//         "NaNValue": {
//             "name": "NaNValue",
//             "type": "Number"
//         },
//         "arrayValue": {
//             "name": "arrayValue",
//             "type": "Array"
//         },
//         "objectValue": {
//             "name": "objectValue",
//             "type": "Object"
//         },
//         "functionValue": {
//             "name": "functionValue",
//             "type": "Function"
//         },
//         "sampleValue": {
//             "sampleMeta": "value",
//             "name": "sampleValue",
//             "type": "descripbe.SampleClass"
//         }
//     }
// }

Build

$ npm build

Test

$ npm test

Using karma-sauceLabs-launcher

For run the tests across many browsers and platforms on Sauce Labs, you must make file 'karma.sauceLabs.json' in the project:

{
    "testName": "your test name",
    "username": "your SauceLabs username",
    "accessKey": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

The customLaunchers object configures individual browsers in file 'karma.conf.js', and the sauceLabs reporter allows your tests results to be properly displayed on https://saucelabs.com.

karma.conf.js

var karma_sauceLabs = require("./karma.sauceLabs.json");

module.exports = function(config) {
    config.set({
        sauceLabs: karma_sauceLabs,
        customLaunchers: {
            sl_chrome: {
                base: 'SauceLabs',
                browserName: 'chrome',
                platform: 'Windows 7',
                version: '35'
            },
        },
        reporters: ['progress', 'saucelabs'],
        browsers: ['sl_chrome'],
    })
}

all browser/platform combos

karma-sauce-launcher