Merge branch 'master' of github.com:leonidas/gulp-project-template

This commit is contained in:
Riku Rouvila
2015-11-27 13:52:06 +02:00
19 changed files with 423 additions and 152 deletions

3
.babelrc Normal file
View File

@@ -0,0 +1,3 @@
{
"presets": ["es2015"]
}

33
.eslintrc Normal file
View File

@@ -0,0 +1,33 @@
{
"extends": "airbnb",
"ecmaFeatures": {
"jsx": true,
"modules": true
},
"env": {
"browser": true,
"node": true
},
"parser": "babel-eslint",
"rules": {
"quotes": [2, "single"],
"react/jsx-uses-react": 2,
"react/jsx-uses-vars": 2,
"react/react-in-jsx-scope": 2,
"comma-dangle": [2, "never"],
"space-after-keywords": [2, "never"],
"react/jsx-quotes": [2, "single"],
"react/prop-types": 0,
"no-use-before-define": 0,
"padded-blocks": 0,
"id-length": [2, {
"min": 3,
"max": 30,
"properties": "never",
"exceptions": ["x", "y", "vx", "vy", "id", "i", "e", "fn"]
}]
},
"plugins": [
"react"
]
}

4
.gitignore vendored
View File

@@ -1,5 +1,5 @@
npm-debug.log
node_modules
bower_components
public
.DS_Store
npm-debug.log

View File

@@ -1,3 +0,0 @@
{
"browserify": true
}

