Browse Source

Merge branch 'd98762625-dynamic-import'

n1474335 6 years ago
parent
commit
ce61bcc078
10 changed files with 225 additions and 124 deletions
  1. 3 1
      .eslintrc.json
  2. 6 4
      Gruntfile.js
  3. 1 0
      babel.config.js
  4. 118 74
      package-lock.json
  5. 4 2
      package.json
  6. 2 2
      src/core/Chef.mjs
  7. 2 2
      src/core/ChefWorker.js
  8. 62 14
      src/core/Recipe.mjs
  9. 13 8
      src/web/RecipeWaiter.mjs
  10. 14 17
      webpack.config.js

+ 3 - 1
.eslintrc.json

@@ -1,10 +1,12 @@
 {
+    "parser": "babel-eslint",
     "parserOptions": {
         "ecmaVersion": 9,
         "ecmaFeatures": {
             "impliedStrict": true
         },
-        "sourceType": "module"
+        "sourceType": "module",
+        "allowImportExportEverywhere": true
     },
     "env": {
         "browser": true,

+ 6 - 4
Gruntfile.js

@@ -194,7 +194,8 @@ module.exports = function (grunt) {
                         sitemap: "./src/web/static/sitemap.js"
                     }, moduleEntryPoints),
                     output: {
-                        path: __dirname + "/build/prod"
+                        path: __dirname + "/build/prod",
+                        globalObject: "this"
                     },
                     resolve: {
                         alias: {
@@ -299,6 +300,9 @@ module.exports = function (grunt) {
                             "./config/modules/OpModules": "./config/modules/Default"
                         }
                     },
+                    output: {
+                        globalObject: "this",
+                    },
                     plugins: [
                         new webpack.DefinePlugin(BUILD_CONSTANTS),
                         new HtmlWebpackPlugin({
@@ -391,9 +395,7 @@ module.exports = function (grunt) {
             generateConfig: {
                 command: [
                     "echo '\n--- Regenerating config files. ---'",
-                    "mkdir -p src/core/config/modules",
-                    "echo 'export default {};\n' > src/core/config/modules/OpModules.mjs",
-                    "echo '[]\n' > src/core/config/OperationConfig.json",
+                    "echo [] > src/core/config/OperationConfig.json",
                     "node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateOpsIndex.mjs",
                     "node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateConfig.mjs",
                     "echo '--- Config scripts finished. ---\n'"

+ 1 - 0
babel.config.js

@@ -15,6 +15,7 @@ module.exports = function(api) {
             }]
         ],
         "plugins": [
+            "babel-plugin-syntax-dynamic-import",
             ["babel-plugin-transform-builtin-extend", {
                 "globals": ["Error"]
             }]

+ 118 - 74
package-lock.json

@@ -1512,9 +1512,9 @@
       "dev": true
     },
     "ansi-escapes": {
-      "version": "3.1.0",
-      "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
-      "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+      "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
       "dev": true
     },
     "ansi-html": {
@@ -1882,6 +1882,32 @@
         "js-tokens": "^3.0.2"
       }
     },
+    "babel-eslint": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz",
+      "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "@babel/parser": "^7.0.0",
+        "@babel/traverse": "^7.0.0",
+        "@babel/types": "^7.0.0",
+        "eslint-scope": "3.7.1",
+        "eslint-visitor-keys": "^1.0.0"
+      },
+      "dependencies": {
+        "eslint-scope": {
+          "version": "3.7.1",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
+          "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
+          }
+        }
+      }
+    },
     "babel-loader": {
       "version": "8.0.4",
       "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.4.tgz",
@@ -1902,6 +1928,12 @@
         "babel-runtime": "^6.22.0"
       }
     },
+    "babel-plugin-syntax-dynamic-import": {
+      "version": "6.18.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
+      "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=",
+      "dev": true
+    },
     "babel-plugin-transform-builtin-extend": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/babel-plugin-transform-builtin-extend/-/babel-plugin-transform-builtin-extend-1.1.2.tgz",
@@ -2534,19 +2566,10 @@
         "unset-value": "^1.0.0"
       }
     },
