Эх сурвалжийг харах

[autocomplete] make sql autocomplete and syntax parsers pluggable (#2861)

* [autocomplete] fixed failure when generating new parser

* [autocomplete] separate module imports from sqlParserRepository and generate new file on make apps

A new file called parserModules.ts which contains the import statements for all available parsers is now generated when running make apps. The generation of the import statements has been removed from generateParsers.js and is now handled by the generateParserModuleImports.js.

A key difference is that the import statements are generated based on the actual available parser files rather than the available Jison parser definition files. This means that a new parser can be created and maintained in a separate project and by adding its generated parser files to the “parser folder” in downstream Hue (desktop/core/src/desktop/js/parse/sql) it will be automatically picked up by the sqlParserRepository.ts

* [ci] add call to generateParserModuleImports

Co-authored-by: Björn Alm <balm@cloudera.com>
Bjorn Alm 3 жил өмнө
parent
commit
25b30d1078

+ 4 - 0
.circleci/config.yml

@@ -101,6 +101,10 @@ commands:
             cp ~/repo/package.json .
             rm -f package-lock.json
             cp ~/repo/package-lock.json .
+
+            cd /usr/share/hue/tools/jison && npm install && node generateParserModuleImports.js
+            cd /usr/share/hue
+            
             npm install
             npm i eslint-plugin-jest@latest --save-dev # Seems to not be found otherwise
             npm run webpack

+ 1 - 0
.eslintignore

@@ -41,4 +41,5 @@
 /desktop/core/src/desktop/js/parse/sql/dasksql/dasksqlAutocompleteParser.js
 /desktop/core/src/desktop/js/parse/sql/dasksql/dasksqlSyntaxParser.js
 /desktop/core/src/desktop/js/parse/sql/dasksql/spec/dasksqlAutocompleteParser_Locations_Spec.js
+/desktop/core/src/desktop/js/parse/sql/parserModules.ts
 **/node_modules/**

+ 2 - 0
.github/workflows/commitflow.yml

@@ -69,6 +69,8 @@ jobs:
 
         cp -r docs /usr/share/hue
 
+        cd /usr/share/hue/tools/jison && npm install && node generateParserModuleImports.js
+
         cd /usr/share/hue
 
         npm install

+ 3 - 0
.gitignore

@@ -115,6 +115,9 @@ junitresults*
 # Generated js bundles
 desktop/core/src/desktop/static/desktop/js/bundles/
 
+# Generated ts files
+desktop/core/src/desktop/js/parse/sql/parserModules.ts
+
 # Hugo generated files
 docs/docs-site/public/
 

+ 1 - 0
Makefile

@@ -245,6 +245,7 @@ install-env:
 npm-install:
 	npm --version
 	node --version
+	pushd tools/jison && npm install && node generateParserModuleImports.js && popd	
 	npm install
 	npm run webpack
 	npm run webpack-login

+ 1 - 32
desktop/core/src/desktop/js/parse/sql/sqlParserRepository.ts

@@ -14,39 +14,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-/* eslint-disable */
 import { AutocompleteParser, SqlParserProvider, SyntaxParser } from 'parse/types';
-
-/**
- * AUTOCOMPLETE_MODULES and SYNTAX_MODULES are generated, do not edit manually, see tools/jison/generateParsers.js
- */
-const AUTOCOMPLETE_MODULES = {
-  calcite: () => import(/* webpackChunkName: "calcite-parser" */ './calcite/calciteAutocompleteParser'),
-  dasksql: () => import(/* webpackChunkName: "dasksql-parser" */ './dasksql/dasksqlAutocompleteParser'),
-  druid: () => import(/* webpackChunkName: "druid-parser" */ './druid/druidAutocompleteParser'),
-  elasticsearch: () => import(/* webpackChunkName: "elasticsearch-parser" */ './elasticsearch/elasticsearchAutocompleteParser'),
-  flink: () => import(/* webpackChunkName: "flink-parser" */ './flink/flinkAutocompleteParser'),
-  generic: () => import(/* webpackChunkName: "generic-parser" */ './generic/genericAutocompleteParser'),
-  hive: () => import(/* webpackChunkName: "hive-parser" */ './hive/hiveAutocompleteParser'),
-  impala: () => import(/* webpackChunkName: "impala-parser" */ './impala/impalaAutocompleteParser'),
-  ksql: () => import(/* webpackChunkName: "ksql-parser" */ './ksql/ksqlAutocompleteParser'),
-  phoenix: () => import(/* webpackChunkName: "phoenix-parser" */ './phoenix/phoenixAutocompleteParser'),
-  presto: () => import(/* webpackChunkName: "presto-parser" */ './presto/prestoAutocompleteParser')
-};
-const SYNTAX_MODULES = {
-  calcite: () => import(/* webpackChunkName: "calcite-parser" */ './calcite/calciteSyntaxParser'),
-  dasksql: () => import(/* webpackChunkName: "dasksql-parser" */ './dasksql/dasksqlSyntaxParser'),
-  druid: () => import(/* webpackChunkName: "druid-parser" */ './druid/druidSyntaxParser'),
-  elasticsearch: () => import(/* webpackChunkName: "elasticsearch-parser" */ './elasticsearch/elasticsearchSyntaxParser'),
-  flink: () => import(/* webpackChunkName: "flink-parser" */ './flink/flinkSyntaxParser'),
-  generic: () => import(/* webpackChunkName: "generic-parser" */ './generic/genericSyntaxParser'),
-  hive: () => import(/* webpackChunkName: "hive-parser" */ './hive/hiveSyntaxParser'),
-  impala: () => import(/* webpackChunkName: "impala-parser" */ './impala/impalaSyntaxParser'),
-  ksql: () => import(/* webpackChunkName: "ksql-parser" */ './ksql/ksqlSyntaxParser'),
-  phoenix: () => import(/* webpackChunkName: "phoenix-parser" */ './phoenix/phoenixSyntaxParser'),
-  presto: () => import(/* webpackChunkName: "presto-parser" */ './presto/prestoSyntaxParser')
-};
-/* eslint-enable */
+import { AUTOCOMPLETE_MODULES, SYNTAX_MODULES } from './parserModules';
 
 export class SqlParserRepository implements SqlParserProvider {
   modulePromises: { [dialect: string]: Promise<AutocompleteParser | SyntaxParser> } = {};

+ 103 - 0
tools/jison/generateParserModuleImports.js

@@ -0,0 +1,103 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/* eslint-disable no-console */
+
+const fs = require('fs');
+const fsExtra = require('fs-extra');
+const pathModule = require('path');
+const { PARSER_FOLDER, LICENSE } = require('./generateParsers');
+
+const fsPromises = fs.promises;
+
+const SQL_PARSER_MODULES_PATH = `${PARSER_FOLDER}parserModules.ts`;
+const SYNTAX_IMPORT_TEMPLATE =
+  '  KEY: () => import(/* webpackChunkName: "KEY-parser" */ \'./KEY/KEYSyntaxParser\')';
+const AUTOCOMPLETE_IMPORT_TEMPLATE =
+  '  KEY: () => import(/* webpackChunkName: "KEY-parser" */ \'./KEY/KEYAutocompleteParser\')';
+const GENERATED_NOTICE = `// PLEASE NOTE!
+// Do not modify, the contents of this file is generated. 
+// For more info see tools/jison/generateParserModuleImports.js
+`;
+
+const isFolder = async fileSystemEntry => {
+  const entryPath = pathModule.join(PARSER_FOLDER, fileSystemEntry);
+  return fsPromises.stat(entryPath).then(stats => stats.isDirectory());
+};
+
+const asyncFilter = async (arr, predicate) => {
+  const orderedBooleans = await Promise.all(arr.map(predicate));
+  return arr.filter((item, index) => orderedBooleans[index]);
+};
+
+const getParserNamesFromFolders = async () => {
+  try {
+    const fileSystemEntries = await fsPromises.readdir(PARSER_FOLDER);
+    return await asyncFilter(fileSystemEntries, isFolder);
+  } catch (err) {
+    console.error(`Failed generating parser names from folders in ${PARSER_FOLDER}`);
+    throw err;
+  }
+};
+
+const getImports = async (parserNames, template, parserType) => {
+  const parserExists = parserName =>
+    fsExtra.pathExists(`${PARSER_FOLDER}/${parserName}/${parserName}${parserType}Parser.js`);
+
+  const filtered = await asyncFilter(parserNames, parserExists);
+
+  return filtered
+    .map(parserName => template.replace(/KEY/g, parserName))
+    .sort()
+    .join(',\n');
+};
+
+const writeFile = async fileContent => {
+  try {
+    await fsExtra.ensureFile(SQL_PARSER_MODULES_PATH);
+    await fsPromises.writeFile(SQL_PARSER_MODULES_PATH, fileContent);
+  } catch (err) {
+    console.error(`Failed writing data to file file: ${SQL_PARSER_MODULES_PATH}`, err);
+    throw err;
+  }
+};
+
+const generateParserModulesFile = async () => {
+  try {
+    // eslint-disable-next-line
+    console.log('Generating file parserModules.ts...');
+
+    const parserNames = await getParserNamesFromFolders();
+    const syntaxImports = await getImports(parserNames, SYNTAX_IMPORT_TEMPLATE, 'Syntax');
+    const autocompleteImports = await getImports(
+      parserNames,
+      AUTOCOMPLETE_IMPORT_TEMPLATE,
+      'Autocomplete'
+    );
+
+    const exportSyntaxLines = `export const SYNTAX_MODULES = {\n${syntaxImports}\n};\n`;
+    const exportAutoCompleteLines = `export const AUTOCOMPLETE_MODULES = {\n${autocompleteImports}\n};\n`;
+    const fileContent = `${LICENSE}\n${GENERATED_NOTICE}\n${exportSyntaxLines}${exportAutoCompleteLines}`;
+    await writeFile(fileContent);
+
+    // eslint-disable-next-line
+    console.log('Done writing parserModules.ts!\n');
+  } catch (err) {
+    console.error(`Failed generating parser modules import file: ${SQL_PARSER_MODULES_PATH}`, err);
+  }
+};
+
+generateParserModulesFile();

+ 25 - 53
tools/jison/generateParsers.js

@@ -16,8 +16,9 @@
 
 /* eslint-disable no-restricted-syntax */
 
-const fs = require('fs');
 const cli = require('jison/lib/cli');
+const fs = require('fs');
+const fsExtra = require('fs-extra');
 
 const LICENSE =
   '// Licensed to Cloudera, Inc. under one\n' +
@@ -43,15 +44,9 @@ const SQL_STATEMENTS_PARSER_JSDOC =
   ' * @return {SqlStatementsParserResult}\n' +
   ' */\n';
 
-const PARSER_FOLDER = '../../desktop/core/src/desktop/js/parse/sql/';
 const OUTPUT_FOLDER = '../../desktop/core/src/desktop/js/parse/';
-const JISON_FOLDER = '../../desktop/core/src/desktop/js/parse/jison/';
-const SQL_PARSER_REPOSITORY_PATH =
-  '../../desktop/core/src/desktop/js/parse/sql/sqlParserRepository.ts';
-const SYNTAX_PARSER_IMPORT_TEMPLATE =
-  '  KEY: () => import(/* webpackChunkName: "KEY-parser" */ \'./KEY/KEYSyntaxParser\')';
-const AUTOCOMPLETE_PARSER_IMPORT_TEMPLATE =
-  '  KEY: () => import(/* webpackChunkName: "KEY-parser" */ \'./KEY/KEYAutocompleteParser\')';
+const PARSER_FOLDER = `${OUTPUT_FOLDER}sql/`;
+const JISON_FOLDER = `${OUTPUT_FOLDER}jison/`;
 
 const parserDefinitions = {
   globalSearchParser: {
@@ -148,11 +143,14 @@ const readFile = path =>
 
 const writeFile = (path, contents) =>
   new Promise((resolve, reject) => {
-    fs.writeFile(path, contents, err => {
-      if (err) {
-        reject();
-      }
-      resolve();
+    fsExtra.ensureFile(path).then(() => {
+      fs.writeFile(path, contents, err => {
+        if (err) {
+          console.log(err);
+          reject();
+        }
+        resolve();
+      });
     });
   });
 
@@ -229,7 +227,10 @@ const generateParser = parserName =>
                     deleteFile(generatedJsFileName);
                     resolve();
                   })
-                  .catch(reject);
+                  .catch(error => {
+                    console.info(error);
+                    reject();
+                  });
               })
               .catch(reject);
           })