21
LICENSE.md Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Riku Rouvila
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -5,54 +5,43 @@
**Koodiklinikka.fi lähdekoodi**. [Issueita](https://github.com/koodiklinikka/koodiklinikka.fi/issues) ja [Pull Requestejä](https://github.com/koodiklinikka/koodiklinikka.fi/pulls) otetaan lämpimästi vastaan. Yritämme pitää kynnyksen kontribuoida projektiin alhaisena, jotta mahdollisimman moni pääsisi jättämään siihen jälkensä. Kaikki koodi katselmoidaan läpi ja mergetään projektiin kun näyttää hyvälle. Muutamasta mergetystä Pull Requestista oikeudet ylläpitää projektia.
[Issueita](https://github.com/koodiklinikka/koodiklinikka.fi/issues) voidaan käyttää myös sivun
[Issueita](https://github.com/koodiklinikka/koodiklinikka.fi/issues) voidaan käyttää myös sivun
* toiminnallisuuteen
* designiin
* designiin
* [HTTP-rajapintaan](https://github.com/koodiklinikka/koodiklinikka.fi-api)
* projektin hallintaan liittyviin asioihin
Tai koko Koodiklinikkaan yleisesti.
-----------------------------
# Contributing
This repository is automatically deployed by [Codeship](https://codeship.com) to a [Digital Ocean](http://digitalocean.com) droplet hosting [http://koodiklinikka.fi](http://koodiklinikka.fi).
## Getting things up and running
- Install [Node.js](http://nodejs.org)
```
git clone git@github.com:koodiklinikka/koodiklinikka.fi.git <your project name>
git clone git@github.com:leonidas/gulp-project-template.git <your project name>
cd <your project name>
npm install
npm start
open http://localhost:9001 in your browser
```
### Enable LiveReload
Install [LiveReload for Chrome](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=en)
## CLI Commands
* npm install
* Installs server-side dependencies from NPM and client-side dependencies from Bower
* Installs server-side dependencies from npm
* npm start
* Compiles your files, starts watching files for changes, serves static files to port 9001
* npm run build
* Builds everything
# Production build
Minification, uglification and other tasks you're expected to run before deploying your product can be made by running the build command with env variable NODE_ENV set to "production"
```
NODE_ENV=production npm run build
```
## API server
API proxy can be defined with **SERVER** environment variable.
```
SERVER=http://localhost:9000 npm start
```
NODE_ENV=production npm run build
## Development guidelines
* **public** - directory should be dedicated only to compiled/copied files from **src** - directory.
#### Directory structure
**public** - directory should be dedicated only to compiled/copied files from **src** - directory.
It should be possible to delete directory completely and after **npm start** or **npm run build** everything should be as they were before the deletion.
* All backend dependencies should be installed with **npm**. Browser dependencies should be installed with **bower** or with **npm**.

View File

@@ -1,18 +0,0 @@
{
"name": "koodiklinikka.fi",
"version": "0.0.0",
"authors": [
"Riku Rouvila <riku.rouvila@gmail.com>"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"font-awesome": "~4.2.0"
}
}

187
gulpfile.babel.js Normal file
View File

@@ -0,0 +1,187 @@
import browserify from 'browserify';
import browserSync from 'browser-sync';
import duration from 'gulp-duration';
import gulp from 'gulp';
import gutil from 'gulp-util';
import jade from 'gulp-jade';
import notifier from 'node-notifier';
import path from 'path';
import prefix from 'gulp-autoprefixer';
import rev from 'gulp-rev';
import source from 'vinyl-source-stream';
import exorcist from 'exorcist';
import transform from 'vinyl-transform';
import sourcemaps from 'gulp-sourcemaps';
import streamify from 'gulp-streamify';
import stylus from 'gulp-stylus';
import uglify from 'gulp-uglify';
import watchify from 'watchify';
import watch from 'gulp-watch';
import inject from 'gulp-inject';
/*eslint "no-process-env":0 */
const production = process.env.NODE_ENV === 'production';
const config = {
source: './src',
destination: './public',
scripts: {
source: './src/main.js',
destination: './public/js/',
extensions: ['.jsx'],
filename: 'bundle.js'
},
templates: {
source: './src/*.jade',
watch: './src/*.jade',
destination: './public/',
revision: './public/**/*.html'
},
styles: {
source: './src/style.styl',
watch: './src/**/*.styl',
destination: './public/css/'
},
assets: {
source: './src/assets/**/*.*',
watch: './src/assets/**/*.*',
destination: './public/'
},
inject: {
resources: ['./public/**/*.css', './public/**/*.js']
}
};
const browserifyConfig = {
entries: [config.scripts.source],
extensions: config.scripts.extensions,
debug: !production,
cache: {},
packageCache: {}
};
function handleError(err) {
gutil.log(err.message);
gutil.beep();
notifier.notify({
title: 'Compile Error',
message: err.message
});
return this.emit('end');
}
gulp.task('scripts', () => {
let pipeline = browserify(browserifyConfig)
.bundle()
.on('error', handleError)
.pipe(source(config.scripts.filename));
if(production) {
pipeline = pipeline
.pipe(streamify(uglify()))
.pipe(streamify(rev()));
} else {
pipeline = pipeline.pipe(transform(() => {
return exorcist(path.join(config.scripts.destination, config.scripts.filename) + '.map');
}));
}
return pipeline.pipe(gulp.dest(config.scripts.destination));
});
gulp.task('templates', ['styles', 'scripts'], () => {
const resources = gulp.src(config.inject.resources, {read: false});
const pipeline = gulp.src(config.templates.source)
.pipe(jade({
pretty: !production
}))
.on('error', handleError)
.pipe(inject(resources, {ignorePath: 'public', removeTags: true}))
.pipe(gulp.dest(config.templates.destination));
if(production) {
return pipeline;
}
return pipeline.pipe(browserSync.reload({
stream: true
}));
});
gulp.task('styles', () => {
let pipeline = gulp.src(config.styles.source);
if(!production) {
pipeline = pipeline.pipe(sourcemaps.init());
}
pipeline = pipeline.pipe(stylus({
'include css': true,
paths: ['node_modules', path.join(__dirname, config.source)],
compress: production
}))
.on('error', handleError)
.pipe(prefix('last 2 versions', 'Chrome 34', 'Firefox 28', 'iOS 7'));
if(production) {
pipeline = pipeline.pipe(rev());
} else {
pipeline = pipeline.pipe(sourcemaps.write('.'));
}
pipeline = pipeline.pipe(gulp.dest(config.styles.destination));
if(production) {
return pipeline;
}
return pipeline.pipe(browserSync.stream({
match: '**/*.css'
}));
});
gulp.task('assets', () => {
return gulp.src(config.assets.source)
.pipe(gulp.dest(config.assets.destination));
});
gulp.task('server', () => {
return browserSync({
open: false,
port: 9001,
notify: false,
ghostMode: false,
server: {
baseDir: config.destination
}
});
});
gulp.task('watch', () => {
['templates', 'styles', 'assets'].forEach((watched) => {
watch(config[watched].watch, () => {
gulp.start(watched);
});
});
const bundle = watchify(browserify(browserifyConfig));
bundle.on('update', () => {
const build = bundle.bundle()
.on('error', handleError)
.pipe(source(config.scripts.filename));
build
.pipe(transform(() => {
return exorcist(config.scripts.destination + config.scripts.filename + '.map');
}))
.pipe(gulp.dest(config.scripts.destination))
.pipe(duration('Rebundling browserify bundle'))
.pipe(browserSync.reload({stream: true}));
}).emit('update');
});
gulp.task('build', ['styles', 'assets', 'scripts', 'templates']);
gulp.task('default', ['styles', 'assets', 'templates', 'watch', 'server']);

View File

@@ -6,10 +6,10 @@
"license": "MIT",
"main": "gulpfile.js",
"scripts": {
"prepublish": "bower install",
"start": "gulp",
"build": "gulp build",
"test": "karma start test/karma.conf.js"
"start": "rm -rf public && gulp",
"build": "rm -rf public && gulp build",
"lint": "eslint src",
"test": "mocha src/**/__tests__/*.js --compilers js:babel-core/register --require test/test-helper"
},
"keywords": [
"gulp",
@@ -17,53 +17,55 @@
],
"dependencies": {
"axios": "^0.4.2",
"lodash": "^2.4.1",
"font-awesome": "^4.4.0",
"http-server": "^0.8.0",
"lodash": "^3.9.1",
"parse-github-event": "^0.2.0",
"react": "^0.12.2",
"timeago": "^0.2.0",
"twitter-text": "^1.11.0",
"http-server": "^0.8.0"
"twitter-text": "^1.11.0"
},
"devDependencies": {
"6to5ify": "^3.1.2",
"bower": "~1.3.5",
"browserify": "~6.1.0",
"coffee-script": "~1.8.0",
"deamdify": "^0.1.1",
"debowerify": "~0.9.1",
"ecstatic": "~0.5.3",
"babel": "^6.1.18",
"babel-core": "^6.2.1",
"babel-eslint": "^4.1.3",
"babel-preset-es2015": "^6.1.18",
"babel-preset-react": "^6.1.18",
"babelify": "^7.2.0",
"browser-sync": "^2.9.4",
"browserify": "^10.2.1",
"chai": "^3.0.0",
"envify": "^3.4.0",
"event-stream": "^3.2.1",
"faker": "^2.1.2",
"gulp": "~3.8.1",
"eslint": "^1.5.1",
"eslint-config-airbnb": "0.0.9",
"eslint-plugin-react": "^3.4.2",
"event-stream": "^3.3.2",
"exorcist": "^0.4.0",
"gulp": "3.9.0",
"gulp-autoprefixer": "1.0.1",
"gulp-concat": "^2.4.3",
"gulp-duration": "0.0.0",
"gulp-inject": "^3.0.0",
"gulp-jade": "~0.9.0",
"gulp-less": "3.0.0",
"gulp-livereload": "~2.1.0",
"gulp-minify-css": "~0.3.5",
"gulp-less": "^3.0.5",
"gulp-replace": "^0.5.3",
"gulp-rev": "^4.0.0",
"gulp-sourcemaps": "^1.3.0",
"gulp-streamify": "0.0.5",
"gulp-stylus": "1.3.3",
"gulp-stylus": "~2.0.0",
"gulp-uglify": "~1.0.1",
"gulp-util": "~3.0.1",
"http-proxy": "^1.8.1",
"jsx-transform": "^0.10.1",
"karma": "~0.12.21",
"karma-chrome-launcher": "~0.1.4",
"karma-cli": "0.0.4",
"karma-coffee-preprocessor": "~0.2.1",
"karma-jasmine": "~0.2.2",
"reactify": "^1.0.0",
"through2": "^0.6.3",
"gulp-watch": "^4.3.4",
"jsdom": "^5.6.0",
"mocha": "^2.2.5",
"node-notifier": "^4.2.1",
"rimraf": "^2.3.4",
"vinyl-source-stream": "~1.0.0",
"watchify": "~2.3.0"
"vinyl-transform": "^1.0.0",
"watchify": "^3.2.1"
},
"browserify": {
"transform": [
"reactify",
"6to5ify",
"debowerify",
"deamdify",
"babelify",
"envify"
]
}

View File

@@ -0,0 +1,37 @@
/* globals beforeEach, describe, it */
import {render} from '../';
import {expect} from 'chai';
const REPO_DATA = {
html_url: 'http://example.com',
full_name: 'Gulp project template',
description: 'Hello world!'
};
const COMMITS = [{
message: 'initial commit',
committer: {
name: 'Riku'
}
}, {
message: 'final commit',
committer: {
name: 'L.H.Ahne'
}
}];
describe('View renderer', function() {
beforeEach(function() {
document.body.innerHTML = render(REPO_DATA, COMMITS);
});
it('should render a title with the string "Hello world!"', function() {
const $title = document.getElementsByTagName('h1')[0];
expect($title.innerHTML).to.equal('Hello world!');
});
it('should render 2 list items', function() {
const $listItems = document.querySelectorAll('li');
expect($listItems.length).to.equal(2);
});
});

View File

@@ -0,0 +1,28 @@
export function render(repository, commits) {
const $commits = commits.map(commit => {
return `
<li class="commit">
<span>
${commit.message.replace(/\n/g, '<br />')}
<span>
<br /><br />
<small>
${commit.committer.name}
</small>
</li>`;
});
return `
<div class="repository">
<h1 class="repository__title">${repository.description}</h1>
<h2 class="repository__description">
<a href="${repository.html_url}">${repository.full_name}</a>
</h2>
<ul class="repository__commits">
${$commits.join('')}
</ul>
</div>
`;
}

View File

@@ -0,0 +1,17 @@
.repository__title
margin-bottom 0
.repository__description
margin-top 0
.repository__commits
margin-top 2em
.commit
margin 1em 0
padding 1em 0
border-bottom 1px solid #eee
small
color #777

10
src/index.jade Normal file
View File

@@ -0,0 +1,10 @@
doctype html
html
head
title Gulp template
// inject:css
// endinject
body
h1 Hello world!
// inject:js
// endinject

8
src/main.js Normal file
View File

@@ -0,0 +1,8 @@
import {getCommits, getRepo} from './services/github';
import {render} from './components/repository';
Promise.all([getRepo(), getCommits()])
.then(([repository, commits]) => {
document.body.innerHTML = render(repository, commits);
});

13
src/services/github.js Normal file
View File

@@ -0,0 +1,13 @@
const REPO_URL = 'https://api.github.com/repos/leonidas/gulp-project-template';
export function getRepo() {
return fetch(REPO_URL).then(res => res.json());
}
export function getCommits() {
return fetch(`${REPO_URL}/commits`)
.then(res => res.json())
.then(commits => {
return commits.map(({commit}) => commit);
});
}

8
src/style.styl Normal file
View File

@@ -0,0 +1,8 @@
body, html
margin 0
padding 1em
font 14px/1.4 'Helvetica Neue', Helvetica, Arial
color #333
@import 'components/repository'
// @import 'bootstrap/dist/css/bootstrap.css'

View File

@@ -1,68 +0,0 @@
// Karma configuration
// Generated on Mon Aug 11 2014 13:43:38 GMT+0300 (EEST)
'use strict';
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '../',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'test/unit/**/*.coffee'
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'**/*.coffee': ['coffee']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true
});
};

7
test/test-helper.js Normal file
View File

@@ -0,0 +1,7 @@
'use strict';
import {jsdom} from 'jsdom';
const document = global.document = jsdom('<html><head></head><body></body></html>');
const window = global.window = document.defaultView;
global.navigator = window.navigator = {};

View File

@@ -1,3 +0,0 @@
describe 'Example spec', ->
it 'should ...', ->
expect(true).toBe(true)