
Security News
Deno 2.2 Improves Dependency Management and Expands Node.js Compatibility
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
decorative.js is an ES7 micro-framework promoting @decorator pattern-driven app development.
...is an ES7 micro-framework promoting @decorator pattern-driven app development.
Using decorators means to write:
It's really "decorative" code you will be writing using @decorators
;)
Decorators were introduced to Babel.js in spring 2015 by Yehuda Katz. Since then, only
a few decorators were implemented (see core-decorators
by jayphelps
). But the world didn't care much,
no matter if they are amazing or not.
When I discovered the es7.decorators I realized the power they could bring to my web apps and I began to implement decorative.js.
decorative.js is currently under development but already used to develop a web app which I will release soon. I rely on this framework and it's toolchain (Babel.js) and it works stable for my use-cases. I can't guarantee that it fully works for you but I think it's worth a try or at least a quick look.
decorative.js heavily relies on ES6 and 7. If you aren't already familiar with the great language features introduced in 6 right now, I kindly recommend you to read on here:
https://github.com/lukehoban/es6features
Well, decorators are part of the ES7 proposals. Let's see, how it looks like to use them.
First, we import
decorative and the decorators we are planning to use.
Afterwards we decorate classes
and class members
like this:
import { Singleton } from 'decorative';
@Singleton()
class MyFirstDecoratedClass {
hello = "Hi, ";
sayHello() {
console.log(this.hello + "I'm a singleton!");
}
}
Guess what? It's really a singleton now!
MyFirstDecoratedClass.sayHello();
Well, now that you got picture: Lets look what we need to run this code in today's JS engines.
Just install this package like this:
npm install decorative --save
If you also plan to use it with React, you may want to install decorative-react
:
npm install decorative-react --save
To use decorative you need to cross-compile the code from tomorrows ECMAScript 7 code back to today's ECMAScript 5 / 6 code. Well, this is quite easy. We use Babel.js and grunt for this. (while gulp is compatible too, I don't have a configuration example yet; feel free to provide one :)
So lets write a Grunt file and include webpack like this:
grunt.initConfig({
webpack: {
app: {
entry: "$yourAppEntry",
output: {
path: '$yourGeneratedOutputDirectory',
filename: "$nameOfTheSingleOutputFileBabelJSGeneratesForYou.js"
},
devtool: 'source-map', // <- so you can debug ECMAScript 7 code in devTools today
module: {
loaders: [
{
test: /\.js$/,
loader: "babel-loader"
}
]
},
watch: true, // <- re-compile on JS source/app code change
keepalive: true, // <- start the webpack dev-server
failOnError: false // <- prevents stopping the dev-server on error
}
}
});
A working example of a Gruntfile can be found in this repositories "Gruntfile.js".
Additionally create a .babelrc
file in your projects root directory (next to Gruntfile.js)
and put it like:
{
"presets": ["es2015", "stage-0"],
"plugins": ["transform-decorators", "syntax-class-properties"]
}
This configures Babel.js to parse/transform the full spectrum of ES6+ features.
Now that we set up Babel.js and grunt, we can run the transpiling process continuously:
grunt webpack
(Or just "grunt" when you set "webpack" as a default task)
Now that the cross-compilation works and you're able to use decorative and it's decorators, lets take a look which decorators have been implemented and are ready to use:
Creates an instance of the decorated class by calling it's constructor.
import { Singleton } from 'decorative';
@Singleton()
class MyFirstDecoratedClass {
hello = "Hi";
sayHello() {
console.log("${this.hello}, I'm a singleton!");
}
}
Assigns any object to the decorated class property. May use/create a singleton instance of a class.
import { Singleton, Inject } from 'decorative';
@Singleton()
class UsingInject {
@Inject('MyFirstDecoratedClass')
myFirstDecoratedClass = null;
}
UsingInject.myFirstDecoratedClass.sayHello();
Injects the provided i18n translation object into the target class prototype.
If a locale is given, it maps like translations[locale]
:
import { Translate, Singleton } from 'decorative';
import i18n from 'i18n/login';
@Translate(i18n, 'de_DE')
@Singleton
class LoginForm {
get loginButtonLabel() {
return this.i18n.loginButtonLabel;
}
getUserWelcomeMsg(name) {
return this.i18n.userWelcomeMsg(name);
}
}
You can use it like:
// if you like getters :)
LoginForm.loginButtonLabel() // "Einloggen"
LoginForm.getUserWelcomeMsg('John Doe') // "Herzlich Willkommen, John Doe!"
// without getters
LoginForm.i18n.loginButtonLabel // "Einloggen"
LoginForm.i18n.userWelcomeMsg('John Doe') // "Herzlich Willkommen, John Doe!"
Well, the i18n/login.js
should look like this:
export default {
de_DE: {
loginButtonLabel: 'Einloggen',
userWelcomeMsg: (name) => return "Herzlich Willkommen, ${name}!"
},
en_GB: {
loginButtonLabel: 'Login',
userWelcomeMsg: (name) => return "Welcome, ${name}!"
}
}
Creates an application instance (e.g. delayed, see below) and emits appStarted
on the internal event bus.
Modules will create their instances afterwards. (see @AppModule
).
An optional onStart()
lifecycle method gets called (if impl.) on the app class right before the modules get started.
The startConfig
-object of @App
supports the following properties:
delayed:Function
This function receives a callback function as it's first argument.
Call it at any time to create the App instance right at the time you call that start
function.
This also delays the startup of the @AppModules
for sure.
import { App } from 'decorative';
// module imports should happen before App declaration
import './modules/Login';
import './modules/Dashboard';
@App({
delayed: (start:Function) => {
$(document).ready(() => {
start();
});
}
})
class MyApp {
}
Limitation: Module imports should happen before App declaration.
Automatically creates an instance of the decorated class and adds it to the application instance.
Afterwards it emits moduleStarted
in the internal event bus so that @Route()
decorators get activated in time.
import { AppModule } from 'decorative';
@AppModule()
class Login {
constructor() {
console.log("I'm a module of ${this.app}!");
}
}
Watches for the module classes to be started and calls the decorated method as soon a matching document.location route gets detected.
import { AppModule, Route } from 'decorative';
@AppModule()
class Login {
@Route('/')
showLoginView() {
// show the login view here (e.g. using decorative-react ;)
}
}
As you can see, the @Route() decorator may be called with a second argument authCb
.
This enabled flexible, (even asynchronous) authentication checks:
@Route('/', isAllowed => {
// check if "/" is allowed, then call:
isAllowed(true); // or false, to deny
})
The method gets called only if isAllowed
gets called.
Limitation: Can be applied to methods in @AppModule decorated classes only yet. (see roadmap)
Wraps a decorated method inside a Promise.
@Promised()
findMyPhone(howItLooksLike, foundCb, imSureSomeoneHasStolenItCb) {
$.ajax({
url: '/whereIsMyPhone',
data: howItLooksLike
})
.then(found);
.catch(imSureSomeoneHasStolenIt);
}
The magic here is, that the method's return value is a promise auto-magically:
.findMySocks({color: 'white', type: 'android'})
.then(myPhone => {
// alright
})
.catch(err => {
this.comingLateToday();
});
Although a lot has been implemented, there is still a lot to do and you are very welcome to push :)
Features planned to be introduced in the near future:
@Singleton
should support constructor arguments@Inject
should be able to consume and resolve a full-featured DI config object as first argument@Route
should support to be applied on non-@StartModule classes (and therefore auto-detect if the class it belongs to is a module)FAQs
decorative.js is an ES7 micro-framework promoting @decorator pattern-driven app development.
The npm package decorative receives a total of 6 weekly downloads. As such, decorative popularity was classified as not popular.
We found that decorative demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Deno 2.2 enhances Node.js compatibility, improves dependency management, adds OpenTelemetry support, and expands linting and task automation for developers.
Security News
React's CRA deprecation announcement sparked community criticism over framework recommendations, leading to quick updates acknowledging build tools like Vite as valid alternatives.
Security News
Ransomware payment rates hit an all-time low in 2024 as law enforcement crackdowns, stronger defenses, and shifting policies make attacks riskier and less profitable.