-    "caller-path": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
-      "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
-      "dev": true,
-      "requires": {
-        "callsites": "^0.2.0"
-      }
-    },
     "callsites": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
-      "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+      "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
       "dev": true
     },
     "camel-case": {
@@ -4078,9 +4101,9 @@
       }
     },
     "eslint": {
-      "version": "5.11.1",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.11.1.tgz",
-      "integrity": "sha512-gOKhM8JwlFOc2acbOrkYR05NW8M6DCMSvfcJiBB5NDxRE1gv8kbvxKaC9u69e6ZGEMWXcswA/7eKR229cEIpvg==",
+      "version": "5.12.1",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.12.1.tgz",
+      "integrity": "sha512-54NV+JkTpTu0d8+UYSA8mMKAG4XAsaOrozA9rCW7tgneg1mevcL7wIotPC+fZ0SkWwdhNqoXoxnQCTBp7UvTsg==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
@@ -4100,6 +4123,7 @@
         "glob": "^7.1.2",
         "globals": "^11.7.0",
         "ignore": "^4.0.6",
+        "import-fresh": "^3.0.0",
         "imurmurhash": "^0.1.4",
         "inquirer": "^6.1.0",
         "js-yaml": "^3.12.0",
@@ -4114,7 +4138,6 @@
         "pluralize": "^7.0.0",
         "progress": "^2.0.0",
         "regexpp": "^2.0.1",
-        "require-uncached": "^1.0.3",
         "semver": "^5.5.1",
         "strip-ansi": "^4.0.0",
         "strip-json-comments": "^2.0.1",
@@ -4138,9 +4161,9 @@
           }
         },
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
           "dev": true,
           "requires": {
             "ansi-styles": "^3.2.1",
@@ -4158,9 +4181,9 @@
           }
         },
         "globals": {
-          "version": "11.9.0",
-          "resolved": "https://registry.npmjs.org/globals/-/globals-11.9.0.tgz",
-          "integrity": "sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg==",
+          "version": "11.10.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz",
+          "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==",
           "dev": true
         },
         "ms": {
@@ -4632,18 +4655,6 @@
         }
       }
     },
-    "extract-text-webpack-plugin": {
-      "version": "4.0.0-beta.0",
-      "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-4.0.0-beta.0.tgz",
-      "integrity": "sha512-Hypkn9jUTnFr0DpekNam53X47tXn3ucY08BQumv7kdGgeVUBLq3DJHJTi6HNxv4jl9W+Skxjz9+RnK0sJyqqjA==",
-      "dev": true,
-      "requires": {
-        "async": "^2.4.1",
-        "loader-utils": "^1.1.0",
-        "schema-utils": "^0.4.5",
-        "webpack-sources": "^1.1.0"
-      }
-    },
     "extract-zip": {
       "version": "1.6.7",
       "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
@@ -6730,6 +6741,16 @@
         "import-from": "^2.1.0"
       }
     },
+    "import-fresh": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz",
+      "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
     "import-from": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
@@ -6881,21 +6902,21 @@
       }
     },
     "inquirer": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz",
-      "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==",
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz",
+      "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==",
       "dev": true,
       "requires": {
-        "ansi-escapes": "^3.0.0",
-        "chalk": "^2.0.0",
+        "ansi-escapes": "^3.2.0",
+        "chalk": "^2.4.2",
         "cli-cursor": "^2.1.0",
         "cli-width": "^2.0.0",
-        "external-editor": "^3.0.0",
+        "external-editor": "^3.0.3",
         "figures": "^2.0.0",
-        "lodash": "^4.17.10",
+        "lodash": "^4.17.11",
         "mute-stream": "0.0.7",
         "run-async": "^2.2.0",
-        "rxjs": "^6.1.0",
+        "rxjs": "^6.4.0",
         "string-width": "^2.1.0",
         "strip-ansi": "^5.0.0",
         "through": "^2.3.6"
@@ -6917,9 +6938,9 @@
           }
         },
         "chalk": {
-          "version": "2.4.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-          "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
           "dev": true,
           "requires": {
             "ansi-styles": "^3.2.1",
@@ -8359,6 +8380,30 @@
         "dom-walk": "^0.1.0"
       }
     },
