javascript - grunt 服务删除了 yeoman 生成器 Angular 应用程序依赖项

标签 javascript angularjs node.js yeoman yeoman-generator-angular

我一直在兜圈子,试图弄清楚这个问题。所以我已经和这个项目一起运输了一段时间了,没有出现任何问题。我已经制作了大量的 Angular 命令来构建指令、 Controller 、 View 等。没问题。然后有一天,我运行了 gruntserve:[target],它覆盖了我所做的最后一条指令。奇怪的是。如果我在观看时手动添加它,它会不断删除它。我删除了 index.html 文件中 build:js block 之间的所有依赖关系,并将所有依赖关系添加回来,除了我注意到的那些。 (顺便说一句,还有一些我直到开始挖掘才发现的。他们碰巧是 Controller )这变得更奇怪了。因此,如果我重新添加依赖项并运行 gruntserve:dist,它就在那里。但我必须先将其添加回来,而不是运行 gruntserve:[target],否则 gruntserve 会把它吹走。

这是我注意到这个错误后所做的事情。我已经更新了 yeoman、generator,这也需要 Nodejs 和 npm 更新。我还浏览了其他几个我认为可能相关的 grunt 工具,并更新了所有这些工具。 watch、ngmin、copy等工具

另一方面,我已经测试过这是否仅在我的计算机上或在其他计算机上。它确实发生在其他人身上。它们都是 OSX、yeoman、generator-Angular、nodejs 和 npm 的各种版本。

这是我的 Gruntfile

