我有一个基于 angular-fullstack-generator 的大型项目,当我运行时:
gulp serve:dist
我懂了:
GET /bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.woff 404 0.739 ms - 14
GET /bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.woff 404 0.739 ms - 14
GET /bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf 404 1.023 ms - 14
GET /bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf 404 1.023 ms - 14
而且由于缺少这些字体,我还看到:
2016-06-22T13:23:22-0600 <error> index.js:19 () { [Error: Failed to lookup view "404" in views directory ".../dist/server/views"] ...}
我通过将以下内容添加到 gulpfile.babel.js 来纠正这个问题:
gulp.task('copy:fonts-extras', () => {
return gulp.src([`${clientPath}/bower_components/bootstrap/fonts/*`], { dot: true }) .pipe(gulp.dest(`${paths.dist}/${clientPath}/bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/`));
});
我把它放在'build'任务中的copy:fonts条目之后。
这似乎有点愚蠢,因为这些字体已经在 bootstrap/fonts/ 目录中,但这不是代码试图访问它们的地方,而且(对我来说)在代码中的哪个位置进行更改并不明显小路。
同时,“生产”构建不会复制视图目录(仅包含 404.html 文件)。
所以我还补充说:
gulp.task('copy:views', () => {
return gulp.src([
'server/views/*'
], { dot: true })
.pipe(gulp.dest(`${paths.dist}/server/views/`));
});
我添加了 copy:views 与我添加 copy:fonts-extra 的位置相同。
有没有更好的方法来纠正这些问题?
我正在使用 angular-fullstack-generator v3.7.6 的最新版本。
也许 beta2 可以纠正这个问题?
这是生成的 gulpfile.babel.js:
// Generated on 2016-06-21 using generator-angular-fullstack 3.7.6
'use strict';
import _ from 'lodash';
import del from 'del';
import gulp from 'gulp';
import grunt from 'grunt';
import path from 'path';
import gulpLoadPlugins from 'gulp-load-plugins';
import http from 'http';
import open from 'open';
import lazypipe from 'lazypipe';
import {stream as wiredep} from 'wiredep';
import nodemon from 'nodemon';
import {Server as KarmaServer} from 'karma';
import runSequence from 'run-sequence';
import {protractor, webdriver_update} from 'gulp-protractor';
import {Instrumenter} from 'isparta';
var plugins = gulpLoadPlugins();
var config;
const clientPath = require('./bower.json').appPath || 'client';
const serverPath = 'server';
const paths = {
client: {
assets: `${clientPath}/assets/**/*`,
images: `${clientPath}/assets/images/**/*`,
scripts: [
`${clientPath}/**/!(*.spec|*.mock).js`,
`!${clientPath}/bower_components/**/*`
],
styles: [`${clientPath}/{app,components}/**/*.scss`],
mainStyle: `${clientPath}/app/app.scss`,
views: `${clientPath}/{app,components}/**/*.html`,
mainView: `${clientPath}/index.html`,
test: [`${clientPath}/{app,components}/**/*.{spec,mock}.js`],
e2e: ['e2e/**/*.spec.js'],
bower: `${clientPath}/bower_components/`
},
server: {
scripts: [
`${serverPath}/**/!(*.spec|*.integration).js`,
`!${serverPath}/config/local.env.sample.js`
],
json: [`${serverPath}/**/*.json`],
test: {
integration: [`${serverPath}/**/*.integration.js`, 'mocha.global.js'],
unit: [`${serverPath}/**/*.spec.js`, 'mocha.global.js']
}
},
karma: 'karma.conf.js',
dist: 'dist'
};
/********************
* Helper functions
********************/
function onServerLog(log) {
console.log(plugins.util.colors.white('[') +
plugins.util.colors.yellow('nodemon') +
plugins.util.colors.white('] ') +
log.message);
}
function checkAppReady(cb) {
var options = {
host: 'localhost',
port: config.port
};
http
.get(options, () => cb(true))
.on('error', () => cb(false));
}
// Call page until first success
function whenServerReady(cb) {
var serverReady = false;
var appReadyInterval = setInterval(() =>
checkAppReady((ready) => {
if (!ready || serverReady) {
return;
}
clearInterval(appReadyInterval);
serverReady = true;
cb();
}),
100);
}
function sortModulesFirst(a, b) {
var module = /\.module\.js$/;
var aMod = module.test(a.path);
var bMod = module.test(b.path);
// inject *.module.js first
if (aMod === bMod) {
// either both modules or both non-modules, so just sort normally
if (a.path < b.path) {
return -1;
}
if (a.path > b.path) {
return 1;
}
return 0;
} else {
return (aMod ? -1 : 1);
}
}
/********************
* Reusable pipelines
********************/
let lintClientScripts = lazypipe()
.pipe(plugins.jshint, `${clientPath}/.jshintrc`)
.pipe(plugins.jshint.reporter, 'jshint-stylish');
let lintServerScripts = lazypipe()
.pipe(plugins.jshint, `${serverPath}/.jshintrc`)
.pipe(plugins.jshint.reporter, 'jshint-stylish');
let lintServerTestScripts = lazypipe()
.pipe(plugins.jshint, `${serverPath}/.jshintrc-spec`)
.pipe(plugins.jshint.reporter, 'jshint-stylish');
let styles = lazypipe()
.pipe(plugins.sourcemaps.init)
.pipe(plugins.sass)
.pipe(plugins.autoprefixer, {browsers: ['last 1 version']})
.pipe(plugins.sourcemaps.write, '.');
let transpileClient = lazypipe()
.pipe(plugins.sourcemaps.init)
.pipe(plugins.babel)
.pipe(plugins.sourcemaps.write, '.');
let transpileServer = lazypipe()
.pipe(plugins.sourcemaps.init)
.pipe(plugins.babel, {
plugins: [
'transform-class-properties',
'transform-runtime'
]
})
.pipe(plugins.sourcemaps.write, '.');
let mocha = lazypipe()
.pipe(plugins.mocha, {
reporter: 'spec',
timeout: 5000,
require: [
'./mocha.conf'
]
});
let istanbul = lazypipe()
.pipe(plugins.istanbul.writeReports)
.pipe(plugins.istanbulEnforcer, {
thresholds: {
global: {
lines: 80,
statements: 80,
branches: 80,
functions: 80
}
},
coverageDirectory: './coverage',
rootDirectory : ''
});
/********************
* Env
********************/
gulp.task('env:all', () => {
let localConfig;
try {
localConfig = require(`./${serverPath}/config/local.env`);
} catch (e) {
localConfig = {};
}
plugins.env({
vars: localConfig
});
});
gulp.task('env:test', () => {
plugins.env({
vars: {NODE_ENV: 'test'}
});
});
gulp.task('env:prod', () => {
plugins.env({
vars: {NODE_ENV: 'production'}
});
});
/********************
* Tasks
********************/
gulp.task('inject', cb => {
runSequence(['inject:js', 'inject:css', 'inject:scss'], cb);
});
gulp.task('inject:js', () => {
return gulp.src(paths.client.mainView)
.pipe(plugins.inject(
gulp.src(_.union(paths.client.scripts, [`!${clientPath}/**/*.{spec,mock}.js`, `!${clientPath}/app/app.js`]), {read: false})
.pipe(plugins.sort(sortModulesFirst)),
{
starttag: '<!-- injector:js -->',
endtag: '<!-- endinjector -->',
transform: (filepath) => '<script src="' + filepath.replace(`/${clientPath}/`, '') + '"></script>'
}))
.pipe(gulp.dest(clientPath));
});
gulp.task('inject:css', () => {
return gulp.src(paths.client.mainView)
.pipe(plugins.inject(
gulp.src(`${clientPath}/{app,components}/**/*.css`, {read: false})
.pipe(plugins.sort()),
{
starttag: '<!-- injector:css -->',
endtag: '<!-- endinjector -->',
transform: (filepath) => '<link rel="stylesheet" href="' + filepath.replace(`/${clientPath}/`, '').replace('/.tmp/', '') + '">'
}))
.pipe(gulp.dest(clientPath));
});
gulp.task('inject:scss', () => {
return gulp.src(paths.client.mainStyle)
.pipe(plugins.inject(
gulp.src(_.union(paths.client.styles, ['!' + paths.client.mainStyle]), {read: false})
.pipe(plugins.sort()),
{
transform: (filepath) => {
let newPath = filepath
.replace(`/${clientPath}/app/`, '')
.replace(`/${clientPath}/components/`, '../components/')
.replace(/_(.*).scss/, (match, p1, offset, string) => p1)
.replace('.scss', '');
return `@import '${newPath}';`;
}
}))
.pipe(gulp.dest(`${clientPath}/app`));
});
gulp.task('styles', () => {
return gulp.src(paths.client.mainStyle)
.pipe(styles())
.pipe(gulp.dest('.tmp/app'));
});
gulp.task('transpile:client', () => {
return gulp.src(paths.client.scripts)
.pipe(transpileClient())
.pipe(gulp.dest('.tmp'));
});
gulp.task('transpile:server', () => {
return gulp.src(_.union(paths.server.scripts, paths.server.json))
.pipe(transpileServer())
.pipe(gulp.dest(`${paths.dist}/${serverPath}`));
});
gulp.task('lint:scripts', cb => runSequence(['lint:scripts:client', 'lint:scripts:server'], cb));
gulp.task('lint:scripts:client', () => {
return gulp.src(_.union(
paths.client.scripts,
_.map(paths.client.test, blob => '!' + blob),
[`!${clientPath}/app/app.constant.js`]
))
.pipe(lintClientScripts());
});
gulp.task('lint:scripts:server', () => {
return gulp.src(_.union(paths.server.scripts, _.map(paths.server.test, blob => '!' + blob)))
.pipe(lintServerScripts());
});
gulp.task('lint:scripts:clientTest', () => {
return gulp.src(paths.client.test)
.pipe(lintClientScripts());
});
gulp.task('lint:scripts:serverTest', () => {
return gulp.src(paths.server.test)
.pipe(lintServerTestScripts());
});
gulp.task('jscs', () => {
return gulp.src(_.union(paths.client.scripts, paths.server.scripts))
.pipe(plugins.jscs())
.pipe(plugins.jscs.reporter());
});
gulp.task('clean:tmp', () => del(['.tmp/**/*'], {dot: true}));
gulp.task('start:client', cb => {
whenServerReady(() => {
open('http://localhost:' + config.port);
cb();
});
});
gulp.task('start:server', () => {
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
config = require(`./${serverPath}/config/environment`);
nodemon(`-w ${serverPath} ${serverPath}`)
.on('log', onServerLog);
});
gulp.task('start:server:prod', () => {
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
config = require(`./${paths.dist}/${serverPath}/config/environment`);
nodemon(`-w ${paths.dist}/${serverPath} ${paths.dist}/${serverPath}`)
.on('log', onServerLog);
});
gulp.task('start:inspector', () => {
gulp.src([])
.pipe(plugins.nodeInspector());
});
gulp.task('start:server:debug', () => {
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
config = require(`./${serverPath}/config/environment`);
nodemon(`-w ${serverPath} --debug-brk ${serverPath}`)
.on('log', onServerLog);
});
gulp.task('watch', () => {
var testFiles = _.union(paths.client.test, paths.server.test.unit, paths.server.test.integration);
plugins.livereload.listen();
plugins.watch(paths.client.styles, () => { //['inject:scss']
gulp.src(paths.client.mainStyle)
.pipe(plugins.plumber())
.pipe(styles())
.pipe(gulp.dest('.tmp/app'))
.pipe(plugins.livereload());
});
plugins.watch(paths.client.views)
.pipe(plugins.plumber())
.pipe(plugins.livereload());
plugins.watch(paths.client.scripts) //['inject:js']
.pipe(plugins.plumber())
.pipe(transpileClient())
.pipe(gulp.dest('.tmp'))
.pipe(plugins.livereload());
plugins.watch(_.union(paths.server.scripts, testFiles))
.pipe(plugins.plumber())
.pipe(lintServerScripts())
.pipe(plugins.livereload());
gulp.watch('bower.json', ['wiredep:client']);
});
gulp.task('serve', cb => {
runSequence(['clean:tmp', 'constant', 'env:all'],
['lint:scripts', 'inject'],
['wiredep:client'],
['transpile:client', 'styles'],
['start:server', 'start:client'],
'watch',
cb);
});
gulp.task('serve:dist', cb => {
runSequence(
'build',
'env:all',
'env:prod',
['start:server:prod', 'start:client'],
cb);
});
gulp.task('serve:debug', cb => {
runSequence(['clean:tmp', 'constant'],
['lint:scripts', 'inject'],
['wiredep:client'],
['transpile:client', 'styles'],
'start:inspector',
['start:server:debug', 'start:client'],
'watch',
cb);
});
gulp.task('test', cb => {
return runSequence('test:server', 'test:client', cb);
});
gulp.task('test:server', cb => {
runSequence(
'env:all',
'env:test',
'mocha:unit',
'mocha:integration',
'mocha:coverage',
cb);
});
gulp.task('mocha:unit', () => {
return gulp.src(paths.server.test.unit)
.pipe(mocha());
});
gulp.task('mocha:integration', () => {
return gulp.src(paths.server.test.integration)
.pipe(mocha());
});
gulp.task('test:client', ['wiredep:test', 'constant'], (done) => {
new KarmaServer({
configFile: `${__dirname}/${paths.karma}`,
singleRun: true
}, done).start();
});
// inject bower components
gulp.task('wiredep:client', () => {
return gulp.src(paths.client.mainView)
.pipe(wiredep({
exclude: [
/bootstrap.js/,
'/json3/',
'/es5-shim/',
/font-awesome\.css/,
/bootstrap\.css/,
/bootstrap-sass-official/,
/bootstrap-social\.css/
],
ignorePath: clientPath
}))
.pipe(gulp.dest(`${clientPath}/`));
});
gulp.task('wiredep:test', () => {
return gulp.src(paths.karma)
.pipe(wiredep({
exclude: [
/bootstrap.js/,
'/json3/',
'/es5-shim/',
/font-awesome\.css/,
/bootstrap\.css/,
/bootstrap-sass-official/,
/bootstrap-social\.css/
],
devDependencies: true
}))
.pipe(gulp.dest('./'));
});
/********************
* Build
********************/
//FIXME: looks like font-awesome isn't getting loaded
gulp.task('build', cb => {
runSequence(
[
'clean:dist',
'clean:tmp'
],
'inject',
'wiredep:client',
[
'transpile:client',
'transpile:server'
],
[
'build:images',
'copy:extras',
'copy:fonts',
'copy:fonts-extras',
'copy:views',
'copy:assets',
'copy:server',
'build:client'
],
cb);
});
gulp.task('clean:dist', () => del([`${paths.dist}/!(.git*|.openshift|Procfile)**`], {dot: true}));
gulp.task('build:client', ['styles', 'html', 'constant', 'build:images'], () => {
var manifest = gulp.src(`${paths.dist}/${clientPath}/assets/rev-manifest.json`);
var appFilter = plugins.filter('**/app.js', {restore: true});
var jsFilter = plugins.filter('**/*.js', {restore: true});
var cssFilter = plugins.filter('**/*.css', {restore: true});
var htmlBlock = plugins.filter(['**/*.!(html)'], {restore: true});
return gulp.src(paths.client.mainView)
.pipe(plugins.useref())
.pipe(appFilter)
.pipe(plugins.addSrc.append('.tmp/templates.js'))
.pipe(plugins.concat('app/app.js'))
.pipe(appFilter.restore)
.pipe(jsFilter)
.pipe(plugins.ngAnnotate())
.pipe(plugins.uglify())
.pipe(jsFilter.restore)
.pipe(cssFilter)
.pipe(plugins.cleanCss({
processImportFrom: ['!fonts.googleapis.com']
}))
.pipe(cssFilter.restore)
.pipe(htmlBlock)
.pipe(plugins.rev())
.pipe(htmlBlock.restore)
.pipe(plugins.revReplace({manifest}))
.pipe(gulp.dest(`${paths.dist}/${clientPath}`));
});
gulp.task('html', function() {
return gulp.src(`${clientPath}/{app,components}/**/*.html`)
.pipe(plugins.angularTemplatecache({
module: 'tracker2App'
}))
.pipe(gulp.dest('.tmp'));
});
gulp.task('constant', function() {
let sharedConfig = require(`./${serverPath}/config/environment/shared`);
return plugins.ngConstant({
name: 'tracker2App.constants',
deps: [],
wrap: true,
stream: true,
constants: { appConfig: sharedConfig }
})
.pipe(plugins.rename({
basename: 'app.constant'
}))
.pipe(gulp.dest(`${clientPath}/app/`))
});
gulp.task('build:images', () => {
return gulp.src(paths.client.images)
.pipe(plugins.imagemin({
optimizationLevel: 5,
progressive: true,
interlaced: true
}))
.pipe(plugins.rev())
.pipe(gulp.dest(`${paths.dist}/${clientPath}/assets/images`))
.pipe(plugins.rev.manifest(`${paths.dist}/${clientPath}/assets/rev-manifest.json`, {
base: `${paths.dist}/${clientPath}/assets`,
merge: true
}))
.pipe(gulp.dest(`${paths.dist}/${clientPath}/assets`));
});
gulp.task('copy:extras', () => {
return gulp.src([
`${clientPath}/favicon.ico`,
`${clientPath}/robots.txt`,
`${clientPath}/.htaccess`
], { dot: true })
.pipe(gulp.dest(`${paths.dist}/${clientPath}`));
});
gulp.task('copy:fonts', () => {
return gulp.src([
`${clientPath}/bower_components/{bootstrap,font-awesome}/fonts/**/*`
], { dot: true })
.pipe(gulp.dest(`${paths.dist}/${clientPath}/bower_components`));
});
gulp.task('copy:fonts-extras', () => {
return gulp.src([
`${clientPath}/bower_components/bootstrap/fonts/*`
], { dot: true })
.pipe(gulp.dest(`${paths.dist}/${clientPath}/bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/`));
});
gulp.task('copy:views', () => {
return gulp.src([
'server/views/*'
], { dot: true })
.pipe(gulp.dest(`${paths.dist}/server/views/`));
});
gulp.task('copy:assets', () => {
return gulp.src([paths.client.assets, '!' + paths.client.images])
.pipe(gulp.dest(`${paths.dist}/${clientPath}/assets`));
});
gulp.task('copy:server', () => {
return gulp.src([
'package.json',
'bower.json',
'.bowerrc'
], {cwdbase: true})
.pipe(gulp.dest(paths.dist));
});
gulp.task('coverage:pre', () => {
return gulp.src(paths.server.scripts)
// Covering files
.pipe(plugins.istanbul({
instrumenter: Instrumenter, // Use the isparta instrumenter (code coverage for ES6)
includeUntested: true
}))
// Force `require` to return covered files
.pipe(plugins.istanbul.hookRequire());
});
gulp.task('coverage:unit', () => {
return gulp.src(paths.server.test.unit)
.pipe(mocha())
.pipe(istanbul())
// Creating the reports after tests ran
});
gulp.task('coverage:integration', () => {
return gulp.src(paths.server.test.integration)
.pipe(mocha())
.pipe(istanbul())
// Creating the reports after tests ran
});
gulp.task('mocha:coverage', cb => {
runSequence('coverage:pre',
'env:all',
'env:test',
'coverage:unit',
'coverage:integration',
cb);
});
// Downloads the selenium webdriver
gulp.task('webdriver_update', webdriver_update);
gulp.task('test:e2e', ['env:all', 'env:test', 'start:server', 'webdriver_update'], cb => {
gulp.src(paths.client.e2e)
.pipe(protractor({
configFile: 'protractor.conf.js',
})).on('error', err => {
console.log(err)
}).on('end', () => {
process.exit();
});
});
/********************
* Grunt ported tasks
********************/
grunt.initConfig({
buildcontrol: {
options: {
dir: paths.dist,
commit: true,
push: true,
connectCommits: false,
message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
},
heroku: {
options: {
remote: 'heroku',
branch: 'master'
}
},
openshift: {
options: {
remote: 'openshift',
branch: 'master'
}
}
}
});
grunt.loadNpmTasks('grunt-build-control');
gulp.task('buildcontrol:heroku', function(done) {
grunt.tasks(
['buildcontrol:heroku'], //you can add more grunt tasks in this array
{gruntfile: false}, //don't look for a Gruntfile - there is none. :-)
function() {done();}
);
});
gulp.task('buildcontrol:openshift', function(done) {
grunt.tasks(
['buildcontrol:openshift'], //you can add more grunt tasks in this array
{gruntfile: false}, //don't look for a Gruntfile - there is none. :-)
function() {done();}
);
});