@@ -301,11 +302,13 @@ const addParsersFromStructure = (structure, dialect) => {
   );
 };
 
+const fileIsVisible = fileName => !fileName.startsWith('.');
+
 const identifySqlParsers = () =>
   new Promise(resolve => {
     listDir(JISON_FOLDER + 'sql').then(files => {
       const promises = [];
-      files.forEach(folder => {
+      files.filter(fileIsVisible).forEach(folder => {
         promises.push(
           listDir(JISON_FOLDER + 'sql/' + folder).then(async jisonFiles => {
             if (jisonFiles.find(fileName => fileName === 'structure.json')) {
@@ -376,12 +379,10 @@ const prepareForNewParser = () =>
                     listDir(JISON_FOLDER + 'sql/' + source).then(files => {
                       const copyPromises = [];
                       files.forEach(file => {
-                        copyPromises.push(
-                          copyFile(
-                            JISON_FOLDER + 'sql/' + source + '/' + file,
-                            JISON_FOLDER + 'sql/' + target + '/' + file
-                          )
-                        );
+                        // "file" can also be a subfolder with files in it
+                        const fromPath = JISON_FOLDER + 'sql/' + source + '/' + file;
+                        const toPath = JISON_FOLDER + 'sql/' + target + '/' + file;
+                        copyPromises.push(fsExtra.copy(fromPath, toPath));
                       });
                       Promise.all(copyPromises).then(() => {
                         const autocompleteSources = [
@@ -484,37 +485,6 @@ identifySqlParsers().then(() => {
             console.log(error);
             console.log('FAIL!');
           });
-      } else {
-        const autocompParsers = [];
-        const syntaxParsers = [];
-        console.log('Updating sqlParserRepository.ts...');
-        Object.keys(parserDefinitions).forEach(key => {
-          if (parserDefinitions[key].sqlParser === 'AUTOCOMPLETE') {
-            autocompParsers.push(
-              AUTOCOMPLETE_PARSER_IMPORT_TEMPLATE.replace(
-                /KEY/g,
-                key.replace('AutocompleteParser', '')
-              )
-            );
-          } else if (parserDefinitions[key].sqlParser === 'SYNTAX') {
-            syntaxParsers.push(
-              SYNTAX_PARSER_IMPORT_TEMPLATE.replace(/KEY/g, key.replace('SyntaxParser', ''))
-            );
-          }
-        });
-        readFile(SQL_PARSER_REPOSITORY_PATH).then(contents => {
-          contents = contents.replace(
-            /const SYNTAX_MODULES = [^}]+}/,
-            'const SYNTAX_MODULES = {\n' + syntaxParsers.sort().join(',\n') + '\n}'
-          );
-          contents = contents.replace(
-            /const AUTOCOMPLETE_MODULES = [^}]+}/,
-            'const AUTOCOMPLETE_MODULES = {\n' + autocompParsers.sort().join(',\n') + '\n}'
-          );
-          writeFile(SQL_PARSER_REPOSITORY_PATH, contents).then(() => {
-            console.log('Done!\n');
-          });
-        });
       }
     };
     generateRecursive();
@@ -522,3 +492,5 @@ identifySqlParsers().then(() => {
 });
 
 /* eslint-enable no-restricted-syntax */
+
+module.exports = { LICENSE, PARSER_FOLDER };

+ 392 - 0
tools/jison/package-lock.json

@@ -0,0 +1,392 @@
+{
+  "name": "parser-generator",
+  "version": "1.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "parser-generator",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "fs-extra": "^10.1.0",
+        "jison": "https://github.com/JohanAhlen/jison/tarball/ad8e41475e"
+      }
+    },
+    "node_modules/amdefine": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+      "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==",
+      "optional": true,
+      "engines": {
+        "node": ">=0.4.2"
+      }
+    },
+    "node_modules/cjson": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/cjson/-/cjson-0.3.0.tgz",
+      "integrity": "sha512-bBRQcCIHzI1IVH59fR0bwGrFmi3Btb/JNwM/n401i1DnYgWndpsUBiQRAddLflkZage20A2d25OAWZZk0vBRlA==",
+      "dependencies": {
+        "jsonlint": "1.6.0"
+      },
+      "engines": {
+        "node": ">= 0.3.0"
+      }
+    },
+    "node_modules/colors": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
+      "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=",
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "node_modules/ebnf-parser": {
+      "version": "0.1.10",
+      "resolved": "https://registry.npmjs.org/ebnf-parser/-/ebnf-parser-0.1.10.tgz",
+      "integrity": "sha1-zR9rpHfFY4xAyX7ZtXLbW6tdgzE="
+    },
+    "node_modules/escodegen": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz",
+      "integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=",
+      "dependencies": {
+        "esprima": "~1.1.1",
+        "estraverse": "~1.5.0",
+        "esutils": "~1.0.0"
+      },
+      "bin": {
+        "escodegen": "bin/escodegen.js",
+        "esgenerate": "bin/esgenerate.js"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      },
+      "optionalDependencies": {
+        "source-map": "~0.1.33"
+      }
+    },
+    "node_modules/esprima": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz",
+      "integrity": "sha1-W28VR/TRAuZw4UDFCb5ncdautUk=",
+      "bin": {
+        "esparse": "bin/esparse.js",
+        "esvalidate": "bin/esvalidate.js"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz",
+      "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz",
+      "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.10",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+      "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+    },
+    "node_modules/jison": {
+      "version": "0.4.17",
+      "resolved": "https://github.com/JohanAhlen/jison/tarball/ad8e41475e",
+      "integrity": "sha512-kKqzzhj1GaVw5vTDQWV3pvKjJS9v5fheDNyN8h3CLj3mHZhbcw2Kb8iT+wPlpCJhbZ3HIxoVJFbmwg7z+ZvGrg==",
+      "license": "MIT",
+      "dependencies": {
+        "cjson": "0.3.0",
+        "ebnf-parser": "0.1.10",
+        "escodegen": "1.3.x",
+        "esprima": "1.1.x",
+        "jison-lex": "0.3.x",
+        "JSONSelect": "0.4.0",
+        "lex-parser": "~0.1.3",
+        "nomnom": "1.5.2"
+      },
+      "bin": {
+        "jison": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/jison-lex": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/jison-lex/-/jison-lex-0.3.4.tgz",
+      "integrity": "sha1-gcoo2E+ESZ36jFlNzePYo/Jux6U=",
+      "dependencies": {
+        "lex-parser": "0.1.x",
+        "nomnom": "1.5.2"
+      },
+      "bin": {
+        "jison-lex": "cli.js"
+      },
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/jsonlint": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.0.tgz",
+      "integrity": "sha1-iKpGvCiaesk7tGyuLVihh6m7SUo=",
+      "dependencies": {
+        "JSV": ">= 4.0.x",
+        "nomnom": ">= 1.5.x"
+      },
+      "bin": {
+        "jsonlint": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/JSONSelect": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz",
+      "integrity": "sha512-VRLR3Su35MH+XV2lrvh9O7qWoug/TUyj9tLDjn9rtpUCNnILLrHjgd/tB0KrhugCxUpj3UqoLqfYb3fLJdIQQQ==",
+      "engines": {
+        "node": ">=0.4.7"
+      }
+    },
+    "node_modules/JSV": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+      "integrity": "sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/lex-parser": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz",
+      "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA="
+    },
+    "node_modules/nomnom": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz",
+      "integrity": "sha1-9DRUSKhTz71cDSYyDyR3qwUm/i8=",
+      "deprecated": "Package no longer supported. Contact support@npmjs.com for more info.",
+      "dependencies": {
+        "colors": "0.5.x",
+        "underscore": "1.1.x"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.1.43",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+      "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+      "optional": true,
+      "dependencies": {
+        "amdefine": ">=0.0.4"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/underscore": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz",
+      "integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA=",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/universalify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+      "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    }
+  },
+  "dependencies": {
+    "amdefine": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+      "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==",
+      "optional": true
+    },
+    "cjson": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/cjson/-/cjson-0.3.0.tgz",
+      "integrity": "sha512-bBRQcCIHzI1IVH59fR0bwGrFmi3Btb/JNwM/n401i1DnYgWndpsUBiQRAddLflkZage20A2d25OAWZZk0vBRlA==",
+      "requires": {
+        "jsonlint": "1.6.0"
+      }
+    },
+    "colors": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz",
+      "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q="
+    },
+    "ebnf-parser": {
+      "version": "0.1.10",
+      "resolved": "https://registry.npmjs.org/ebnf-parser/-/ebnf-parser-0.1.10.tgz",
+      "integrity": "sha1-zR9rpHfFY4xAyX7ZtXLbW6tdgzE="
+    },
+    "escodegen": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz",
+      "integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=",
+      "requires": {
+        "esprima": "~1.1.1",
+        "estraverse": "~1.5.0",
+        "esutils": "~1.0.0",
+        "source-map": "~0.1.33"
+      }
+    },
+    "esprima": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz",
+      "integrity": "sha1-W28VR/TRAuZw4UDFCb5ncdautUk="
+    },
+    "estraverse": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz",
+      "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E="
+    },
+    "esutils": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz",
+      "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA="
+    },
+    "fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "requires": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.10",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+      "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+    },
+    "jison": {
+      "version": "https://github.com/JohanAhlen/jison/tarball/ad8e41475e",
+      "integrity": "sha512-kKqzzhj1GaVw5vTDQWV3pvKjJS9v5fheDNyN8h3CLj3mHZhbcw2Kb8iT+wPlpCJhbZ3HIxoVJFbmwg7z+ZvGrg==",
+      "requires": {
+        "cjson": "0.3.0",
+        "ebnf-parser": "0.1.10",
+        "escodegen": "1.3.x",
+        "esprima": "1.1.x",
+        "jison-lex": "0.3.x",
+        "JSONSelect": "0.4.0",
+        "lex-parser": "~0.1.3",
+        "nomnom": "1.5.2"
+      }
+    },
+    "jison-lex": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/jison-lex/-/jison-lex-0.3.4.tgz",
+      "integrity": "sha1-gcoo2E+ESZ36jFlNzePYo/Jux6U=",
+      "requires": {
+        "lex-parser": "0.1.x",
+        "nomnom": "1.5.2"
+      }
+    },
+    "jsonfile": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+      "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+      "requires": {
+        "graceful-fs": "^4.1.6",
+        "universalify": "^2.0.0"
+      }
+    },
+    "jsonlint": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.0.tgz",
+      "integrity": "sha1-iKpGvCiaesk7tGyuLVihh6m7SUo=",
+      "requires": {
+        "JSV": ">= 4.0.x",
+        "nomnom": ">= 1.5.x"
+      }
+    },
+    "JSONSelect": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.4.0.tgz",
+      "integrity": "sha512-VRLR3Su35MH+XV2lrvh9O7qWoug/TUyj9tLDjn9rtpUCNnILLrHjgd/tB0KrhugCxUpj3UqoLqfYb3fLJdIQQQ=="
+    },
+    "JSV": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+      "integrity": "sha512-ZJ6wx9xaKJ3yFUhq5/sk82PJMuUyLk277I8mQeyDgCTjGdjWJIvPfaU5LIXaMuaN2UO1X3kZH4+lgphublZUHw=="
+    },
+    "lex-parser": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz",
+      "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA="
+    },
+    "nomnom": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.5.2.tgz",
+      "integrity": "sha1-9DRUSKhTz71cDSYyDyR3qwUm/i8=",
+      "requires": {
+        "colors": "0.5.x",
+        "underscore": "1.1.x"
+      }
+    },
+    "source-map": {
+      "version": "0.1.43",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+      "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+      "optional": true,
+      "requires": {
+        "amdefine": ">=0.0.4"
+      }
+    },
+    "underscore": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.1.7.tgz",
+      "integrity": "sha1-QLq4S60Z0jAJbo1u9ii/8FXYPbA="
+    },
+    "universalify": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+      "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
+    }
+  }
+}

+ 1 - 0
tools/jison/package.json

@@ -9,6 +9,7 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
+    "fs-extra": "^10.1.0",
     "jison": "https://github.com/JohanAhlen/jison/tarball/ad8e41475e"
   }
 }