// Generated on 2015-02-12 using generator-angular 0.10.0
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

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);

	// Configurable paths for the application
	var appConfig = {
		app: require('./bower.json').appPath || 'app',
		dist: 'dist'
	};

	// Define the configuration for all the tasks
	grunt.initConfig({

		// Project settings
		yeoman: appConfig,

		// Watches files for changes and runs tasks based on the changed files
		watch: {
			bower: {
				files: ['bower.json'],
				tasks: ['wiredep']
			},
			js: {
				files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
				tasks: ['newer:jshint:all'],
				options: {
					livereload: '<%= connect.options.livereload %>'
				}
			},
			jsTest: {
				files: ['test/spec/{,*/}*.js'],
				tasks: ['newer:jshint:test', 'karma']
			},
			compass: {
				files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
				tasks: ['compass:server', 'autoprefixer']
			},
			gruntfile: {
				files: ['Gruntfile.js']
			},
			livereload: {
				options: {
					livereload: '<%= connect.options.livereload %>'
				},
				files: [
					'<%= yeoman.app %>/{,*/}*.html',
					'.tmp/styles/{,*/}*.css',
					'<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
				]
			}
		},

		// The actual grunt server settings
		connect: {
			options: {
				port: 9000,
				// Change this to '0.0.0.0' to access the server from outside.
				hostname: 'localhost',
				livereload: 35729
			},
			livereload: {
				options: {
					open: true,
					middleware: function (connect) {
						return [
							connect.static('.tmp'),
							connect().use(
								'/bower_components',
								connect.static('./bower_components')
							),
							connect.static(appConfig.app)
						];
					}
				}
			},
			test: {
				options: {
					port: 9001,
					middleware: function (connect) {
						return [
							connect.static('.tmp'),
							connect.static('test'),
							connect().use(
								'/bower_components',
								connect.static('./bower_components')
							),
							connect.static(appConfig.app)
						];
					}
				}
			},
			dist: {
				options: {
					open: true,
					base: '<%= yeoman.dist %>'
				}
			}
		},

		// Make sure code styles are up to par and there are no obvious mistakes
		jshint: {
			options: {
				jshintrc: '.jshintrc',
				reporter: require('jshint-stylish')
			},
			all: {
				src: [
					'Gruntfile.js',
					'<%= yeoman.app %>/scripts/{,*/}*.js'
				]
			},
			test: {
				options: {
					jshintrc: 'test/.jshintrc'
				},
				src: ['test/spec/{,*/}*.js']
			}
		},

		// Empties folders to start fresh
		clean: {
			dist: {
				files: [{
					dot: true,
					src: [
						'.tmp',
						'<%= yeoman.dist %>/{,*/}*',
						'!<%= yeoman.dist %>/.git{,*/}*'
					]
				}]
			},
			server: '.tmp'
		},

		// Add vendor prefixed styles
		autoprefixer: {
			options: {
				browsers: ['last 1 version']
			},
			dist: {
				files: [{
					expand: true,
					cwd: '.tmp/styles/',
					src: '{,*/}*.css',
					dest: '.tmp/styles/'
				}]
			}
		},

		// Automatically inject Bower components into the app
		wiredep: {
			app: {
				src: ['<%= yeoman.app %>/index.html'],
				ignorePath:  /\.\.\//
			},
			sass: {
				src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
				ignorePath: /(\.\.\/){1,2}bower_components\//
			}
		},

		// Compiles Sass to CSS and generates necessary files if requested
		compass: {
			options: {
				sassDir: '<%= yeoman.app %>/styles',
				cssDir: '.tmp/styles',
				generatedImagesDir: '.tmp/images/generated',
				imagesDir: '<%= yeoman.app %>/images',
				javascriptsDir: '<%= yeoman.app %>/scripts',
				fontsDir: '<%= yeoman.app %>/styles/fonts',
				importPath: './bower_components',
				httpImagesPath: '/images',
				httpGeneratedImagesPath: '/images/generated',
				httpFontsPath: '/styles/fonts',
				relativeAssets: false,
				assetCacheBuster: false,
				raw: 'Sass::Script::Number.precision = 10\n',
				sourcemap: true
			},
			dist: {
				options: {
					generatedImagesDir: '<%= yeoman.dist %>/images/generated'
				}
			},
			server: {
				options: {
					debugInfo: true
				}
			}
		},

		// Renames files for browser caching purposes
		filerev: {
			dist: {
				src: [
					'<%= yeoman.dist %>/scripts/{,*/}*.js',
					'<%= yeoman.dist %>/styles/{,*/}*.css',
					'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
					'<%= yeoman.dist %>/styles/fonts/*'
				]
			}
		},

		// 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 %>',
				flow: {
					html: {
						steps: {
							js: ['concat', 'uglifyjs'],
							css: ['cssmin']
						},
						post: {}
					}
				}
			}
		},

		// Performs rewrites based on filerev and the useminPrepare configuration
		usemin: {
			html: ['<%= yeoman.dist %>/{,*/}*.html'],
			css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
			options: {
				assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
			}
		},

		// The following *-min tasks will produce minified files in the dist folder
		// 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 %>/styles/main.css': [
		//         '.tmp/styles/{,*/}*.css'
		//       ]
		//     }
		//   }
		// },
		// uglify: {
		//   dist: {
		//     files: {
		//       '<%= yeoman.dist %>/scripts/scripts.js': [
		//         '<%= yeoman.dist %>/scripts/scripts.js'
		//       ]
		//     }
		//   }
		// },
		// concat: {
		//   dist: {}
		// },

		imagemin: {
			dist: {
				files: [{
					expand: true,
					cwd: '<%= yeoman.app %>/images',
					src: '{,*/}*.{png,jpg,jpeg,gif}',
					dest: '<%= yeoman.dist %>/images'
				}]
			}
		},

		svgmin: {
			dist: {
				files: [{
					expand: true,
					cwd: '<%= yeoman.app %>/images',
					src: '{,*/}*.svg',
					dest: '<%= yeoman.dist %>/images'
				}]
			}
		},

		htmlmin: {
			dist: {
				options: {
					collapseWhitespace: true,
					conservativeCollapse: true,
					collapseBooleanAttributes: true,
					removeCommentsFromCDATA: true,
					removeOptionalTags: true
				},
				files: [{
					expand: true,
					cwd: '<%= yeoman.dist %>',
					src: ['*.html', 'views/{,*/}*.html'],
					dest: '<%= yeoman.dist %>'
				}]
			}
		},

		// ng-annotate tries to make the code safe for minification automatically
		// by using the Angular long form for dependency injection.
		ngAnnotate: {
			dist: {
				files: [{
					expand: true,
					cwd: '.tmp/concat/scripts',
					src: ['*.js', '!oldieshim.js'],
					dest: '.tmp/concat/scripts'
				}]
			}
		},

		// Replace Google CDN references
		cdnify: {
			dist: {
				html: ['<%= yeoman.dist %>/*.html']
			}
		},

		// Copies remaining files to places other tasks can use
		copy: {
			dist: {
				files: [{
					expand: true,
					dot: true,
					cwd: '<%= yeoman.app %>',
					dest: '<%= yeoman.dist %>',
					src: [
						'*.{ico,png,txt}',
						'.htaccess',
						'*.html',
						'views/{,*/}*.html',
						'images/{,*/}*.{webp}',
						'fonts/{,*/}*.*'
					]
				}, {
					expand: true,
					cwd: '.tmp/images',
					dest: '<%= yeoman.dist %>/images',
					src: ['generated/*']
				}, {
					expand: true,
					cwd: '.',
					src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
					dest: '<%= yeoman.dist %>'
				}, {
					expand: true,
					cwd: 'bower_components/components-font-awesome/fonts',
					dest: '<%= yeoman.dist %>/fonts',
					src: './**'
				}]
			},
			styles: {
				expand: true,
				cwd: '<%= yeoman.app %>/styles',
				dest: '.tmp/styles/',
				src: '{,*/}*.css'
			}
		},

		// Run some tasks in parallel to speed up the build process
		concurrent: {
			server: [
				'compass:server'
			],
			test: [
				'compass'
			],
			dist: [
				'compass:dist',
				'imagemin',
				'svgmin'
			]
		},

		// Test settings
		karma: {
			unit: {
				configFile: 'test/karma.conf.js',
				singleRun: true
			}
		},

		// Bump
		bump: {
			options: {
				files: [
					'package.json'
					, 'bower.json'
				]
				, updateConfigs: []
				// , commit: true
				// , commitMessage: 'Release v%VERSION%'
				// , commitFiles: [
				// 	'package.json'
				// 	, 'bower.json'
				// ]
				// , createTag: true
				// , tagName: 'v%VERSION%'
				// , tagMessage: 'Version %VERSION%'
				// , push: true
				// , pushTo: 'upstream'
				, gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d'
				, globalReplace: false
			}
		},

		ngconstant: {
			options: {
				space: '  ',
				wrap: '"use strict";\n\n {%= __ngModule %}',
				name: 'config',
				dest: '<%= yeoman.app %>/scripts/config.js'
			},
			// Environment targets
			dev: {
				constants: {
					ENV: 'development',
					debug: true,
					API_URL: 'https://some-api.staging.example.com',
					AUTH_URL: 'https://auth.staging.example.com',
					GD_API: 'https://bi.example.com'
				}
			},
			production: {
				constants: {
					ENV: 'production',
					debug: false,
					API_URL: 'https://some-api.example.com',
					AUTH_URL: 'https://auth.example.com',
					GD_API: 'https://bi.example.com'
				}
			},
			staging: {
				constants: {
					ENV: 'staging',
					debug: false,
					API_URL: 'https://some-api.staging.example.com',
					AUTH_URL: 'https://auth.staging.example.com',
					GD_API: 'https://bi.example.com'
				}
			}
		},

		// Git Hook for Bumping version on each push/pre-commit
		githooks: {
			all: {
				options: {
					template: 'hooks/pre-commit.js'
				},
				'pre-commit': 'bump'
			}
		},

		template: {
			'process-html-template': {
				options: {
					data: {
						version: appConfig.version,
						date: new Date()
					}
				},
				files: {
					'app/index.html': ['app/index.html.tpl']
				}
			}
		}
	});


	grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
		if(target === 'dist')
			return grunt.task.run(['build', 'connect:dist:keepalive']);

		if(target === 'dist2')
			return grunt.task.run(['build:staging', 'connect:dist:keepalive']);

		if(!target)
			target = 'staging'

		grunt.task.run([
			'clean:server',
			'ngconstant:' + target,
			'wiredep',
			'concurrent:server',
			'autoprefixer',
			'connect:livereload',
			'template',
			'watch'
		]);
	});

	grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
		grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
		grunt.task.run(['serve:' + target]);
	});

	grunt.registerTask('test', [
		'clean:server',
		'concurrent:test',
		'autoprefixer',
		'connect:test',
		'karma'
	]);

	grunt.registerTask('build', function(target){
		target = target || 'production'
		grunt.task.run([
			'clean:dist',
			'ngconstant:' + target,
			'wiredep',
			'useminPrepare',
			'concurrent:dist',
			'autoprefixer',
			'concat',
			'ngAnnotate',
			'copy:dist',
			'cdnify',
			'cssmin',
			'uglify',
			'filerev',
			'usemin',
			'htmlmin'
		]);
	});

	grunt.registerTask('default', [
		'newer:jshint',
		'test',
		'build'
	]);
};

