The Balsa scaffolding tool provides a lightweight framework for scaffolding projects. This project provides a setup for Angular 2 & Webpack using Balsa.
As of now, you'll need Node 5.0+ & NPM to get this all working. But let me suggest nvm or n to manage multiple versions of Node & NPM.
To add it to your project, just run:
npm i -D https://github.com/gonzofish/balsa https://github.com/gonzofish/ng2-webpack-balsa
And add, to your project's package.json's scripts
section:
"balsa": "node ./node_modules/ng2-webpack-balsa"
Then you can run commands like the following:
npm run balsa
npm run balsa initial
npm run balsa component
npm run balsa component my-component
Again, if you follow the above guidance, all commands start with npm run balsa
.
All names & selectors need to be in dash format which is all lowercase with words seprated by hyphens (-).
npm run balsa component
npm run balsa component-name
The component command will scaffold out a new component, including unit test file. You will be prompted with the following options:
Selector:
- this will be the selector used in a parent component to use the component. The value should be in dash format. This value will also be used to create the exported class name. For instance, enteringmy-component
will create the class nameMyComponent
.Use inline styles (y/n)?
- enteringy
oryes
will add a line for styles. For instance, if you entery
and the component selector ismy-component
, a line readingstyles: []
will be added to the TS file. Otherwise, the line will readstyles: [require('./my-component.component.scss')]
.Use inline template (y/n)?
- enteringy
oryes
will use an empty inline template. Enteringn
orno
will add a line liketemplate: require('./my-component.component.html')
to the TS file.Lifecycle hooks (comma-separated; i.e., "OnInit, OnDestroy"):
- this allows you to add lifecycle hooks to the component. To add no hooks, just enter nothing. Valid options are as follows (and unknown values will be ignored):DoCheck
orcheck
OnChanges
orchanges
OnDestroy
ordestroy
OnInit
orinit
This will create, at least, the following:
src/app/components/[selector]/[selector].component.ts
src/test/components/[selector].component.spec.ts
And, depending on prompt answers:
src/app/components/[selector]/[selector].component.scss
(if inline template isn
orno
)src/app/components/[selector]/[selector].component.html
(if inline template isn
orno
)
> npm run balsa component
Selector: pizza-party
Use inline styles (y/n)? n
Use an inline template (y/n)? y
Lifecycle hooks (comma-separated; i.e., "OnInit, OnDestroy"): init,destroy,docheck
src/app/components/pizza-party/pizza-party.component.ts
import {
Component, OnInit, OnDestroy, DoCheck
} from '@angular/core';
@Component({
selector: 'pizza-party',
styles: [require('./pizza-party.component.scss')],
template: ``
})
export class PizzaPartyComponent implements OnInit, OnDestroy, DoCheck {
ngOnInit() {
}
ngOnDestroy() {
}
ngDoCheck() {
}
}
src/test/specs/components/pizza-party.component.spec.ts
import {
beforeEach,
describe,
expect,
it
} from '@angular/core/testing';
import { ComponentFixture } from '@angular/compiler/testing';
import * as testUtils from '../../test-utils';
import { PizzaPartyComponent } from '../../../app/components/pizza-party/pizza-party.component.ts';
describe('Component: PizzaPartyComponent', () => {
let component: PizzaPartyComponent;
let fixture: ComponentFixture<PizzaPartyComponent>;
beforeEach(testUtils.createComponent(PizzaPartyComponent, (componentInstance: PizzaPartyComponent, componentFixture: ComponentFixture<PizzaPartyComponent>) => {
component = componentInstance;
fixture = componentFixture;
}));
});
There will also be an empty src/app/components/pizza-party.component.scss
file.
npm run balsa initial
Initializes the directory to a new Git project, sets up a basic README.md & package.json. The prompts are:
README title
- the value entered will be the first line of README.mdpackage.json name
- this will be used for thename
attribute in package.jsonRepository URL:
- this will be used for theurl
attribute of therepository
section of package.json.
This section does no error checking and will overwrite any existing files and create a new Git repository! This will create:
README.md
package.json
.git
> npm run balsa initial
README title: A Food Story
package.json name: food-story
Repository URL: https://github.com/gonzofish/food-story
package.json
(truncated)
{
"name": "food-story",
"version": "0.0.0",
...
"repository": {
"url": "https://github.com/gonzofish/food-story"
}
}
README.md
# A Food Story
And a new .git
directory will be created.
npm run balsa pipe
npm run balsa pipe my-pipe
Pipe name (in dash-case):
- only asked if it is not providedInput type (type:fileLocation, blank is
any):
- provide the input type and, if necessary, the location of the interface detailing that type. Entering blank sets the type toany
.Output type(type:fileLocation, blank is
any):
- provide the output type and, if necessary, the location of the interface detailing that type. Entering blank sets the type toany
.Additional arguments (argument:type:fileLocation, comma-separated):
- provide a comma-separated list of additional arguments to the pipe.
This will produce:
src/app/pipes/[pipe name].pipe.ts
src/test/specs/pipes/[pipe name].pipe.spec.ts
> npm run balsa pipe first-time
Input type (type:fileLocation, blank is `any`): MyType:types/my
Output type(type:fileLocation, blank is `any`): YourType:types/your
Additional arguments (argument:type:fileLocation, comma-separated): arg1:Type:types/my,arg2:string,arg3:AnotherType:from/somewhere,arg4:FinalType:types/my
src/app/pipes/first-time.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
import { MyType,Type,FinalType } from '../types/my'
import { YourType } from '../types/your'
import { AnotherType } from '../from/somewhere'
@Pipe({ name: 'firstTime' })
export class FirstTimePipe implements PipeTransform {
transform(value: MyType, arg1: Type, arg2: string, arg3: AnotherType, arg4: FinalType): YourType {
}
}
src/test/specs/pipes/first-time.pipe.spec.ts
Input type (type:fileLocation, blank is `any`): MyType:types/my
Output type(type:fileLocation, blank is `any`): YourType:types/your
Additional arguments (argument:type:fileLocation, comma-separated): arg1:Type:types/my,arg2:string,arg3:AnotherType:from/somewhere,arg4:FinalType:types/my
npm run balsa route
npm run balsa route child-component-name
If the route's component selector is not passed as an argument to the command, you will be asked the same questions as the component command. Answer these questions for the new route component. After that, the only question is:
What is the parent component?
- the selector of place to route from
This produce the same output as the component command for the route component.
It will also modify the parent component to use routes. This adds
ROUTER_DIRECTIVES
&ROUTER_PROVIDERS
to the@Component
decorator- Ensures that
Routes
,ROUTER_DIRECTIVES
, andROUTER_PROVIDERS
are all imported from@angular/router
(runnpm i -S @angular/router
if you haven't already) - Creates the
@Routes
decorator and adds the new route (if necessary)
Currently src/app/components/app/app.component.ts
looks like:
import {
Component
} from '@angular/core';
@Component({
selector: 'app',
template: require('./app.component.html')
})
export class AppComponent {
}
> npm run balsa route burrito-bungalo
Use inline styles (y/n)? y
Use an inline template (y/n)? y
Lifecycle hooks (comma-separated; i.e., "OnInit, OnDestroy"):
What is the parent component? app
All files will be created similar to the component command example.
src/app/components/app/app.component.ts
import {
Component
} from '@angular/core';
import { Routes, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router';
import { BurritoBungalowComponent } from '../burrito-bungalow/burrito-bungalow.component';
@Component({
selector: 'app',
template: require('./app.component.html'),
directives: [ROUTER_DIRECTIVES],
providers: [ROUTER_PROVIDERS]
})
@Routes([
{ component: BurritoBungalowComponent, path: '/burrito-bungalow' }
])
export class AppComponent {}
npm run balsa service
npm run balsa service service-name
Using the dash naming method, creates a service and its unit test file. Your will be prompted with a single question:
Service name:
- answer this using the dash format. For instance,my-new-service
.
This will produce the following:
src/app/services/[service name].service.ts
src/test/services/[service name].service.spec.ts
> npm run balsa service
Service name: order-handling
src/app/services/order-handling.service.ts
import { Injectable } from '@angular/core';
@Injectable()
export class OrderHandlingService {
constructor() {}
}
src/test/specs/services/order-handling.service.spec.ts
import {
beforeEachProviders,
describe,
expect,
inject,
it
} from '@angular/core/testing';
import { OrderHandlingService } from '../../../app/services/order-handling.service.ts';
describe('Service: OrderHandling', () =>{
beforeEachProviders(() => [OrderHandlingService]);
it('should', inject([OrderHandlingService], (service: OrderHandlingService) => {
}));
});