【问题标题】:Configure Karma to load pegjs with requirejs配置 Karma 以使用 requirejs 加载 pegjs
【发布时间】:2016-02-26 23:50:11
【问题描述】:

尝试使用 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 的需要引导文件,它的配置方式如下:

'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([])

还尝试在业力生成的网页中不包含 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 或这里找不到任何相关内容。使用 karma 来 requirejs/pegjs 似乎是一个特定的问题......欢迎任何想法。

按照 dan 的回答更新:

所以我在 parser.js 中从同步 require 切换到异步 require:

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:终于明白并找到了可以接受的解决方案。

【问题讨论】:

    标签: javascript node.js requirejs karma-runner pegjs


    【解决方案1】:

    听起来您正在通过 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

    我相信这标志着它是一项预处理工作,但我不完全确定为什么会产生如此大的差异。

    【讨论】:

    • 是的,pieceOpiland,这是我尝试过的众多组合之一,但仍然失败。我会更新问题。
    • 嗯,您发布的最新错误中缺少通常由 requirejs 添加的 .js 扩展名。你介意也包括你的 requirejs 配置吗?
    • 好的,我已经完成了我使用的 require 配置,但我怀疑你会有更多有用的信息。请注意,我的项目在带有节点的命令行中运行良好。问题在于业力测试。
    【解决方案2】:

    据我所知,Karma 是一个测试框架,它将在浏览器中运行您的测试。

    这不适合测试许多节点模块。

    浏览器无法同步执行此操作:var PEG = require('pegjs')。这就是为什么它要求您使用require([]),您传递一个回调以在 pegjs 完成加载时执行。

    使用 pegjs 的 bower 版本并确保在调用 require('pegjs') 之前加载它可能会有所帮助。这将确保 pegjs 已经为 context _ 加载(​​我认为是默认的 requirejs 上下文)。

    它也无法从带有fs.readFileSync(grammarFile, 'utf8') 的文件系统加载文件,因此您必须以另一种方式进行。你可以让 Karma 托管你的 peg 语法,方法是把它放在 files 数组中,然后使用 requirejs text plugin 加载它。

    如果您正在测试的模块旨在在 node.js 上而不是在浏览器中运行,那么它可能更适合使用不在浏览器中运行代码但在节点中运行的测试框架,这样您就可以拥有所有可用的节点模块。如果您针对的是浏览器,我会重写它以更具体地针对浏览器。

    【讨论】:

    • 也试过了(见更新的问题),但它似乎也无法加载凉亭版本。无论如何,bower 版本似乎与 npm 版本不兼容,因为当我尝试使用命令行运行我的程序时,我收到一条错误消息,提示 pegjs 函数(buildParser)之一不可用。所以两个模块(bower/npm)都不能互换,这是一个问题。我想让我的解析器在浏览器和命令行中使用。我最初认为能够做到这一点是 nodejs 的前提之一。
    • 根据您对语法文件的观点,我将重构我的项目,以便 main.js 加载文件并且解析器获取一大块文本。我尽量远离 requirejs mumbo jumbo...
    • 每次运行脚本时都需要生成解析器吗?它可以生成为将与应用程序一起部署的构建工件吗?在浏览器中运行 bower 版本时,打开开发者控制台是否会告诉您任何信息?浏览器是否成功获取 pegjs 的 bower 版本(检查网络选项卡没有 404)?脚本错误是否在调试器中暂停并给出任何有意义的信息,因为它通常是由错误的脚本引起的?
    • 另外,你可以使用requirejs config的paths属性来设置pegjs的路径。 require.config({baseUrl: '/base/js', paths: {'pegjs': '../bower_components/pegjs/peg-0.9.0'} })。然后你可以使用define(['pegjs'], function(PEG) {})。虽然,我不希望这会直接提供帮助。
    • 啊,如果这是导致问题的原因,您可以使用 requirejs shim 选项告诉 requirejs 它应该期望 pegjs 脚本设置哪个全局变量。 requirejs.org/docs/api.html#config-shim
    【解决方案3】:

    因此,在 dan 和 pieceOpiland 的各种答案和 cmets 的帮助下,我终于找到了一种方法来做我想做的事。

    首先,pegjs 与许多 javascript 库一样有两种格式:npm 模块和 bower 模块。

    Npm 模块用于为节点制作的脚本并从命令行调用。 Bower 模块用于在浏览器中加载脚本。

    我的第一个误解是'require' 会在节点和浏览器中模糊地工作。这是错误的。似乎需要一个模块以便它在浏览器中工作的唯一方法是通过异步调用,例如:

    require(['module'], function(module) {
      ...
    });
    

    另一个误解是我可以在浏览器中加载 npm 模块。对于要与我的页面一起加载的各种 npm 文件,某种魔法会起作用。这可能是可能的,但只能使用诸如 browserify 之类的特殊工具。无需特殊改造,浏览器只能加载bower版本。此外,pegjs bower 模块的制作方式使得全局变量的定义如下:

    var PEG = {
     ...
    }
    
    module.exports = PEG;
    

    基本上,bower 模块将一个全局变量(实际上是几个全局变量)插入到顶级作用域。

    因此,我实际上并没有让我的客户端代码(在浏览器和节点中运行的那个)加载模块,而是将模块加载到任一:

    1. main.js 通过对 npm 模块的同步要求,如下所示:var PEG = require('pegjs');
    2. main-browser.js 通过加载 bower pegjs 脚本时可用的全局变量(通过 &lt;script&gt; 标签)

    然后,这两个“主要”都将 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',
    ],
    

    【讨论】:

    • 忘掉所有的 require/bower 废话,帮自己一个忙,转向 webpack!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-19
    • 1970-01-01
    相关资源
    最近更新 更多