[英]Ionic cli exiting after grunt serve
I'm having an issue trying to run my ionic project. 我在尝试运行离子项目时遇到了问题。 Upon exacting "grunt serve", it performs the various tasks specified in the Grunt file except it stops and exits the Ionic CLI immediately after it opens the ionic CLI.
严格执行“grunt serve”后,它会执行Grunt文件中指定的各种任务,除非它在打开离子CLI后立即停止并退出Ionic CLI。 I will provide a snapshot of the terminal here:
我将在这里提供终端的快照:
Has anyone else ever had this issue? 还有其他人有过这个问题吗? I would appreciate any and all help.
我将不胜感激任何帮助。 Thank you so much for any response!
非常感谢您的回复!
EDIT: I'm not entirely sure if it matters, however upon installation, the program was unable to install the cordova-whitelist plugin. 编辑:我不完全确定它是否重要,但是在安装时,程序无法安装cordova-whitelist插件。
EDIT 2: Here is the Gruntfile.js 编辑2:这是Gruntfile.js
' “
// Generated on 2015-05-22 using generator-ionic 0.7.3
'use strict';
var _ = require('lodash');
var path = require('path');
var cordovaCli = require('cordova');
var spawn = process.platform === 'win32' ? require('win-spawn') : require('child_process').spawn;
module.exports = function (grunt) {
// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);
// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);
// Define the configuration for all the tasks
grunt.initConfig({
// Project settings
yeoman: {
// configurable paths
app: 'app',
scripts: 'scripts',
styles: 'styles',
images: 'images',
test: 'test',
dist: 'www'
},
// Environment Variables for Angular App
// This creates an Angular Module that can be injected via ENV
// Add any desired constants to the ENV objects below.
// https://github.com/diegonetto/generator-ionic/blob/master/docs/FAQ.md#how-do-i-add-constants
ngconstant: {
options: {
space: ' ',
wrap: '"use strict";\n\n {%= __ngModule %}',
name: 'config',
dest: '<%= yeoman.app %>/<%= yeoman.scripts %>/configuration.js'
},
development: {
constants: {
ENV: {
name: 'development',
apiEndpoint: 'http://dev.yoursite.com:10000/'
}
}
},
production: {
constants: {
ENV: {
name: 'production',
apiEndpoint: 'http://api.yoursite.com/'
}
}
}
},
// Watches files for changes and runs tasks based on the changed files
watch: {
bower: {
files: ['bower.json'],
tasks: ['wiredep', 'newer:copy:app']
},
html: {
files: ['<%= yeoman.app %>/**/*.html'],
tasks: ['newer:copy:app']
},
js: {
files: ['<%= yeoman.app %>/<%= yeoman.scripts %>/**/*.js'],
tasks: ['newer:copy:app', 'newer:jshint:all']
},
styles: {
files: ['<%= yeoman.app %>/<%= yeoman.styles %>/**/*.css'],
tasks: ['newer:copy:styles', 'autoprefixer', 'newer:copy:tmp']
},
gruntfile: {
files: ['Gruntfile.js'],
tasks: ['ngconstant:development', 'newer:copy:app']
}
},
// The actual grunt server settings
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost'
},
dist: {
options: {
base: '<%= yeoman.dist %>'
}
},
coverage: {
options: {
port: 9002,
open: true,
base: ['coverage']
}
}
},
// Make sure code styles are up to par and there are no obvious mistakes
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: [
'Gruntfile.js',
'<%= yeoman.app %>/<%= yeoman.scripts %>/**/*.js'
],
test: {
options: {
jshintrc: 'test/.jshintrc'
},
src: ['test/unit/**/*.js']
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [{
dot: true,
src: [
'.temp',
'<%= yeoman.dist %>/*',
'!<%= yeoman.dist %>/.git*'
]
}]
},
server: '.temp'
},
autoprefixer: {
options: {
browsers: ['last 1 version']
},
dist: {
files: [{
expand: true,
cwd: '.temp/<%= yeoman.styles %>/',
src: '{,*/}*.css',
dest: '.temp/<%= yeoman.styles %>/'
}]
}
},
// Automatically inject Bower components into the app
wiredep: {
app: {
src: ['<%= yeoman.app %>/index.html'],
ignorePath: /\.\.\//
}
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
html: '<%= yeoman.app %>/index.html',
options: {
dest: '<%= yeoman.dist %>',
staging: '.temp',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin']
},
post: {}
}
}
}
},
// Performs rewrites based on the useminPrepare configuration
usemin: {
html: ['<%= yeoman.dist %>/**/*.html'],
css: ['<%= yeoman.dist %>/<%= yeoman.styles %>/**/*.css'],
options: {
assetsDirs: ['<%= yeoman.dist %>']
}
},
// The following *-min tasks produce minified files in the dist folder
cssmin: {
options: {
//root: '<%= yeoman.app %>',
noRebase: true
}
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeCommentsFromCDATA: true,
removeOptionalTags: true
},
files: [{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html', 'templates/**/*.html'],
dest: '<%= yeoman.dist %>'
}]
}
},
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>',
src: [
'<%= yeoman.images %>/**/*.{png,jpg,jpeg,gif,webp,svg}',
'*.html',
'templates/**/*.html',
'fonts/*'
]
}, {
expand: true,
cwd: '.temp/<%= yeoman.images %>',
dest: '<%= yeoman.dist %>/<%= yeoman.images %>',
src: ['generated/*']
}]
},
styles: {
expand: true,
cwd: '<%= yeoman.app %>/<%= yeoman.styles %>',
dest: '.temp/<%= yeoman.styles %>/',
src: '{,*/}*.css'
},
fonts: {
expand: true,
cwd: 'app/bower_components/ionic/release/fonts/',
dest: '<%= yeoman.app %>/fonts/',
src: '*'
},
vendor: {
expand: true,
cwd: '<%= yeoman.app %>/vendor',
dest: '.temp/<%= yeoman.styles %>/',
src: '{,*/}*.css'
},
app: {
expand: true,
cwd: '<%= yeoman.app %>',
dest: '<%= yeoman.dist %>/',
src: [
'**/*',
'!**/*.(scss,sass,css)',
]
},
tmp: {
expand: true,
cwd: '.temp',
dest: '<%= yeoman.dist %>/',
src: '**/*'
}
},
concurrent: {
ionic: {
tasks: [],
options: {
logConcurrentOutput: true
}
},
server: [
'copy:styles',
'copy:vendor',
'copy:fonts'
],
test: [
'copy:styles',
'copy:vendor',
'copy:fonts'
],
dist: [
'copy:styles',
'copy:vendor',
'copy:fonts'
]
},
// By default, your `index.html`'s <!-- Usemin block --> will take care of
// minification. These next options are pre-configured if you do not wish
// to use the Usemin blocks.
// cssmin: {
// dist: {
// files: {
// '<%= yeoman.dist %>/<%= yeoman.styles %>/main.css': [
// '.temp/<%= yeoman.styles %>/**/*.css',
// '<%= yeoman.app %>/<%= yeoman.styles %>/**/*.css'
// ]
// }
// }
// },
// uglify: {
// dist: {
// files: {
// '<%= yeoman.dist %>/<%= yeoman.scripts %>/scripts.js': [
// '<%= yeoman.dist %>/<%= yeoman.scripts %>/scripts.js'
// ]
// }
// }
// },
// concat: {
// dist: {}
// },
// Test settings
// These will override any config options in karma.conf.js if you create it.
karma: {
options: {
basePath: '',
frameworks: ['mocha', 'chai'],
files: [
'<%= yeoman.app %>/bower_components/angular/angular.js',
'<%= yeoman.app %>/bower_components/angular-mocks/angular-mocks.js',
'<%= yeoman.app %>/bower_components/angular-animate/angular-animate.js',
'<%= yeoman.app %>/bower_components/angular-sanitize/angular-sanitize.js',
'<%= yeoman.app %>/bower_components/angular-ui-router/release/angular-ui-router.js',
'<%= yeoman.app %>/bower_components/ionic/release/js/ionic.js',
'<%= yeoman.app %>/bower_components/ionic/release/js/ionic-angular.js',
'<%= yeoman.app %>/<%= yeoman.scripts %>/**/*.js',
'<%= yeoman.test %>/mock/**/*.js',
'<%= yeoman.test %>/spec/**/*.js'
],
autoWatch: false,
reporters: ['dots', 'coverage'],
port: 8080,
singleRun: false,
preprocessors: {
// Update this if you change the yeoman config path
'<%= yeoman.app %>/<%= yeoman.scripts %>/**/*.js': ['coverage']
},
coverageReporter: {
reporters: [
{ type: 'html', dir: 'coverage/' },
{ type: 'text-summary' }
]
}
},
unit: {
// Change this to 'Chrome', 'Firefox', etc. Note that you will need
// to install a karma launcher plugin for browsers other than Chrome.
browsers: ['PhantomJS'],
background: true
},
continuous: {
browsers: ['PhantomJS'],
singleRun: true,
}
},
// ngAnnotate tries to make the code safe for minification automatically by
// using the Angular long form for dependency injection.
ngAnnotate: {
dist: {
files: [{
expand: true,
cwd: '.temp/concat/<%= yeoman.scripts %>',
src: '*.js',
dest: '.temp/concat/<%= yeoman.scripts %>'
}]
}
}
});
// Register tasks for all Cordova commands
_.functions(cordovaCli).forEach(function (name) {
grunt.registerTask(name, function () {
this.args.unshift(name.replace('cordova:', ''));
// Handle URL's being split up by Grunt because of `:` characters
if (_.contains(this.args, 'http') || _.contains(this.args, 'https')) {
this.args = this.args.slice(0, -2).concat(_.last(this.args, 2).join(':'));
}
var done = this.async();
var exec = process.platform === 'win32' ? 'cordova.cmd' : 'cordova';
var cmd = path.resolve('./node_modules/cordova/bin', exec);
var flags = process.argv.splice(3);
var child = spawn(cmd, this.args.concat(flags));
child.stdout.on('data', function (data) {
grunt.log.writeln(data);
});
child.stderr.on('data', function (data) {
grunt.log.error(data);
});
child.on('close', function (code) {
code = code ? false : true;
done(code);
});
});
});
// Since Apache Ripple serves assets directly out of their respective platform
// directories, we watch all registered files and then copy all un-built assets
// over to <%= yeoman.dist %>/. Last step is running cordova prepare so we can refresh the ripple
// browser tab to see the changes. Technically ripple runs `cordova prepare` on browser
// refreshes, but at this time you would need to re-run the emulator to see changes.
grunt.registerTask('ripple', ['wiredep', 'newer:copy:app', 'ripple-emulator']);
grunt.registerTask('ripple-emulator', function () {
grunt.config.set('watch', {
all: {
files: _.flatten(_.pluck(grunt.config.get('watch'), 'files')),
tasks: ['newer:copy:app', 'prepare']
}
});
var cmd = path.resolve('./node_modules/ripple-emulator/bin', 'ripple');
var child = spawn(cmd, ['emulate']);
child.stdout.on('data', function (data) {
grunt.log.writeln(data);
});
child.stderr.on('data', function (data) {
grunt.log.error(data);
});
process.on('exit', function (code) {
child.kill('SIGINT');
process.exit(code);
});
return grunt.task.run(['watch']);
});
// Dynamically configure `karma` target of `watch` task so that
// we don't have to run the karma test server as part of `grunt serve`
grunt.registerTask('watch:karma', function () {
var karma = {
files: ['<%= yeoman.app %>/<%= yeoman.scripts %>/**/*.js', '<%= yeoman.test %>/spec/**/*.js'],
tasks: ['newer:jshint:test', 'karma:unit:run']
};
grunt.config.set('watch', karma);
return grunt.task.run(['watch']);
});
// Wrap ionic-cli commands
grunt.registerTask('ionic', function() {
var done = this.async();
var script = path.resolve('./node_modules/ionic/bin/', 'ionic');
var flags = process.argv.splice(3);
var child = spawn(script, this.args.concat(flags), { stdio: 'inherit' });
child.on('close', function (code) {
code = code ? false : true;
done(code);
});
});
grunt.registerTask('test', [
'wiredep',
'clean',
'concurrent:test',
'autoprefixer',
'karma:unit:start',
'watch:karma'
]);
grunt.registerTask('serve', function (target) {
if (target === 'compress') {
return grunt.task.run(['compress', 'ionic:serve']);
}
grunt.config('concurrent.ionic.tasks', ['ionic:serve', 'watch']);
grunt.task.run(['wiredep', 'init', 'concurrent:ionic']);
});
grunt.registerTask('emulate', function() {
grunt.config('concurrent.ionic.tasks', ['ionic:emulate:' + this.args.join(), 'watch']);
return grunt.task.run(['init', 'concurrent:ionic']);
});
grunt.registerTask('run', function() {
grunt.config('concurrent.ionic.tasks', ['ionic:run:' + this.args.join(), 'watch']);
return grunt.task.run(['init', 'concurrent:ionic']);
});
grunt.registerTask('build', function() {
return grunt.task.run(['init', 'ionic:build:' + this.args.join()]);
});
grunt.registerTask('init', [
'clean',
'ngconstant:development',
'wiredep',
'concurrent:server',
'autoprefixer',
'newer:copy:app',
'newer:copy:tmp'
]);
grunt.registerTask('compress', [
'clean',
'ngconstant:production',
'wiredep',
'useminPrepare',
'concurrent:dist',
'autoprefixer',
'concat',
'ngAnnotate',
'copy:dist',
'cssmin',
'uglify',
'usemin',
'htmlmin'
]);
grunt.registerTask('coverage',
['karma:continuous',
'connect:coverage:keepalive'
]);
grunt.registerTask('default', [
'wiredep',
'newer:jshint',
'karma:continuous',
'compress'
]);
};
' “
grunt-concurrent@2.0.0
can not keep running ionic:serve
and watch
tasks in parallel. grunt-concurrent@2.0.0
无法继续运行ionic:serve
并行ionic:serve
和watch
任务。 Details of the issue sindresorhus/grunt-concurrent sindresorhus / grunt-concurrent问题的详细信息
It's possible to solve the issue downgrading to grunt-concurrent@1.0.0
, using the command: 使用以下命令可以解决降级到
grunt-concurrent@1.0.0
的问题:
To fix the issue: 要解决此问题:
npm install grunt-concurrent@1.0.0 --save-dev
Also is posible use grunt-parallel
as alternative grunt-parallel works fine to this case. 也可以使用
grunt-parallel
作为替代grunt-parallel对这种情况很好。
Altenative fix the issue: Altenative解决了这个问题:
grunt-parallel
with npm install grunt-parallel --save-dev
grunt-parallel
与npm install grunt-parallel --save-dev
grunt.initConfig({ parallel: { assets: { options: { grunt: true }, tasks: ['ionic:serve', 'watch'] } } });
Modify the serve task registration grunt.registerTask('serve', ... )
修改服务任务注册
grunt.registerTask('serve', ... )
grunt.config('concurrent.ionic.tasks', ['ionic:serve', 'watch']);
grunt.config('concurrent.ionic.tasks', ['ionic:serve', 'watch']);
grunt.task.run
with grunt.task.run(['wiredep', 'init', 'concurrent:ionic', 'parallel:assets']);
grunt.task.run(['wiredep', 'init', 'concurrent:ionic', 'parallel:assets']);
修改grunt.task.run
grunt.task.run(['wiredep', 'init', 'concurrent:ionic', 'parallel:assets']);
finally, you will see the serve task registration, like this: 最后,您将看到服务任务注册,如下所示:
grunt.registerTask('serve', function (target) { if (target === 'compress') { return grunt.task.run(['compress', 'ionic:serve']); } grunt.task.run(['wiredep', 'init', 'concurrent:ionic', 'parallel:assets']); });
grunt serve
grunt serve
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.