配置 Karma 以使用 requirejs 加载 pegjs
Configure Karma to load pegjs with requirejs
正在尝试使用 PegJS 和 requirejs 测试项目。
我有几个源文件,实现为通过 require API 加载的 AMD 模块(定义)。 目录结构下:
js/
somefile.js
main.js
parser.js
test/
parser.spec.js
我已经编写了一个 parser.js 模块来加载 PegJS 语法文件并使用 PegJS 创建一个 peg 解析器:
define(function() {
'use strict';
var PEG = require('pegjs');
var grammarFile = 'grammar.peg'
return {
parse: function(fs, content, debug) {
var grammar = fs.readFileSync(grammarFile, 'utf8').toString();
// Build parser from grammar
var parser = PEG.buildParser(grammar, { trace: debug });
[...]
这与在命令行上使用 node.js 执行的 main.js 配合得很好。
现在我想使用 karma、jasmine 和 PhantomJS 测试我的项目。我有一个 karma.conf.js 这样的:
frameworks: ['jasmine', 'requirejs'],
files: [
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js',
],
还有一个名为 test-main.js 的 require bootstrap 文件,它是这样配置的:
'use strict';
var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;
// Get a list of all the test files to include
Object.keys(window.__karma__.files).forEach(function(file) {
console.log(file);
if (TEST_REGEXP.test(file)) {
// Normalize paths to RequireJS module names.
// If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
// then do not normalize the paths
var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
allTestFiles.push(file);
}
});
require.config({
// Karma serves files under /base, which is the basePath from your config file
baseUrl: '/base/js',
// dynamically load all test files
deps: allTestFiles,
// we have to kickoff jasmine, as it is asynchronous
callback: window.__karma__.start
});
现在,当我启动测试 (grunt karma
) 时,我收到了这个错误:
PhantomJS 1.9.8 (Linux 0.0.0) ERROR: Error{message: 'Module name "pegjs" has not been loaded yet for context: _. Use require([])
所以我尝试用这种方式在Karma加载的文件中包含pegjs karma.conf.js:
files: [
{ pattern: 'node_modules/pegjs/lib/**/*.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
当我这样做时,我仍然得到一个错误:
Error: Module name "utils/arrays" has not been loaded yet for context: _. Use require([])
查看pegjs模块内部,确实有一个arrays.js文件:
compiler/
compiler.js
grammar-error.js
parser.js
peg.js
utils/
arrays.js
classes.js
objects.js
所以也尝试包含数组:
files: [
{ pattern: 'node_modules/pegjs/lib/utils/arrays.js', included: true },
{ pattern: 'node_modules/pegjs/lib/**/*.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
我得到:
ReferenceError: Can't find variable: module
at /blabla/node_modules/pegjs/lib/utils/arrays.js:108
因为:
108 module.exports = arrays;
所以,为了加载 npm 模块,我尝试以这种方式加载 bower 模块:
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
又来了:
PhantomJS 1.9.8 (Linux 0.0.0) ERROR: Error{message: 'Module name "pegjs" has not been loaded yet for context: _. Use require([])
还尝试不在 karma 生成的网页中包含 pegjs:
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: false },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
但它失败了:
PhantomJS 1.9.8 (Linux 0.0.0) ERROR: 'There is no timestamp for /base/bower_components/pegjs/peg-0.9.0!'
尝试将 bower_component 文件夹放入 js 文件夹,但没有成功。
所以我不知道从这里开始......在 Google 或这里找不到任何相关内容。这似乎是 requirejs/pegjs 因果报应的特定问题...欢迎任何想法。
根据丹的回答更新:
所以我在 parser.js:
中从同步需求切换到异步需求
define(['../bower_components/pegjs/peg-0.9.0'], function(PEG) {
'use strict';
var grammarFile = 'grammar.peg'
return {
parse: function(fs, content, debug) {
var grammar = fs.readFileSync(grammarFile, 'utf8').toString();
// Build parser from grammar
var parser = PEG.buildParser(grammar, { trace: debug });
[...]
试图在 karma.conf.js:
中包含 pegjs bower 组件
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: false },
或不包含:
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
但总是得到同样的错误:
Error: Script error for "/blabla/bower_components/pegjs/peg-0.9.0", needed by: /blabla/js/parser.js
http://requirejs.org/docs/errors.html#scripterror
at /blabla/node_modules/requirejs/require.js:140
是的,文件存在:
$ file /home/aa024149/share/logofrjs/bower_components/pegjs/peg-0.9.0.js
/blabla/bower_components/pegjs/peg-0.9.0.js: ASCII text, with very long lines
UPDATE2:终于理解并找到了可以接受的解决方案。
听起来你正在通过 requirejs 加载 pegjs。如果是这种情况,则 pegjs 不应是包含的文件。在您的 karma.conf.js 中,您是否尝试过以下操作:
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: false },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
included 的值表示 karma 服务器生成的网页是否应该有该文件的脚本标签(参见 http://karma-runner.github.io/0.13/config/files.html)。所以你的 karma.config 的:
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
会导致karma生成一个head标签类似于:
的网页
<head>
<script src="/base/bower_components/pegjs/peg-0.9.0.js"></script>
<script src="/base/require.js"></script>
<script src="/base/test/test-main.js"></script>
</head>
根据我的经验,我见过很多与此类似的行为,这些行为都是由于将我的文件标记为 included: true
而导致的。如果您尝试使用 requirejs 加载某个文件,请确保将其标记为 included: false
.
我认为这标志着它是一项预处理工作,但我不完全确定为什么会产生如此大的不同。
据我所知,Karma 是一个测试框架,它将 运行 您在浏览器中的测试。
这不适合测试很多节点模块。
浏览器无法同步执行此操作:var PEG = require('pegjs')
。这就是它要求您使用 require([])
的原因,当 pegjs 完成加载时,您会传递一个要执行的回调。
使用 pegjs 的 bower 版本并确保在调用 require('pegjs')
之前加载它可能会有所帮助。这将确保已经为上下文 _ 加载了 pegjs(我认为是默认的 requirejs 上下文)。
它也无法使用 fs.readFileSync(grammarFile, 'utf8')
从文件系统加载文件,因此您必须以其他方式进行。你可以要求 Karma 将你的 peg 语法放在文件数组中,然后使用 requirejs text plugin.
加载它。
如果您正在测试的模块是针对 运行ning 在 node.js 而不是在浏览器中,那么它可能更适合使用不 运行 的测试框架浏览器中的代码,但 运行 将其放在节点中,因此您拥有所有可用的节点模块。如果你是针对浏览器的,我会重写它以更具体地针对浏览器。
所以在 dan 和 pieceOpiland 的各种回答和评论的帮助下,我终于找到了一种方法来做我想做的事。
首先,与许多 javascript 库一样,pegjs 有两种格式:npm 模块和 bower 模块。
Npm 模块用于为节点制作的脚本,并从命令行调用。 Bower 模块用于在浏览器中加载脚本。
我的第一个误解是 'require' 可以在节点和浏览器中模糊地工作。这是错误的。似乎要求模块使其在浏览器中工作的唯一方法是通过异步调用,如:
require(['module'], function(module) {
...
});
另一个误解是我可以在浏览器中加载 npm 模块。某种魔法会作用于我的页面加载的各种 npm 文件。这可能是可行的,但只能使用一些特殊工具,如 browserify。不做特殊改造,浏览器只能加载bower版本。此外,pegjs bower 模块的制作方式使得全局变量的定义如下:
var PEG = {
...
}
module.exports = PEG;
基本上,bower 模块将一个全局变量(实际上是几个全局变量)插入到顶级范围。
因此,我没有让我的客户端代码(浏览器和节点中的 运行 代码)加载模块,而是实际上在以下任一位置加载模块:
- main.js 通过对 npm 模块的同步要求,如下所示:
var PEG = require('pegjs');
- main-browser.js 通过加载 bower pegjs 脚本时可用的全局变量(通过
<script>
标记)
这两个 'mains' 然后将 PEG 变量注入我的解析器函数。
为了 karma 工作,我只需要在生成的页面中包含 pegjs bower 模块(karma.conf.js 提取):
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js',
],
正在尝试使用 PegJS 和 requirejs 测试项目。 我有几个源文件,实现为通过 require API 加载的 AMD 模块(定义)。 目录结构下:
js/
somefile.js
main.js
parser.js
test/
parser.spec.js
我已经编写了一个 parser.js 模块来加载 PegJS 语法文件并使用 PegJS 创建一个 peg 解析器:
define(function() {
'use strict';
var PEG = require('pegjs');
var grammarFile = 'grammar.peg'
return {
parse: function(fs, content, debug) {
var grammar = fs.readFileSync(grammarFile, 'utf8').toString();
// Build parser from grammar
var parser = PEG.buildParser(grammar, { trace: debug });
[...]
这与在命令行上使用 node.js 执行的 main.js 配合得很好。 现在我想使用 karma、jasmine 和 PhantomJS 测试我的项目。我有一个 karma.conf.js 这样的:
frameworks: ['jasmine', 'requirejs'],
files: [
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js',
],
还有一个名为 test-main.js 的 require bootstrap 文件,它是这样配置的:
'use strict';
var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;
// Get a list of all the test files to include
Object.keys(window.__karma__.files).forEach(function(file) {
console.log(file);
if (TEST_REGEXP.test(file)) {
// Normalize paths to RequireJS module names.
// If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
// then do not normalize the paths
var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
allTestFiles.push(file);
}
});
require.config({
// Karma serves files under /base, which is the basePath from your config file
baseUrl: '/base/js',
// dynamically load all test files
deps: allTestFiles,
// we have to kickoff jasmine, as it is asynchronous
callback: window.__karma__.start
});
现在,当我启动测试 (grunt karma
) 时,我收到了这个错误:
PhantomJS 1.9.8 (Linux 0.0.0) ERROR: Error{message: 'Module name "pegjs" has not been loaded yet for context: _. Use require([])
所以我尝试用这种方式在Karma加载的文件中包含pegjs karma.conf.js:
files: [
{ pattern: 'node_modules/pegjs/lib/**/*.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
当我这样做时,我仍然得到一个错误:
Error: Module name "utils/arrays" has not been loaded yet for context: _. Use require([])
查看pegjs模块内部,确实有一个arrays.js文件:
compiler/
compiler.js
grammar-error.js
parser.js
peg.js
utils/
arrays.js
classes.js
objects.js
所以也尝试包含数组:
files: [
{ pattern: 'node_modules/pegjs/lib/utils/arrays.js', included: true },
{ pattern: 'node_modules/pegjs/lib/**/*.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
我得到:
ReferenceError: Can't find variable: module
at /blabla/node_modules/pegjs/lib/utils/arrays.js:108
因为:
108 module.exports = arrays;
所以,为了加载 npm 模块,我尝试以这种方式加载 bower 模块:
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
又来了:
PhantomJS 1.9.8 (Linux 0.0.0) ERROR: Error{message: 'Module name "pegjs" has not been loaded yet for context: _. Use require([])
还尝试不在 karma 生成的网页中包含 pegjs:
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: false },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
但它失败了:
PhantomJS 1.9.8 (Linux 0.0.0) ERROR: 'There is no timestamp for /base/bower_components/pegjs/peg-0.9.0!'
尝试将 bower_component 文件夹放入 js 文件夹,但没有成功。
所以我不知道从这里开始......在 Google 或这里找不到任何相关内容。这似乎是 requirejs/pegjs 因果报应的特定问题...欢迎任何想法。
根据丹的回答更新:
所以我在 parser.js:
中从同步需求切换到异步需求define(['../bower_components/pegjs/peg-0.9.0'], function(PEG) {
'use strict';
var grammarFile = 'grammar.peg'
return {
parse: function(fs, content, debug) {
var grammar = fs.readFileSync(grammarFile, 'utf8').toString();
// Build parser from grammar
var parser = PEG.buildParser(grammar, { trace: debug });
[...]
试图在 karma.conf.js:
中包含 pegjs bower 组件{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: false },
或不包含:
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
但总是得到同样的错误:
Error: Script error for "/blabla/bower_components/pegjs/peg-0.9.0", needed by: /blabla/js/parser.js
http://requirejs.org/docs/errors.html#scripterror
at /blabla/node_modules/requirejs/require.js:140
是的,文件存在:
$ file /home/aa024149/share/logofrjs/bower_components/pegjs/peg-0.9.0.js
/blabla/bower_components/pegjs/peg-0.9.0.js: ASCII text, with very long lines
UPDATE2:终于理解并找到了可以接受的解决方案。
听起来你正在通过 requirejs 加载 pegjs。如果是这种情况,则 pegjs 不应是包含的文件。在您的 karma.conf.js 中,您是否尝试过以下操作:
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: false },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
included 的值表示 karma 服务器生成的网页是否应该有该文件的脚本标签(参见 http://karma-runner.github.io/0.13/config/files.html)。所以你的 karma.config 的:
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js'
],
会导致karma生成一个head标签类似于:
的网页<head>
<script src="/base/bower_components/pegjs/peg-0.9.0.js"></script>
<script src="/base/require.js"></script>
<script src="/base/test/test-main.js"></script>
</head>
根据我的经验,我见过很多与此类似的行为,这些行为都是由于将我的文件标记为 included: true
而导致的。如果您尝试使用 requirejs 加载某个文件,请确保将其标记为 included: false
.
我认为这标志着它是一项预处理工作,但我不完全确定为什么会产生如此大的不同。
据我所知,Karma 是一个测试框架,它将 运行 您在浏览器中的测试。
这不适合测试很多节点模块。
浏览器无法同步执行此操作:var PEG = require('pegjs')
。这就是它要求您使用 require([])
的原因,当 pegjs 完成加载时,您会传递一个要执行的回调。
使用 pegjs 的 bower 版本并确保在调用 require('pegjs')
之前加载它可能会有所帮助。这将确保已经为上下文 _ 加载了 pegjs(我认为是默认的 requirejs 上下文)。
它也无法使用 fs.readFileSync(grammarFile, 'utf8')
从文件系统加载文件,因此您必须以其他方式进行。你可以要求 Karma 将你的 peg 语法放在文件数组中,然后使用 requirejs text plugin.
如果您正在测试的模块是针对 运行ning 在 node.js 而不是在浏览器中,那么它可能更适合使用不 运行 的测试框架浏览器中的代码,但 运行 将其放在节点中,因此您拥有所有可用的节点模块。如果你是针对浏览器的,我会重写它以更具体地针对浏览器。
所以在 dan 和 pieceOpiland 的各种回答和评论的帮助下,我终于找到了一种方法来做我想做的事。
首先,与许多 javascript 库一样,pegjs 有两种格式:npm 模块和 bower 模块。
Npm 模块用于为节点制作的脚本,并从命令行调用。 Bower 模块用于在浏览器中加载脚本。
我的第一个误解是 'require' 可以在节点和浏览器中模糊地工作。这是错误的。似乎要求模块使其在浏览器中工作的唯一方法是通过异步调用,如:
require(['module'], function(module) {
...
});
另一个误解是我可以在浏览器中加载 npm 模块。某种魔法会作用于我的页面加载的各种 npm 文件。这可能是可行的,但只能使用一些特殊工具,如 browserify。不做特殊改造,浏览器只能加载bower版本。此外,pegjs bower 模块的制作方式使得全局变量的定义如下:
var PEG = {
...
}
module.exports = PEG;
基本上,bower 模块将一个全局变量(实际上是几个全局变量)插入到顶级范围。
因此,我没有让我的客户端代码(浏览器和节点中的 运行 代码)加载模块,而是实际上在以下任一位置加载模块:
- main.js 通过对 npm 模块的同步要求,如下所示:
var PEG = require('pegjs');
- main-browser.js 通过加载 bower pegjs 脚本时可用的全局变量(通过
<script>
标记)
这两个 'mains' 然后将 PEG 变量注入我的解析器函数。
为了 karma 工作,我只需要在生成的页面中包含 pegjs bower 模块(karma.conf.js 提取):
files: [
{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
{ pattern: './test/**/*.spec.js', included: false },
{ pattern: './js/**/*.js', included: false},
'./test/test-main.js',
],