+    "mini-css-extract-plugin": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz",
+      "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.1.0",
+        "schema-utils": "^1.0.0",
+        "webpack-sources": "^1.1.0"
+      },
+      "dependencies": {
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        }
+      }
+    },
     "minimalistic-assert": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -9470,6 +9515,15 @@
         "no-case": "^2.2.0"
       }
     },
+    "parent-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz",
+      "integrity": "sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
     "parse-asn1": {
       "version": "5.1.1",
       "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz",
@@ -10782,16 +10836,6 @@
       "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
       "dev": true
     },
-    "require-uncached": {
-      "version": "1.0.3",
-      "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
-      "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
-      "dev": true,
-      "requires": {
-        "caller-path": "^0.1.0",
-        "resolve-from": "^1.0.0"
-      }
-    },
     "requires-port": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -10842,9 +10886,9 @@
       }
     },
     "resolve-from": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
-      "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
       "dev": true
     },
     "resolve-pkg": {
@@ -10930,9 +10974,9 @@
       }
     },
     "rxjs": {
-      "version": "6.3.3",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
-      "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
+      "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
       "dev": true,
       "requires": {
         "tslib": "^1.9.0"
@@ -11337,9 +11381,9 @@
       }
     },
     "slice-ansi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.0.0.tgz",
-      "integrity": "sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+      "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
       "dev": true,
       "requires": {
         "ansi-styles": "^3.2.0",
@@ -12107,21 +12151,21 @@
       "dev": true
     },
     "table": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/table/-/table-5.1.1.tgz",
