kach / nearley

📜🔜🌲 Simple, fast, powerful parser toolkit for JavaScript.
https://nearley.js.org
MIT License
3.6k stars 232 forks source link

Allow use of `import` #535

Open bburns opened 4 years ago

bburns commented 4 years ago

I'm making a console node app where the package is also used by a React app, and would like to include a nearley parser. I've been using "type":"module" in my packages with import statements, and tried the following -

import nearley from 'nearley'
import * as grammar from './grammar.js'

but get an error -

file:///.../grammar.js:24
   window.grammar = grammar;
   ^

ReferenceError: window is not defined
    at file:///.../grammar.js:24:4

The relevant code is -

if (typeof module !== 'undefined'&& typeof module.exports !== 'undefined') {
   module.exports = grammar;
} else {
   window.grammar = grammar;
}

Is there a way to use import with nearley? If I use require I get

const nearley = require("nearley");
                ^
ReferenceError: require is not defined

I'm fairly new to node and am not sure how to manage the different module systems.

I came across some links that say there's a way to use conditional exports to support both types, but am not sure how involved that would be.

https://alexomara.com/blog/dual-packages-in-node-js-conditional-exports/ -

With Node.js 13.7.0 we finally have a standard way to make dual-packages which support both ESM and CJS with backwards compatibility with older Node.js versions, via conditional exports.

https://nodejs.org/api/esm.html#esm_conditional_exports -

For example, a package that wants to provide different ES module exports for require() and import can be written:

// package.json
{
"main": "./main-require.cjs",
"exports": {
"import": "./main-module.js",
"require": "./main-require.cjs"
},
"type": "module"
}

Do you think this would be possible, or is there another way to work around this issue?

Thank you -

hgiesel commented 3 years ago

You need a packager like rollup or webpack. For example with rollup I use @rollup/plugin-node-resolve, and @rollup/plugin-commonjs, to first resolve nearley (which is a node module), and then turn it into an ES module.

GormanFletcher commented 3 years ago

I got my Nearley grammar working in my ESM Node.js / TypeScript project by having rollup convert the UMD package to ESM:

$ npm install -D rollup @rollup/plugin-commonjs
// rollup-nearley.js
import commonjs from "@rollup/plugin-commonjs";

export default {
  input: "lib/my-grammar.umd.js",
  output: {
    file: "lib/my-grammar.js",
    format: "es",
  },
  plugins: [commonjs()],
};
// package.json
{
  "scripts": {
    "nearley": "nearleyc lib/my-grammar.ne -o lib/my-grammar.umd.js && rollup -c rollup-nearley.js"
  }
}
// lib/foo.ts
import grammar from "./beep-grammar.js";
// lib/my-grammar.d.ts
import type { CompilerRules } from "nearley";

const rules = CompilerRules;
export = rules;

I have to copy the grammar file out of the project (e.g., to /tmp/) to use nearley-test, since nearley-test tries to use require(...) and Node.js rejects that inside the ESM project.

johndeighan commented 4 days ago

In many cases, having to use a bundler isn't a viable solution. You might try using "dynamic import". It should work with both ES6 and CommonJS modules. You must remember that dynamic import is async. With the latest NodeJS, you can use async in top level code. In my code, e.g., I use this to import a grammar:

    import {pathToFileURL} from 'node:url';

    let grammar = await import(pathToFileURL(jsPath));

However, there is still a problem which requires me to patch the *.js file that nearley produces. See issue #594. Easy fix, but it doesn't appear that anyone is responsible for working through the issues. Because of that, I think that people (such as myself) don't want to spend the time to submit patches which will likely be ignored.