这是我的index.html

<!doctype html>
<html class="no-js">
	<head>
		<meta charset="utf-8">
		<title>Title</title>
		<meta name="description" content="">
		<meta name="viewport" content="width=device-width">
		<meta name="version" content=" - Thu Feb 12 2015 11:25:22 GMT-0800 (PST)">
		<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
		<!-- build:css(.) styles/vendor.css -->
		<!-- bower:css -->
		<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
		<link rel="stylesheet" href="bower_components/seiyria-bootstrap-slider/dist/css/bootstrap-slider.css" />
		<link rel="stylesheet" href="bower_components/components-font-awesome/css/font-awesome.css" />
		<!-- endbower -->
		<!-- endbuild -->
		<!-- build:css(.tmp) styles/main.css -->
		<link rel="stylesheet" href="styles/main.css">
		<link rel="stylesheet" href="styles/ticker.css">
		<!-- endbuild -->
	</head>
	<body
		ng-app="app"
		ng-controller="GlobalCtrl"
		ng-class="{blueBg: state.includes('home')}"
	>
		<!--[if lt IE 7]>
			<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
		<![endif]-->

		<div class="container">

			<header
				id="header"
				ng-controller="HeaderCtrl"
				ng-include="'views/header.html'"
				class="navbar-fixed-top"
			>
			</header>

			<div
				ui-view=""
				id="content"
				class="view-animate"
			>
			</div>

		</div>

		<!-- Google Analytics: change UA-XXXXX-X to be your site's ID -->
		 <script>
			 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
			 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
			 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
			 })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

			 ga('create', 'UA-XXXX', 'auto');
			 ga('set', 'forceSSL', true);
		</script>

		<!-- build:js(.) scripts/oldieshim.js -->
		<!--[if lt IE 9]>
		<script src="bower_components/es5-shim/es5-shim.js"></script>
		<script src="bower_components/json3/lib/json3.js"></script>
		<![endif]-->
		<!-- endbuild -->

		<!-- build:js(.) scripts/vendor.js -->
		<!-- bower:js -->
		<script src="bower_components/modernizr/modernizr.js"></script>
		<script src="bower_components/jquery/dist/jquery.js"></script>
		<script src="bower_components/angular/angular.js"></script>
		<script src="bower_components/angular-resource/angular-resource.js"></script>
		<script src="bower_components/angular-sanitize/angular-sanitize.js"></script>
		<script src="bower_components/angular-route/angular-route.js"></script>
		<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
		<script src="bower_components/bootstrap/dist/js/bootstrap.js"></script>
		<script src="bower_components/seiyria-bootstrap-slider/js/bootstrap-slider.js"></script>
		<script
		src="bower_components/venturocket-angular-slider/build/angular-slider.js"></script>
		<script src="bower_components/angucomplete/angucomplete.js"></script>
		<script src="bower_components/raphael/raphael.js"></script>
		<script src="bower_components/prefixfree/prefixfree.min.js"></script>
		<script src="bower_components/numeral/numeral.js"></script>
		<script src="bower_components/ui-router/release/angular-ui-router.js"></script>
		<script src="bower_components/angular-animate/angular-animate.js"></script>
		<script src="bower_components/angular-ui-router/release/angular-ui-router.js"></script>
		<script src="bower_components/angular-ui-utils/ui-utils.js"></script>
		<script src="bower_components/angular-cookies/angular-cookies.js"></script>
		<script src="bower_components/angular-touch/angular-touch.js"></script>
		<script src="bower_components/ui-router-extras/release/ct-ui-router-extras.js"></script>
		<script src="bower_components/angular-logex/dist/log-ex-unobtrusive.js"></script>
		<script src="bower_components/jquerykeyframes/jquery.keyframes.js"></script>
		<script src="bower_components/ng-file-upload/angular-file-upload-shim.js"></script>
		<script src="bower_components/ng-file-upload/angular-file-upload.js"></script>
		<!-- endbower -->
		<!-- endbuild -->
		<!-- build:js({.tmp,app}) scripts/scripts.js -->
		<script src="scripts/app.js"></script>
		<script src="/scripts/config.js"></script>
		<script src="scripts/controllers/home.js"></script>
		<script src="scripts/controllers/navigation.js"></script>
		<script src="scripts/controllers/global.js"></script>
		<script src="scripts/filters/capitalize.js"></script>
		<script src="scripts/filters/currencyabbr.js"></script>
		<script src="scripts/controllers/campaigns.js"></script>
		<script src="scripts/controllers/performance.js"></script>
		<script src="scripts/services/audience.js"></script>
		<script src="scripts/services/user.js"></script>
		<script src="scripts/services/gooddata.js"></script>
		<script src="scripts/services/polaris_campaign.js"></script>
		<script src="scripts/controllers/audiences.js"></script>
		<script src="scripts/services/country.js"></script>
		<script src="scripts/services/industry.js"></script>
		<script src="scripts/services/pagefilter.js"></script>
		<script src="scripts/lib/top5.js"></script>
		<script src="scripts/lib/tooltip.js"></script>
		<script src="scripts/directives/top5chart.js"></script>
		<script src="scripts/directives/ticker.js"></script>
		<script src="scripts/directives/funnel.js"></script>
		<script src="scripts/directives/spinner.js"></script>
		<script src="scripts/services/summary.js"></script>
		<script src="scripts/directives/mostactive.js"></script>
		<script src="scripts/directives/trend.js"></script>
		<script src="scripts/services/helper.js"></script>
		<script src="scripts/controllers/header.js"></script>
		<script src="scripts/services/navigation.js"></script>
		<script src="scripts/controllers/audiencesedit.js"></script>
		<script src="scripts/controllers/audiencescreate.js"></script>
		<script src="scripts/controllers/audienceslist.js"></script>
		<script src="scripts/controllers/modal.js"></script>
		<script src="scripts/services/locations.js"></script>
		<script src="scripts/services/paginator.js"></script>
		<script src="scripts/services/audienceservice.js"></script>
		<script src="scripts/controllers/audiencescreateexisting.js"></script>
		<script src="scripts/controllers/audiencescreateexistinglist.js"></script>
		<script src="scripts/controllers/audiencescreateexistingedit.js"></script>
		<script src="scripts/controllers/audiencescreatecustom.js"></script>
		<script src="scripts/controllers/audiencesupload.js"></script>
		<script src="scripts/services/campaign.js"></script>
		<script src="scripts/services/modal.js"></script>
		<script src="scripts/services/spinner.js"></script>
		<script src="scripts/directives/spinner.js"></script>
		<script src="scripts/controllers/audiencemodal.js"></script>
		<!-- endbuild -->
