From d9b80089c422e9ee0558b5e4d1665a1e51a8d631 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Sun, 12 May 2024 16:29:51 -0500 Subject: [PATCH] feat: support module.hot.accept adds support for part of webpack's module.hot.accept API. The signature is `(dependencies: string | string[], callback: () => void) => void`. --- fixture/a.js | 17 + fixture/c.js | 2 + fixture/d.js | 2 + fixture/e.js | 8 + fixture/f.js | 2 + fixture/g.js | 1 + fixture/h.js | 2 + fixture/i.js | 2 + fixture/j.js | 3 + fixture/k.js | 1 + package.json | 7 +- pnpm-lock.yaml | 652 +++++++++++++++++++++++++++++++++++- src/ModuleGraph.ts | 101 ++++++ src/hotModuleReplacement.ts | 70 ++++ src/index.ts | 33 +- src/launcher.ts | 96 +++--- test/ModuleGraph.test.ts | 125 +++++++ toolchain.config.cjs | 2 +- 18 files changed, 1071 insertions(+), 55 deletions(-) create mode 100644 fixture/a.js create mode 100644 fixture/c.js create mode 100644 fixture/d.js create mode 100644 fixture/e.js create mode 100644 fixture/f.js create mode 100644 fixture/g.js create mode 100644 fixture/h.js create mode 100644 fixture/i.js create mode 100644 fixture/j.js create mode 100644 fixture/k.js create mode 100644 src/ModuleGraph.ts create mode 100644 src/hotModuleReplacement.ts create mode 100644 test/ModuleGraph.test.ts diff --git a/fixture/a.js b/fixture/a.js new file mode 100644 index 0000000..70158bf --- /dev/null +++ b/fixture/a.js @@ -0,0 +1,17 @@ +const { inspect } = require('util') +let c = require('./c') +let e = require('./e') +const f = require('./f') + +function logStatus() { + console.log('in a', inspect({ c, e, f }, { depth: 100 })) +} + +setInterval(logStatus, 1000) + +if (module.hot) { + module.hot.accept(['./c', './e'], () => { + c = require('./c') + e = require('./e') + }) +} diff --git a/fixture/c.js b/fixture/c.js new file mode 100644 index 0000000..6dd8fd4 --- /dev/null +++ b/fixture/c.js @@ -0,0 +1,2 @@ +const d = require('./d') +module.exports = { d } diff --git a/fixture/d.js b/fixture/d.js new file mode 100644 index 0000000..779c6b8 --- /dev/null +++ b/fixture/d.js @@ -0,0 +1,2 @@ +const k = require('./k') +module.exports = { k, d: 'dvalue' } diff --git a/fixture/e.js b/fixture/e.js new file mode 100644 index 0000000..8d8794a --- /dev/null +++ b/fixture/e.js @@ -0,0 +1,8 @@ +let i = require('./i') +let j = require('./j') + +module.exports = { i, j } + +if (module.hot) { + module.hot.accept('./i', () => (i = module.exports.i = require('./i'))) +} diff --git a/fixture/f.js b/fixture/f.js new file mode 100644 index 0000000..a2f7e04 --- /dev/null +++ b/fixture/f.js @@ -0,0 +1,2 @@ +const g = require('./g') +module.exports = { g } diff --git a/fixture/g.js b/fixture/g.js new file mode 100644 index 0000000..5846c6c --- /dev/null +++ b/fixture/g.js @@ -0,0 +1 @@ +module.exports = 'gvalue' diff --git a/fixture/h.js b/fixture/h.js new file mode 100644 index 0000000..a2f7e04 --- /dev/null +++ b/fixture/h.js @@ -0,0 +1,2 @@ +const g = require('./g') +module.exports = { g } diff --git a/fixture/i.js b/fixture/i.js new file mode 100644 index 0000000..2433dc9 --- /dev/null +++ b/fixture/i.js @@ -0,0 +1,2 @@ +let h = require('./h') +module.exports = { h } diff --git a/fixture/j.js b/fixture/j.js new file mode 100644 index 0000000..3cacdd2 --- /dev/null +++ b/fixture/j.js @@ -0,0 +1,3 @@ +const h = require('./h') +const k = require('./k') +module.exports = { h, k } diff --git a/fixture/k.js b/fixture/k.js new file mode 100644 index 0000000..85236e2 --- /dev/null +++ b/fixture/k.js @@ -0,0 +1 @@ +module.exports = 'kvalue' diff --git a/package.json b/package.json index d064f4e..00289eb 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ }, "homepage": "https://github.com/jedwards1211/smart-restart#readme", "devDependencies": { + "@babel/node": "^7.23.9", "@babel/plugin-syntax-flow": "^7.14.5", "@babel/plugin-transform-react-jsx": "^7.14.9", "@jcoreio/eslint-plugin-implicit-dependencies": "^1.1.1", @@ -38,18 +39,20 @@ "@jcoreio/toolchain-mocha": "^4.2.0", "@jcoreio/toolchain-semantic-release": "^4.2.0", "@jcoreio/toolchain-typescript": "^4.2.0", + "@types/chai": "^4.0.0", "@types/debug": "^4.1.1", "@types/lodash": "^4.17.1", + "@types/mocha": "^10.0.6", "@typescript-eslint/eslint-plugin": "^7.6.0", "@typescript-eslint/parser": "^7.6.0", - "chai": "^4.3.7", + "chai": "^4.4.1", "cross-env": "^5.2.0", "eslint": "^8.56.0", "eslint-config-prettier": "^3.3.0", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-no-only-tests": "^3.1.0", "flow-bin": "^0.129.0", - "mocha": "^10.2.0", + "mocha": "^10.4.0", "typescript": "^5.1.0" }, "dependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eacccfe..01dbaa8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,9 @@ dependencies: version: 4.17.11 devDependencies: + '@babel/node': + specifier: ^7.23.9 + version: 7.23.9(@babel/core@7.24.5) '@babel/plugin-syntax-flow': specifier: ^7.14.5 version: 7.24.1(@babel/core@7.24.5) @@ -55,12 +58,18 @@ devDependencies: '@jcoreio/toolchain-typescript': specifier: ^4.2.0 version: 4.2.0(eslint@8.57.0)(typescript@5.4.5) + '@types/chai': + specifier: ^4.0.0 + version: 4.3.16 '@types/debug': specifier: ^4.1.1 version: 4.1.12 '@types/lodash': specifier: ^4.17.1 version: 4.17.1 + '@types/mocha': + specifier: ^10.0.6 + version: 10.0.6 '@typescript-eslint/eslint-plugin': specifier: ^7.6.0 version: 7.8.0(@typescript-eslint/parser@7.8.0)(eslint@8.57.0)(typescript@5.4.5) @@ -68,7 +77,7 @@ devDependencies: specifier: ^7.6.0 version: 7.8.0(eslint@8.57.0)(typescript@5.4.5) chai: - specifier: ^4.3.7 + specifier: ^4.4.1 version: 4.4.1 cross-env: specifier: ^5.2.0 @@ -89,7 +98,7 @@ devDependencies: specifier: ^0.129.0 version: 0.129.0 mocha: - specifier: ^10.2.0 + specifier: ^10.4.0 version: 10.4.0 typescript: specifier: ^5.1.0 @@ -405,6 +414,22 @@ packages: picocolors: 1.0.0 dev: true + /@babel/node@7.23.9(@babel/core@7.24.5): + resolution: {integrity: sha512-/d4ju/POwlGIJlZ+NqWH1qu61wt6ZlTZZZutrK2MOSdaH1JCh726nLw/GSvAjG+LTY6CO9SsB8uWcttnFKm6yg==} + engines: {node: '>=6.9.0'} + hasBin: true + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.24.5 + '@babel/register': 7.23.7(@babel/core@7.24.5) + commander: 4.1.1 + core-js: 3.37.0 + node-environment-flags: 1.0.6 + regenerator-runtime: 0.14.1 + v8flags: 3.2.0 + dev: true + /@babel/parser@7.24.5: resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} engines: {node: '>=6.0.0'} @@ -1706,6 +1731,10 @@ packages: fastq: 1.17.1 dev: true + /@types/chai@4.3.16: + resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} + dev: true + /@types/debug@4.1.12: resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: @@ -1720,6 +1749,10 @@ packages: resolution: {integrity: sha512-X+2qazGS3jxLAIz5JDXDzglAF3KpijdhFxlf/V1+hEsOUc+HnWi81L/uv/EvGuV90WY+7mPGFCUDGfQC3Gj95Q==} dev: true + /@types/mocha@10.0.6: + resolution: {integrity: sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==} + dev: true + /@types/ms@0.7.34: resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} dev: true @@ -1971,15 +2004,57 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true + /array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + is-array-buffer: 3.0.4 + dev: true + /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} dev: true + /array.prototype.reduce@1.0.7: + resolution: {integrity: sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-array-method-boxes-properly: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + is-string: 1.0.7 + dev: true + + /arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + is-array-buffer: 3.0.4 + is-shared-array-buffer: 1.0.3 + dev: true + /assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true + /available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + dependencies: + possible-typed-array-names: 1.0.0 + dev: true + /babel-parse-wild-code@2.1.5: resolution: {integrity: sha512-PxrFABFZZRi8X4BfMsptUOMGwMcSyQVn0D4kR2/uhUSi0fP1s9vkt39axHjwZlCEJS9tbwAdEYLCd+R3syKRrw==} dependencies: @@ -2106,6 +2181,17 @@ packages: write-file-atomic: 3.0.3 dev: true + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + dev: true + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2279,6 +2365,11 @@ packages: browserslist: 4.23.0 dev: true + /core-js@3.37.0: + resolution: {integrity: sha512-fu5vHevQ8ZG4og+LXug8ulUtVxjOcEYvifJr7L5Bfq9GOztVqsKd9/59hUk2ZSbCrS3BqUr3EpaYGIYzq7g3Ug==} + requiresBuild: true + dev: true + /cross-env@5.2.0: resolution: {integrity: sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==} engines: {node: '>=4.0'} @@ -2307,6 +2398,33 @@ packages: which: 2.0.2 dev: true + /data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + + /data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + + /data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + dev: true + /debug@4.1.1: resolution: {integrity: sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==} peerDependencies: @@ -2363,11 +2481,29 @@ packages: strip-bom: 4.0.0 dev: true + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + dev: true + /define-lazy-prop@2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} dev: true + /define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + dev: true + /diff@5.0.0: resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} engines: {node: '>=0.3.1'} @@ -2399,6 +2535,99 @@ packages: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true + /es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.6 + get-intrinsic: 1.2.4 + get-symbol-description: 1.0.2 + globalthis: 1.0.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + internal-slot: 1.0.7 + is-array-buffer: 3.0.4 + is-callable: 1.2.7 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.3 + is-string: 1.0.7 + is-typed-array: 1.1.13 + is-weakref: 1.0.2 + object-inspect: 1.13.1 + object-keys: 1.1.1 + object.assign: 4.1.5 + regexp.prototype.flags: 1.5.2 + safe-array-concat: 1.1.2 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.15 + dev: true + + /es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + dev: true + + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + dev: true + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + dev: true + + /es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + dev: true + + /es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + /es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} dev: true @@ -2716,6 +2945,12 @@ packages: engines: {node: '>=0.10.0'} dev: true + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + /foreground-child@2.0.0: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} @@ -2756,6 +2991,20 @@ packages: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} dev: true + /function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -2775,6 +3024,17 @@ packages: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} dev: true + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + dev: true + /get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} @@ -2795,6 +3055,15 @@ packages: engines: {node: '>=16'} dev: true + /get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + dev: true + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2842,6 +3111,14 @@ packages: type-fest: 0.20.2 dev: true + /globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + gopd: 1.0.1 + dev: true + /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -2854,6 +3131,12 @@ packages: slash: 3.0.0 dev: true + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.4 + dev: true + /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true @@ -2862,6 +3145,10 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -2871,6 +3158,29 @@ packages: engines: {node: '>=8'} dev: true + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + dependencies: + es-define-property: 1.0.0 + dev: true + + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + /hasha@5.2.2: resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} engines: {node: '>=8'} @@ -2891,6 +3201,13 @@ packages: hasBin: true dev: true + /homedir-polyfill@1.0.3: + resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} + engines: {node: '>=0.10.0'} + dependencies: + parse-passwd: 1.0.0 + dev: true + /html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true @@ -2939,18 +3256,68 @@ packages: resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} dev: true + /internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.0.6 + dev: true + + /is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + /is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} dependencies: binary-extensions: 2.2.0 + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + /is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} dependencies: hasown: 2.0.2 dev: true + /is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + dependencies: + is-typed-array: 1.1.13 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + /is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -2983,6 +3350,18 @@ packages: dependencies: is-extglob: 2.1.1 + /is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -3004,6 +3383,21 @@ packages: isobject: 3.0.1 dev: true + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + has-tostringtag: 1.0.2 + dev: true + + /is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + dev: true + /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -3014,6 +3408,27 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.2 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + dependencies: + which-typed-array: 1.1.15 + dev: true + /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} dev: true @@ -3023,6 +3438,12 @@ packages: engines: {node: '>=10'} dev: true + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.7 + dev: true + /is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -3035,6 +3456,10 @@ packages: is-docker: 2.2.1 dev: true + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -3422,6 +3847,13 @@ packages: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} dev: true + /node-environment-flags@1.0.6: + resolution: {integrity: sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==} + dependencies: + object.getownpropertydescriptors: 2.1.8 + semver: 5.7.2 + dev: true + /node-preload@0.2.1: resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} engines: {node: '>=8'} @@ -3486,6 +3918,38 @@ packages: - supports-color dev: true + /object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.getownpropertydescriptors@2.1.8: + resolution: {integrity: sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A==} + engines: {node: '>= 0.8'} + dependencies: + array.prototype.reduce: 1.0.7 + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + gopd: 1.0.1 + safe-array-concat: 1.1.2 + dev: true + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -3591,6 +4055,11 @@ packages: callsites: 3.1.0 dev: true + /parse-passwd@1.0.0: + resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} + engines: {node: '>=0.10.0'} + dev: true + /path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} @@ -3678,6 +4147,11 @@ packages: find-up: 3.0.0 dev: true + /possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + dev: true + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -3744,6 +4218,16 @@ packages: '@babel/runtime': 7.24.5 dev: true + /regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 + dev: true + /regexpu-core@5.3.2: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} @@ -3831,14 +4315,38 @@ packages: queue-microtask: 1.2.3 dev: true + /safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} dev: true + /safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-regex: 1.1.4 + dev: true + /semver@5.6.0: resolution: {integrity: sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==} dev: true + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + dev: true + /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -3859,6 +4367,28 @@ packages: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} dev: true + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + dev: true + + /set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + dev: true + /shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} @@ -3890,6 +4420,16 @@ packages: engines: {node: '>=8'} dev: true + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 + dev: true + /signal-exit@3.0.2: resolution: {integrity: sha512-meQNNykwecVxdu1RlYMKpQx4+wefIYpmxi6gexo/KAbwquJrBUrBmKYJrE8KFkVQAAVWEnwNdu21PgrD77J3xA==} dev: true @@ -3988,6 +4528,33 @@ packages: strip-ansi: 7.1.0 dev: true + /string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + dev: true + + /string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + dev: true + + /string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + dev: true + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -4106,6 +4673,50 @@ packages: engines: {node: '>=8'} dev: true + /typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + dev: true + + /typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + dev: true + /typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: @@ -4118,6 +4729,15 @@ packages: hasBin: true dev: true + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.7 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: false @@ -4171,15 +4791,43 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} dev: true + /v8flags@3.2.0: + resolution: {integrity: sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==} + engines: {node: '>= 0.10'} + dependencies: + homedir-polyfill: 1.0.3 + dev: true + /validate-npm-package-name@5.0.1: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: true + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + /which-module@2.0.0: resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} dev: true + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + dev: true + /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} dependencies: diff --git a/src/ModuleGraph.ts b/src/ModuleGraph.ts new file mode 100644 index 0000000..186931f --- /dev/null +++ b/src/ModuleGraph.ts @@ -0,0 +1,101 @@ +export type HotReloadCallback = () => void + +class Module { + children: Set = new Set() + parents: Set = new Set() + hotReloadCallbacks: Map = new Map() + + constructor(public id: string) {} + + addHotReloadCallback(id: string, callback: HotReloadCallback) { + let callbacks = this.hotReloadCallbacks.get(id) + if (!callbacks) this.hotReloadCallbacks.set(id, (callbacks = [])) + callbacks.push(callback) + } +} + +export default class ModuleGraph { + modules: Map = new Map() + + get(id: string): Module | undefined { + return this.modules.get(id) + } + + getOrCreate(id: string): Module { + let mod = this.modules.get(id) + if (mod) return mod + this.modules.set(id, (mod = new Module(id))) + return mod + } + + register(id: string, parentId: string) { + const mod = this.getOrCreate(id) + const parent = this.getOrCreate(parentId) + mod.parents.add(parent) + parent.children.add(mod) + } + + delete(ids: Iterable) { + for (const id of ids) { + const mod = this.modules.get(id) + if (!mod) continue + for (const child of mod.children) child.parents.delete(mod) + for (const parent of mod.parents) parent.children.delete(mod) + mod.children.clear() + mod.parents.clear() + mod.hotReloadCallbacks.clear() + this.modules.delete(id) + } + } + + addHotReloadCallback( + id: string, + parentId: string, + callback: HotReloadCallback + ) { + const parent = this.getOrCreate(parentId) + parent.addHotReloadCallback(id, callback) + } + + invalidate(ids: Iterable): { + restart: boolean + reloadIds: Set + unloadIds: Set + callbacks: HotReloadCallback[] + } { + let restart = false + const reloadIds: Set = new Set() + const unloadIds: Set = new Set() + const callbacks: Set = new Set() + const queue: Module[] = [] + for (const id of ids) { + const mod = this.modules.get(id) + if (mod) queue.push(mod) + } + + while (queue.length) { + const mod = queue.pop() + if (!mod) continue + unloadIds.add(mod.id) + if (!mod.parents.size) restart = true + for (const parent of mod.parents) { + const hotReloadCallbacks = parent.hotReloadCallbacks.get(mod.id) + if (hotReloadCallbacks?.length) { + reloadIds.add(mod.id) + for (const callback of hotReloadCallbacks) callbacks.add(callback) + } else { + queue.push(parent) + } + } + } + + if (restart) + return { + restart, + reloadIds: new Set(), + unloadIds: new Set(), + callbacks: [], + } + return { restart, reloadIds, unloadIds, callbacks: [...callbacks] } + } +} diff --git a/src/hotModuleReplacement.ts b/src/hotModuleReplacement.ts new file mode 100644 index 0000000..9c6481e --- /dev/null +++ b/src/hotModuleReplacement.ts @@ -0,0 +1,70 @@ +import chalk from 'chalk' +import path from 'path' +import Module from 'module' +import ModuleGraph, { HotReloadCallback } from './ModuleGraph' +import debounce from 'lodash/debounce' +import { sendMessageToParent } from './launcher' + +function log(...args: any[]) { + // eslint-disable-next-line no-console + console.error(chalk.bold.red('[smart-restart]'), ...args) +} + +const moduleGraph = new ModuleGraph() + +// @ts-expect-error can't seem to declaration merge this in +if (Module.prototype.hot) { + log('Something else has already monkeypatched module.hot!') +} else { + Object.defineProperties(Module.prototype, { + hot: { + get(this: Module) { + return { + accept: ( + requests: string | string[], + callback: HotReloadCallback + ) => { + for (const request of Array.isArray(requests) + ? requests + : [requests]) { + // @ts-expect-error not typed + const targetId = Module._resolveFilename(request, this) + moduleGraph.addHotReloadCallback(targetId, this.id, callback) + } + }, + } + }, + }, + }) +} + +export function registerModuleParent(moduleName: string, parentName: string) { + moduleGraph.register(moduleName, parentName) +} + +const invalidateQueue: string[] = [] + +export function invalidate(moduleName: string) { + invalidateQueue.push(moduleName) + invalidateSoon() +} + +const invalidateSoon = debounce(() => { + if (!invalidateQueue.length) return + const moduleNames = [...invalidateQueue] + invalidateQueue.length = 0 + const { restart, reloadIds, unloadIds, callbacks } = + moduleGraph.invalidate(moduleNames) + if (restart) { + sendMessageToParent({ restart: true }) + } else { + log( + `hot reloading modules:${[...reloadIds] + .map((id) => `\n - ${path.relative(process.cwd(), id)}`) + .join('')}` + ) + for (const id of unloadIds) delete require.cache[id] + for (const callback of callbacks) callback() + log(`hot reload complete`) + } +}, 100) diff --git a/src/index.ts b/src/index.ts index 3781105..e7f533b 100755 --- a/src/index.ts +++ b/src/index.ts @@ -34,6 +34,11 @@ export interface LaunchOptions { restartOnExit?: boolean } +export type MessageForChild = + | { type: 'launch'; options: LaunchOptions } + | { type: 'clearRequireCache' } + | { type: 'fileChange'; file: string } + function launch(ops: LaunchOptions) { let lastErr = '' let child: ChildProcess | undefined @@ -81,6 +86,14 @@ function launch(ops: LaunchOptions) { if (typeof codeOrError !== 'number' || options.restartOnExit) restart() } + function sendToChild(message: MessageForChild) { + child?.send(message, (err) => { + if (err) { + log('failed to send message to child', err) + } + }) + } + function restart() { if (childRunning) { if (killTimeout == null) { @@ -107,14 +120,17 @@ function launch(ops: LaunchOptions) { watcher.on('change', function (file) { log('File', path.relative(process.cwd(), file), 'has changed') if (deleteRequireCache[file]) clearRequireCacheSoon() - else restartSoon() + // else restartSoon() + else { + sendToChild({ type: 'fileChange', file }) + } }) const args = [ options.command, [ ...options.commandOptions, - path.resolve(__dirname, 'launcher.js'), + path.resolve(__dirname, `launcher${path.extname(__filename)}`), ...options.args, ], Object.assign(options.spawnOptions, { @@ -132,12 +148,14 @@ function launch(ops: LaunchOptions) { debug('spawned child pid: ', child.pid) child.on('message', (message: MessageFromChild) => { debug('message received', message) - const { status, file, parent, err } = message + const { status, file, parent, err, restart: childSaysRestart } = message + if (childSaysRestart) { + restart() + return + } if (status === 'ready') { debug('sending message:', options) - child?.send(options, (error) => { - if (error) debug(error.stack) - }) + sendToChild({ type: 'launch', options }) return } if (err && (options.restartOnError || err !== lastErr)) { @@ -164,9 +182,8 @@ function launch(ops: LaunchOptions) { } restart() - const restartSoon = debounce(restart, 500) const clearRequireCacheSoon = debounce(() => { - child?.send('CLEAR_REQUIRE_CACHE') + sendToChild({ type: 'clearRequireCache' }) log('cleared require cache') }, 500) diff --git a/src/launcher.ts b/src/launcher.ts index 8958cbf..4939913 100644 --- a/src/launcher.ts +++ b/src/launcher.ts @@ -4,8 +4,9 @@ import path from 'path' import createDebug from 'debug' import chalk from 'chalk' import util from 'util' -import module = require('module') -import { type LaunchOptions } from '.' +import Module = require('module') +import { MessageForChild } from '.' +import { invalidate, registerModuleParent } from './hotModuleReplacement' const debug = createDebug('smart-restart:launcher') @@ -15,6 +16,7 @@ function log(...args: any[]) { } export type MessageFromChild = { + restart?: boolean file?: string parent?: string err?: string @@ -26,59 +28,69 @@ const natives = process.binding('natives') const deleteRequireCache: { [key in string]?: boolean } = {} -function sendMessage(message: MessageFromChild) { +export function sendMessageToParent(message: MessageFromChild) { debug('sending message: ', message) process.send?.(message) } -process.on('message', (options: 'CLEAR_REQUIRE_CACHE' | LaunchOptions) => { - debug('message received', options) - if (options === 'CLEAR_REQUIRE_CACHE') { - for (const key in deleteRequireCache) { - if (deleteRequireCache[key]) delete require.cache[key] +process.on('message', (message: MessageForChild) => { + debug('message received', message) + switch (message.type) { + case 'clearRequireCache': { + for (const key in deleteRequireCache) { + if (deleteRequireCache[key]) delete require.cache[key] + } + return } - return - } - const origDeleteRequireCache = new Set(options.deleteRequireCache) - options.deleteRequireCache?.forEach((m) => (deleteRequireCache[m] = true)) - const main = path.resolve(options.main) - // @ts-expect-error not typed - const _load_orig = module._load - // @ts-expect-error not typed - module._load = function ( - name: string, - parent: { id: string }, - isMain: boolean - ) { - // @ts-expect-error not typed - const file = module._resolveFilename(name, parent) - if ( - parent && - (options.includeModules || file.indexOf('node_modules') < 0) && - !natives[file] && - file !== main - ) { - if (!origDeleteRequireCache.has(file)) { - deleteRequireCache[file] = deleteRequireCache[parent.id] || false + case 'fileChange': { + invalidate(message.file) + return + } + case 'launch': { + const { options } = message + const origDeleteRequireCache = new Set(options.deleteRequireCache) + options.deleteRequireCache?.forEach((m) => (deleteRequireCache[m] = true)) + const main = path.resolve(options.main) + // @ts-expect-error not typed + const _load_orig = Module._load + // @ts-expect-error not typed + Module._load = function ( + name: string, + parent: { id: string }, + isMain: boolean + ) { + // @ts-expect-error not typed + const file = Module._resolveFilename(name, parent) + registerModuleParent(file, parent.id) + if ( + parent && + (options.includeModules || file.indexOf('node_modules') < 0) && + !natives[file] && + file !== main + ) { + if (!origDeleteRequireCache.has(file)) { + deleteRequireCache[file] = deleteRequireCache[parent.id] || false + } + sendMessageToParent({ + file, + parent: parent.id, + }) + } else { + debug('ignoring module: ', name) + } + return _load_orig(name, parent, isMain) } - sendMessage({ - file, - parent: parent.id, - }) - } else { - debug('ignoring module: ', name) + require(main) } - return _load_orig(name, parent, isMain) } - require(main) }) -sendMessage({ status: 'ready' }) +sendMessageToParent({ status: 'ready' }) process.on('uncaughtException', (err: any) => { log('uncaught exception in child process:', err) - sendMessage({ err: util.inspect(err) }) + sendMessageToParent({ err: util.inspect(err) }) }) process.on('unhandledRejection', (err: any) => { log('unhandled rejection in child process:', err) - sendMessage({ err: util.inspect(err) }) + sendMessageToParent({ err: util.inspect(err) }) }) diff --git a/test/ModuleGraph.test.ts b/test/ModuleGraph.test.ts new file mode 100644 index 0000000..e4215d9 --- /dev/null +++ b/test/ModuleGraph.test.ts @@ -0,0 +1,125 @@ +import { describe, it } from 'mocha' +import { expect } from 'chai' +import ModuleGraph from '../src/ModuleGraph' + +describe(`ModuleGraph`, function () { + it(`.register() and .delete()`, function () { + const graph = new ModuleGraph() + graph.register('child1', 'parent1') + graph.register('child1', 'parent2') + graph.register('child2', 'parent2') + expect(graph.get('child1')?.id).to.equal('child1') + expect(graph.get('child2')?.id).to.equal('child2') + expect([...graph.get('child1')!.parents].map((p) => p.id)).to.deep.equal([ + 'parent1', + 'parent2', + ]) + expect([...graph.get('child2')!.parents].map((p) => p.id)).to.deep.equal([ + 'parent2', + ]) + + expect([...graph.get('parent1')!.children].map((c) => c.id)).to.deep.equal([ + 'child1', + ]) + expect([...graph.get('parent2')!.children].map((c) => c.id)).to.deep.equal([ + 'child1', + 'child2', + ]) + graph.delete(['parent1']) + + expect(graph.get('parent1')).to.equal(undefined) + expect([...graph.get('child1')!.parents].map((p) => p.id)).to.deep.equal([ + 'parent2', + ]) + expect([...graph.get('child2')!.parents].map((p) => p.id)).to.deep.equal([ + 'parent2', + ]) + expect([...graph.get('parent2')!.children].map((c) => c.id)).to.deep.equal([ + 'child1', + 'child2', + ]) + + graph.delete(['parent2']) + + expect([...graph.get('child1')!.parents].map((p) => p.id)).to.deep.equal([]) + expect([...graph.get('child2')!.parents].map((p) => p.id)).to.deep.equal([]) + }) + + it(`.invalidate()`, function () { + /** + * A + * * * + * E C + * * \ \ + * I J D + * \ / \ / + * F H K + * \ / + * G + */ + const graph = new ModuleGraph() + for (const [child, parents] of [ + ['G', ['F', 'H']], + ['H', ['I', 'J']], + ['I', ['E']], + ['J', ['E']], + ['K', ['J', 'D']], + ['E', ['A']], + ['D', ['C']], + ['C', ['A']], + ] as [string, string[]][]) { + for (const parent of parents) graph.register(child, parent) + } + + for (const mod of ['A', 'F', 'G']) { + expect(graph.invalidate([mod])).to.deep.equal({ + restart: true, + reloadIds: new Set(), + unloadIds: new Set(), + callbacks: [], + }) + expect(graph.invalidate([mod, 'H'])).to.deep.equal({ + restart: true, + reloadIds: new Set(), + unloadIds: new Set(), + callbacks: [], + }) + } + function reloadC() {} + function reloadE() {} + function reloadI() {} + graph.getOrCreate('E').hotReloadCallbacks.set('I', [reloadI]) + graph.getOrCreate('A').hotReloadCallbacks.set('E', [reloadE]) + graph.getOrCreate('A').hotReloadCallbacks.set('C', [reloadC]) + expect(graph.invalidate(['H', 'K'])).to.deep.equal({ + restart: false, + reloadIds: new Set(['C', 'E', 'I']), + unloadIds: new Set(['H', 'I', 'J', 'K', 'D', 'C', 'E']), + callbacks: [reloadC, reloadE, reloadI], + }) + expect(graph.invalidate(['K'])).to.deep.equal({ + restart: false, + reloadIds: new Set(['C', 'E']), + unloadIds: new Set(['J', 'K', 'D', 'C', 'E']), + callbacks: [reloadC, reloadE], + }) + expect(graph.invalidate(['D'])).to.deep.equal({ + restart: false, + reloadIds: new Set(['C']), + unloadIds: new Set(['D', 'C']), + callbacks: [reloadC], + }) + expect(graph.invalidate(['J'])).to.deep.equal({ + restart: false, + reloadIds: new Set(['E']), + unloadIds: new Set(['J', 'E']), + callbacks: [reloadE], + }) + expect(graph.invalidate(['I'])).to.deep.equal({ + restart: false, + reloadIds: new Set(['I']), + unloadIds: new Set(['I']), + callbacks: [reloadI], + }) + }) +}) diff --git a/toolchain.config.cjs b/toolchain.config.cjs index 5af1b04..8080b50 100644 --- a/toolchain.config.cjs +++ b/toolchain.config.cjs @@ -1,6 +1,6 @@ /* eslint-env node, es2018 */ module.exports = { - cjsBabelEnv: { forceAllTransforms: true }, + cjsBabelEnv: { targets: { node: 16 } }, // esmBabelEnv: { targets: { node: 16 } }, outputEsm: false, // disables ESM output (default: true) // esWrapper: true, // outputs ES module wrappers for CJS modules (default: false)