-      "integrity": "sha512-NUjapYb/qd4PeFW03HnAuOJ7OMcBkJlqeClWxeNlQ0lXGSb52oZXGzkO0/I0ARegQ2eUT1g2VDJH0eUxDRcHmw==",
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/table/-/table-5.2.2.tgz",
+      "integrity": "sha512-f8mJmuu9beQEDkKHLzOv4VxVYlU68NpdzjbGPl69i4Hx0sTopJuNxuzJd17iV2h24dAfa93u794OnDA5jqXvfQ==",
       "dev": true,
       "requires": {
         "ajv": "^6.6.1",
         "lodash": "^4.17.11",
-        "slice-ansi": "2.0.0",
+        "slice-ansi": "^2.0.0",
         "string-width": "^2.1.1"
       },
       "dependencies": {
         "ajv": {
-          "version": "6.6.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz",
-          "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==",
+          "version": "6.7.0",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz",
+          "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==",
           "dev": true,
           "requires": {
             "fast-deep-equal": "^2.0.1",

+ 4 - 2
package.json

@@ -33,14 +33,15 @@
     "@babel/core": "^7.2.2",
     "@babel/preset-env": "^7.2.3",
     "autoprefixer": "^9.4.3",
+    "babel-eslint": "^10.0.1",
     "babel-loader": "^8.0.4",
+    "babel-plugin-syntax-dynamic-import": "^6.18.0",
     "bootstrap": "^4.2.1",
     "chromedriver": "^2.45.0",
     "colors": "^1.3.3",
     "css-loader": "^2.1.0",
-    "eslint": "^5.11.1",
+    "eslint": "^5.12.1",
     "exports-loader": "^0.7.0",
-    "extract-text-webpack-plugin": "^4.0.0-alpha0",
     "file-loader": "^3.0.1",
     "grunt": "^1.0.3",
     "grunt-accessibility": "~6.0.0",
@@ -58,6 +59,7 @@
     "imports-loader": "^0.8.0",
     "ink-docstrap": "^1.3.2",
     "jsdoc-babel": "^0.5.0",
+    "mini-css-extract-plugin": "^0.5.0",
     "nightwatch": "^1.0.18",
     "node-sass": "^4.11.0",
     "postcss-css-variables": "^0.11.0",

+ 2 - 2
src/core/Chef.mjs

@@ -157,9 +157,9 @@ class Chef {
      * @param {number} pos.end - The end offset.
      * @returns {Object}
      */
-    calculateHighlights(recipeConfig, direction, pos) {
+    async calculateHighlights(recipeConfig, direction, pos) {
         const recipe = new Recipe(recipeConfig);
-        const highlights = recipe.generateHighlightList();
+        const highlights = await recipe.generateHighlightList();
 
         if (!highlights) return false;
 

+ 2 - 2
src/core/ChefWorker.js

@@ -157,8 +157,8 @@ async function getDishAs(data) {
  * @param {number} pos.start - The start offset.
  * @param {number} pos.end - The end offset.
  */
-function calculateHighlights(recipeConfig, direction, pos) {
-    pos = self.chef.calculateHighlights(recipeConfig, direction, pos);
+async function calculateHighlights(recipeConfig, direction, pos) {
+    pos = await self.chef.calculateHighlights(recipeConfig, direction, pos);
 
     self.postMessage({
         action: "highlightsCalculated",

+ 62 - 14
src/core/Recipe.mjs

@@ -4,12 +4,15 @@
  * @license Apache-2.0
  */
 
-import OpModules from "./config/modules/OpModules";
 import OperationConfig from "./config/OperationConfig.json";
 import OperationError from "./errors/OperationError";
+import Operation from "./Operation";
 import DishError from "./errors/DishError";
 import log from "loglevel";
 
+// Cache container for modules
+let modules = null;
+
 /**
  * The Recipe controls a list of Operations and the Dish they operate on.
  */
@@ -36,16 +39,43 @@ class Recipe  {
      * @param {Object} recipeConfig
      */
     _parseConfig(recipeConfig) {
-        for (let c = 0; c < recipeConfig.length; c++) {
-            const operationName = recipeConfig[c].op;
-            const opConf = OperationConfig[operationName];
-            const opObj = OpModules[opConf.module][operationName];
-            const operation = new opObj();
-            operation.ingValues = recipeConfig[c].args;
-            operation.breakpoint = recipeConfig[c].breakpoint;
-            operation.disabled = recipeConfig[c].disabled;
-            this.addOperation(operation);
+        recipeConfig.forEach(c => {
+            this.opList.push({
+                name: c.op,
+                module: OperationConfig[c.op].module,
+                ingValues: c.args,
+                breakpoint: c.breakpoint,
+                disabled: c.disabled,
+            });
+        });
+    }
+
+
+    /**
+     * Populate elements of opList with operation instances.
+     * Dynamic import here removes top-level cyclic dependency issue.
+     *
+     * @private
+     */
+    async _hydrateOpList() {
+        if (!modules) {
+            // Using Webpack Magic Comments to force the dynamic import to be included in the main chunk
+            // https://webpack.js.org/api/module-methods/
+            modules = await import(/* webpackMode: "eager" */ "./config/modules/OpModules");
+            modules = modules.default;
         }
+
+        this.opList = this.opList.map(o => {
+            if (o instanceof Operation) {
+                return o;
+            } else {
+                const op = new modules[o.module][o.name]();
+                op.ingValues = o.ingValues;
+                op.breakpoint = o.breakpoint;
+                op.disabled = o.disabled;
+                return op;
+            }
+        });
     }
 
 
@@ -55,7 +85,10 @@ class Recipe  {
      * @returns {Object[]}
      */
     get config() {
-        return this.opList.map(op => op.config);
+        return this.opList.map(op => ({
+            op: op.name,
+            args: op.ingValues,
+        }));
     }
 
 
@@ -75,7 +108,19 @@ class Recipe  {
      * @param {Operation[]} operations
      */
     addOperations(operations) {
-        this.opList = this.opList.concat(operations);
+        operations.forEach(o => {
+            if (o instanceof Operation) {
+                this.opList.push(o);
+            } else {
+                this.opList.push({
+                    name: o.name,
+                    module: o.module,
+                    ingValues: o.args,
+                    breakpoint: o.breakpoint,
+                    disabled: o.disabled,
+                });
+            }
+        });
     }
 
 
@@ -108,7 +153,7 @@ class Recipe  {
 
 
     /**
-     * Returns true if there is an Flow Control Operation in this Recipe.
+     * Returns true if there is a Flow Control Operation in this Recipe.
      *
      * @returns {boolean}
      */
@@ -137,6 +182,8 @@ class Recipe  {
 
         if (startFrom === 0) this.lastRunOp = null;
 
+        await this._hydrateOpList();
+
         log.debug(`[*] Executing recipe of ${this.opList.length} operations, starting at ${startFrom}`);
 
         for (let i = startFrom; i < this.opList.length; i++) {
@@ -254,7 +301,8 @@ class Recipe  {
      * @returns {function} highlights[].b
      * @returns {Object[]} highlights[].args
      */
-    generateHighlightList() {
+    async generateHighlightList() {
+        await this._hydrateOpList();
         const highlights = [];
 
         for (let i = 0; i < this.opList.length; i++) {

+ 13 - 8
src/web/RecipeWaiter.mjs

@@ -561,20 +561,25 @@ class RecipeWaiter {
             this.ingredientChildRuleID = null;
 
             // Find relevant rules in the stylesheet
-            for (const i in document.styleSheets[0].cssRules) {
-                if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients") {
-                    this.ingredientRuleID = i;
-                }
-                if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients > div") {
-                    this.ingredientChildRuleID = i;
+            // try/catch for chrome 64+ CORS error on cssRules.
+            try {
+                for (const i in document.styleSheets[0].cssRules) {
+                    if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients") {
+                        this.ingredientRuleID = i;
+                    }
+                    if (document.styleSheets[0].cssRules[i].selectorText === ".ingredients > div") {
+                        this.ingredientChildRuleID = i;
+                    }
                 }
+            } catch (e) {
+                // Do nothing.
             }
         }
 
         if (!this.ingredientRuleID || !this.ingredientChildRuleID) return;
 
-        const ingredientRule = document.styleSheets[0].cssRules[this.ingredientRuleID],
-            ingredientChildRule = document.styleSheets[0].cssRules[this.ingredientChildRuleID];
+        const ingredientRule = document.styleSheets[0].cssRules[this.ingredientRuleID];
+        const ingredientChildRule = document.styleSheets[0].cssRules[this.ingredientChildRuleID];
 
         if (recList.clientWidth < 450) {
             ingredientRule.style.gridTemplateColumns = "auto auto";

+ 14 - 17
webpack.config.js

@@ -1,5 +1,5 @@
 const webpack = require("webpack");
-const ExtractTextPlugin = require("extract-text-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 const path = require("path");
 
 /**
@@ -31,8 +31,6 @@ const banner = `/**
  * limitations under the License.
  */`;
 
-const vendorCSS = new ExtractTextPlugin("vendor.css");
-const projectCSS = new ExtractTextPlugin("styles.css");
 
 module.exports = {
     plugins: [
@@ -49,8 +47,9 @@ module.exports = {
         new webpack.DefinePlugin({
             "process.browser": "true"
         }),
-        vendorCSS,
-        projectCSS
+        new MiniCssExtractPlugin({
+            filename: "[name].css"
+        }),
     ],
     resolve: {
         alias: {
@@ -80,21 +79,19 @@ module.exports = {
             },
             {
                 test: /\.css$/,
-                use: projectCSS.extract({
-                    use: [
-                        { loader: "css-loader" },
-                        { loader: "postcss-loader" },
-                    ]
-                })
+                use: [
+                    MiniCssExtractPlugin.loader,
+                    "css-loader",
+                    "postcss-loader",
+                ]
             },
             {
                 test: /\.scss$/,
-                use: vendorCSS.extract({
-                    use: [
-                        { loader: "css-loader" },
-                        { loader: "sass-loader" }
-                    ]
-                })
+                use: [
+                    MiniCssExtractPlugin.loader,
+                    "css-loader",
+                    "sass-loader",
+                ]
             },
             {
                 test: /\.(ico|eot|ttf|woff|woff2)$/,