</body>
</html>

这是我的 package.json

{
  "name": "app",
  "version": "0.4.7",
  "description": "Description",
  "author": "John Smith <jsmith@company.com>",
  "contributors": [
  	{
  		"name": "John Smith",
  		"email": "jsmith@company.com"
  	}
  ],
  "repository": {
  	"type": "git",
  	"url": "https://github.com/Demandbase/app"
  },
  "dependencies": {
    "ui-router": "0.0.1"
  },
  "devDependencies": {
    "execSync": "^1.0.2",
    "grunt": "^0.4.1",
    "grunt-autoprefixer": "2.2.*",
    "grunt-bump": "0.0.16",
    "grunt-concurrent": "1.0.*",
    "grunt-contrib-clean": "^0.5.0",
    "grunt-contrib-compass": "^1.0.*",
    "grunt-contrib-concat": "^0.4.0",
    "grunt-contrib-connect": "^0.7.1",
    "grunt-contrib-copy": "0.7.*",
    "grunt-contrib-cssmin": "^0.12.*",
    "grunt-contrib-htmlmin": "0.4.*",
    "grunt-contrib-imagemin": "^0.8.1",
    "grunt-contrib-jshint": "^0.10.0",
    "grunt-contrib-uglify": "^0.4.0",
    "grunt-contrib-watch": "^0.6.*",
    "grunt-filerev": "2.1.*",
    "grunt-githooks": "^0.3.1",
    "grunt-google-cdn": "^0.4.0",
    "grunt-karma": "^0.9.0",
    "grunt-newer": "^0.7.0",
    "grunt-ng-annotate": "0.9.*",
    "grunt-ng-constant": "^1.0.0",
    "grunt-svgmin": "^0.4.0",
    "grunt-template": "^0.2.3",
    "grunt-usemin": "3.0.*",
    "grunt-wiredep": "^2.0.*",
    "jshint-stylish": "^0.2.0",
    "karma": "^0.12.24",
    "karma-coverage": "^0.2.6",
    "karma-jasmine": "^0.1.5",
    "karma-phantomjs-launcher": "^0.1.4",
    "load-grunt-tasks": "^3.1.*",
    "time-grunt": "^0.3.1"
  },
  "engines": {
    "node": ">=0.10.0"
  },
  "scripts": {
    "test": "grunt test"
  },
  "private": true
}

这是我的所有版本(请记住,在其他计算机上的各种版本上进行了测试,结果都是相同的) Node :v0.12.0(在 v0.11.* 上测试,结果相同) npm:2.5.1 Yeoman:1.4.5(尽管我在 1.3.3 上也遇到了同样的问题) Generator-Angular:0.11.1(已更新,更新前没有捕获之前的版本) OSX:10.10(也发生在 10.9.4)

请帮忙,我很困惑,我的项目已经停止了,直到我可以添加更多依赖项。

最佳答案

这实际上是 yeoman 的一个功能:wiredep

它使 Bower.json 的依赖关系与 <!-- bower:js --> <!-- endbower --> 之间的依赖关系保持同步。和<!-- bower:css --> <!-- endbower -->

删除这些特殊标签或从 gruntfile 末尾的服务命令中禁用 wiredep 任务以进行快速修复。

另一种选择是实际使用wiredep来管理您的依赖项,只需告诉bower保存您安装的内容,例如

bower install angular --save

它会将其添加到 Bower.json 文件中,然后 gruntserve 会将其放入 index.html 中,神奇!

关于javascript - grunt 服务删除了 yeoman 生成器 Angular 应用程序依赖项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28486918/

相关文章:

javascript - 如果我可以使用 $scope.$root,为什么还要使用 $rootScope?

javascript - 返回语句在带有 Angular 的 JavaScript 中不起作用

python - 无法在 Azure Web 应用程序上运行 EXE 文件

javascript - 多次调用异步函数

javascript - 从用户集合中获取每个文档 Firebase

javascript - jQuery - append() 方法因空格而无法正常工作

javascript - 通过键 JavaScript 从关联数组调用函数

angularjs - Angular : How to change the data type using $formatters and $parsers?

node.js - NPM 安装无法与代理周围的 Artifactory 一起使用

javascript - React Native - onChangeText 和 onSubmitEditing 之间的区别?