diff --git a/.c8rc.json b/.c8rc.json new file mode 100644 index 00000000..3e42a4a6 --- /dev/null +++ b/.c8rc.json @@ -0,0 +1,5 @@ +{ + "include": ["src/**"], + "exclude": ["src/**/__tests__/**"], + "reporter": ["text", "lcov"] +} diff --git a/.changeset/petite-signs-dance.md b/.changeset/petite-signs-dance.md new file mode 100644 index 00000000..b353956a --- /dev/null +++ b/.changeset/petite-signs-dance.md @@ -0,0 +1,5 @@ +--- +"htmljs-parser": patch +--- + +Refactor parser to allow individual states to process multiple characters. This allows for eager scanning, simplifies things some, and improves performance by about 30% in realworld tempaltes. diff --git a/package-lock.json b/package-lock.json index 1c54cfb4..23a385aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@types/node": "^20.11.16", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", + "c8": "^11.0.0", "cross-env": "^7.0.3", "degit": "^2.8.4", "esbuild": "0.20.0", @@ -30,245 +31,11 @@ "mitata": "^0.1.8", "mocha": "^10.2.0", "mocha-snap": "^5.0.0", - "nyc": "^15.1.0", "prettier": "^3.2.5", "tsx": "^4.19.2", "typescript": "^5.3.3" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/runtime": { "version": "7.27.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", @@ -279,52 +46,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", - "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, "node_modules/@changesets/apply-release-plan": { @@ -1270,23 +999,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1297,17 +1009,6 @@ "node": ">=8" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1494,6 +1195,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2029,20 +1737,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2149,26 +1843,6 @@ "node": ">= 8" } }, - "node_modules/append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "default-require-extensions": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true, - "license": "MIT" - }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -2400,97 +2074,299 @@ "dev": true, "license": "ISC" }, - "node_modules/browserslist": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", - "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "node_modules/c8": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-11.0.0.tgz", + "integrity": "sha512-e/uRViGHSVIJv7zsaDKM7VRn2390TgHXqUSvYwPHBQaU6L7E9L0n9JbdkwdYPvshDT0KymBmmlwSpms3yBaMNg==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", + "license": "ISC", "dependencies": { - "caniuse-lite": "^1.0.30001726", - "electron-to-chromium": "^1.5.173", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^8.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" }, "bin": { - "browserslist": "cli.js" + "c8": "bin/c8.js" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": "20 || >=22" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } } }, - "node_modules/caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "node_modules/c8/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "dependencies": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, "engines": { - "node": ">=8" + "node": "18 || 20 || >=22" } }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "node_modules/c8/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "18 || 20 || >=22" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/c8/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "node_modules/c8/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "license": "MIT" + }, + "node_modules/c8/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8/node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/c8/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c8/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/c8/node_modules/test-exclude": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-8.0.0.tgz", + "integrity": "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^13.0.6", + "minimatch": "^10.2.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/c8/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/c8/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/c8/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -2499,46 +2375,46 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" + "license": "MIT", + "engines": { + "node": ">=6" + } }, "node_modules/chalk": { "version": "4.1.2", @@ -2618,16 +2494,6 @@ "node": ">=8" } }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -2760,13 +2626,6 @@ "node": ">=18" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true, - "license": "MIT" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2774,13 +2633,6 @@ "dev": true, "license": "MIT" }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -2894,16 +2746,6 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -2921,22 +2763,6 @@ "dev": true, "license": "MIT" }, - "node_modules/default-require-extensions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", - "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "strip-bom": "^4.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -3067,13 +2893,6 @@ "node": ">= 0.4" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.182", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.182.tgz", - "integrity": "sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==", - "dev": true, - "license": "ISC" - }, "node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", @@ -3257,13 +3076,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "license": "MIT" - }, "node_modules/esbuild": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.0.tgz", @@ -3935,24 +3747,6 @@ "node": ">=8" } }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -4047,48 +3841,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -4167,16 +3919,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -4225,16 +3967,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -4502,46 +4234,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasha/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hasha/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -4668,16 +4360,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5146,13 +4828,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "license": "MIT" - }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -5246,63 +4921,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "append-transform": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-processinfo": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", - "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", - "dev": true, - "license": "ISC", - "dependencies": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.3", - "istanbul-lib-coverage": "^3.2.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -5334,21 +4952,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/istanbul-reports": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", @@ -5363,13 +4966,6 @@ "node": ">=8" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", @@ -5398,19 +4994,6 @@ "node": ">=4" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -5432,19 +5015,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -5564,13 +5134,6 @@ "node": ">=8" } }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5697,42 +5260,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -5826,6 +5353,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mitata": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/mitata/-/mitata-0.1.14.tgz", @@ -6035,280 +5572,62 @@ }, "node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "process-on-spawn": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nyc": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", - "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "get-package-type": "^0.1.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "bin": { - "nyc": "bin/nyc.js" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/nyc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/nyc/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/nyc/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/nyc/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/nyc/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/nyc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/nyc/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/nyc/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=8" + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/nyc/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/nyc/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/nyc/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "path-key": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nyc/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/object-inspect": { @@ -6539,19 +5858,6 @@ "node": ">=8" } }, - "node_modules/p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -6562,22 +5868,6 @@ "node": ">=6" } }, - "node_modules/package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/package-manager-detector": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", @@ -6638,6 +5928,33 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -6691,19 +6008,6 @@ "node": ">=6" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -6740,19 +6044,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/process-on-spawn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", - "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "fromentries": "^1.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6920,19 +6211,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", - "dev": true, - "license": "ISC", - "dependencies": { - "es6-error": "^4.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -6943,13 +6221,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true, - "license": "ISC" - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -7235,13 +6506,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "license": "ISC" - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -7443,41 +6707,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/spawn-wrap/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, "node_modules/spawndamnit": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", @@ -7646,16 +6875,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -7721,67 +6940,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8481,16 +7639,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, "node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", @@ -8576,37 +7724,6 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -8617,16 +7734,28 @@ "punycode": "^2.1.0" } }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" } }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -8728,13 +7857,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true, - "license": "ISC" - }, "node_modules/which-typed-array": { "version": "1.1.19", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", @@ -8841,26 +7963,6 @@ "dev": true, "license": "ISC" }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -8871,13 +7973,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, "node_modules/yaml": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", diff --git a/package.json b/package.json index 799b9316..92871831 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "@types/node": "^20.11.16", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", + "c8": "^11.0.0", "cross-env": "^7.0.3", "degit": "^2.8.4", "esbuild": "0.20.0", @@ -24,7 +25,6 @@ "mitata": "^0.1.8", "mocha": "^10.2.0", "mocha-snap": "^5.0.0", - "nyc": "^15.1.0", "prettier": "^3.2.5", "tsx": "^4.19.2", "typescript": "^5.3.3" @@ -63,7 +63,7 @@ "bench": "tsx bench.mts", "build": "tsc -b && tsx build.mts", "change": "changeset add", - "ci:test": "nyc npm test -- --forbid-only", + "ci:test": "c8 npm test -- --forbid-only", "format": "npm run lint:eslint -- --fix && npm run lint:prettier -- --write && (fixpack || true)", "lint": "tsc -b && npm run lint:eslint && npm run lint:prettier -- -l && fixpack", "lint:eslint": "eslint -f visualstudio .", diff --git a/src/__tests__/api.test.ts b/src/__tests__/api.test.ts new file mode 100644 index 00000000..aafc05b1 --- /dev/null +++ b/src/__tests__/api.test.ts @@ -0,0 +1,54 @@ +import assert from "node:assert/strict"; +import { createParser, isValidAttrValue } from ".."; + +describe("api", () => { + it("parses without any handlers registered", () => { + const parser = createParser({}); + // Exercises every optional handler call against an empty handler object. + parser.parse( + [ + '', + "", + "", + "", + "// line comment", + "/* block comment */", + "$ const scriptlet = 1;", + "$ { block(); }", + "static const statement = 1", + "(y) { body }>", + " ${placeholder}", + "", + " typeArgs=1/>", + "unclosed(", + ].join("\n"), + ); + }); + + it("reports locations for offset ranges", () => { + const parser = createParser({}); + parser.parse("
\n hi\n
"); + assert.deepEqual(parser.locationAt({ start: 8, end: 8 }), { + start: { line: 1, character: 2 }, + end: { line: 1, character: 2 }, + }); + assert.deepEqual(parser.locationAt({ start: 1, end: 12 }), { + start: { line: 0, character: 1 }, + end: { line: 2, character: 1 }, + }); + }); +}); + +describe("validation internals", () => { + it("rejects doubled operators followed by a terminator", () => { + assert.equal(isValidAttrValue(" ++ ,", true), 0); + }); + + it("continues past arrows before terminators", () => { + assert.equal(isValidAttrValue("a=>,b", true), 2); + }); + + it("rejects identifiers separated by whitespace at end of input", () => { + assert.equal(isValidAttrValue("x i", true), 0); + }); +}); diff --git a/src/__tests__/fixtures/argument-attr-ternary/__snapshots__/argument-attr-ternary.expected.txt b/src/__tests__/fixtures/argument-attr-ternary/__snapshots__/argument-attr-ternary.expected.txt new file mode 100644 index 00000000..1aeb9501 --- /dev/null +++ b/src/__tests__/fixtures/argument-attr-ternary/__snapshots__/argument-attr-ternary.expected.txt @@ -0,0 +1,10 @@ +1╭─
+ │ ││ │ ││ ││ │ ╰─ closeTagEnd(div) + │ ││ │ ││ ││ ╰─ closeTagName "div" + │ ││ │ ││ │╰─ closeTagStart " \ No newline at end of file diff --git a/src/__tests__/fixtures/attr-bound-concise/__snapshots__/attr-bound-concise.expected.txt b/src/__tests__/fixtures/attr-bound-concise/__snapshots__/attr-bound-concise.expected.txt new file mode 100644 index 00000000..68b7f7be --- /dev/null +++ b/src/__tests__/fixtures/attr-bound-concise/__snapshots__/attr-bound-concise.expected.txt @@ -0,0 +1,7 @@ +1╭─ div foo:=bar + │ │ │ │ │ ├─ closeTagEnd(div) + │ │ │ │ │ ╰─ openTagEnd + │ │ │ │ ╰─ attrValue:bound.value "bar" + │ │ │ ╰─ attrValue:bound ":=bar" + │ │ ╰─ attrName "foo" + ╰─ ╰─ tagName "div" \ No newline at end of file diff --git a/src/__tests__/fixtures/attr-bound-concise/input.marko b/src/__tests__/fixtures/attr-bound-concise/input.marko new file mode 100644 index 00000000..1448861e --- /dev/null +++ b/src/__tests__/fixtures/attr-bound-concise/input.marko @@ -0,0 +1 @@ +div foo:=bar \ No newline at end of file diff --git a/src/__tests__/fixtures/attr-eof-after-name/__snapshots__/attr-eof-after-name.expected.txt b/src/__tests__/fixtures/attr-eof-after-name/__snapshots__/attr-eof-after-name.expected.txt new file mode 100644 index 00000000..06e78100 --- /dev/null +++ b/src/__tests__/fixtures/attr-eof-after-name/__snapshots__/attr-eof-after-name.expected.txt @@ -0,0 +1,5 @@ +1╭─
x
+ │ ││││││ │ ╰─ closeTagEnd(div) + │ ││││││ ╰─ closeTagName "div" + │ │││││╰─ closeTagStart "x \ No newline at end of file diff --git a/src/__tests__/fixtures/cdata-eof/__snapshots__/cdata-eof.expected.txt b/src/__tests__/fixtures/cdata-eof/__snapshots__/cdata-eof.expected.txt new file mode 100644 index 00000000..5db5c0b6 --- /dev/null +++ b/src/__tests__/fixtures/cdata-eof/__snapshots__/cdata-eof.expected.txt @@ -0,0 +1,2 @@ +1╭─ + │ ││ │ ╰─ error(INVALID_EXPRESSION:Mismatched group. A "]" character was found when ")" was expected.) + │ ││ ╰─ attrName + │ │╰─ tagName "div" + ╰─ ╰─ openTagStart \ No newline at end of file diff --git a/src/__tests__/fixtures/expression-mismatched-group-pair/input.marko b/src/__tests__/fixtures/expression-mismatched-group-pair/input.marko new file mode 100644 index 00000000..7f17e0bb --- /dev/null +++ b/src/__tests__/fixtures/expression-mismatched-group-pair/input.marko @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/__tests__/fixtures/html-comment-eof/__snapshots__/html-comment-eof.expected.txt b/src/__tests__/fixtures/html-comment-eof/__snapshots__/html-comment-eof.expected.txt new file mode 100644 index 00000000..b80df7a3 --- /dev/null +++ b/src/__tests__/fixtures/html-comment-eof/__snapshots__/html-comment-eof.expected.txt @@ -0,0 +1,2 @@ +1╭─ + │ │ │ ╰─ comment.value " b " + │ │ ├─ comment "" + │ │ ╰─ openTagEnd + │ ├─ closeTagEnd(div) + ╰─ ╰─ tagName "span" +3╭─ + ╰─ ╰─ closeTagEnd(span) \ No newline at end of file diff --git a/src/__tests__/fixtures/semicolon-comments/input.marko b/src/__tests__/fixtures/semicolon-comments/input.marko new file mode 100644 index 00000000..3d65e25d --- /dev/null +++ b/src/__tests__/fixtures/semicolon-comments/input.marko @@ -0,0 +1,2 @@ +div; /* a */ +span; diff --git a/src/__tests__/fixtures/tag-name-placeholder-empty/__snapshots__/tag-name-placeholder-empty.expected.txt b/src/__tests__/fixtures/tag-name-placeholder-empty/__snapshots__/tag-name-placeholder-empty.expected.txt new file mode 100644 index 00000000..bfc8242f --- /dev/null +++ b/src/__tests__/fixtures/tag-name-placeholder-empty/__snapshots__/tag-name-placeholder-empty.expected.txt @@ -0,0 +1,3 @@ +1╭─ <${}/> + │ │ ╰─ error(MALFORMED_PLACEHOLDER:Invalid placeholder, the expression cannot be missing) + ╰─ ╰─ openTagStart \ No newline at end of file diff --git a/src/__tests__/fixtures/tag-name-placeholder-empty/input.marko b/src/__tests__/fixtures/tag-name-placeholder-empty/input.marko new file mode 100644 index 00000000..d68ea0c3 --- /dev/null +++ b/src/__tests__/fixtures/tag-name-placeholder-empty/input.marko @@ -0,0 +1 @@ +<${}/> \ No newline at end of file diff --git a/src/__tests__/fixtures/tag-name-placeholder-eof/__snapshots__/tag-name-placeholder-eof.expected.txt b/src/__tests__/fixtures/tag-name-placeholder-eof/__snapshots__/tag-name-placeholder-eof.expected.txt new file mode 100644 index 00000000..caf50d09 --- /dev/null +++ b/src/__tests__/fixtures/tag-name-placeholder-eof/__snapshots__/tag-name-placeholder-eof.expected.txt @@ -0,0 +1,3 @@ +1╭─ <${a + │ │ ╰─ error(MALFORMED_OPEN_TAG:EOF reached while parsing tag name) + ╰─ ╰─ openTagStart \ No newline at end of file diff --git a/src/__tests__/fixtures/tag-name-placeholder-eof/input.marko b/src/__tests__/fixtures/tag-name-placeholder-eof/input.marko new file mode 100644 index 00000000..16659d11 --- /dev/null +++ b/src/__tests__/fixtures/tag-name-placeholder-eof/input.marko @@ -0,0 +1 @@ +<${a \ No newline at end of file diff --git a/src/__tests__/fixtures/tag-var-comma-concise/__snapshots__/tag-var-comma-concise.expected.txt b/src/__tests__/fixtures/tag-var-comma-concise/__snapshots__/tag-var-comma-concise.expected.txt new file mode 100644 index 00000000..7eac43c4 --- /dev/null +++ b/src/__tests__/fixtures/tag-var-comma-concise/__snapshots__/tag-var-comma-concise.expected.txt @@ -0,0 +1,10 @@ +1╭─ div/v, a=1 + │ │ ││ ││╰─ attrValue.value + │ │ ││ │╰─ attrValue "=1" + │ │ ││ ╰─ attrName + │ │ │╰─ tagVar.value + │ │ ╰─ tagVar "/v" + ╰─ ╰─ tagName "div" +2╭─ + │ ├─ openTagEnd + ╰─ ╰─ closeTagEnd(div) \ No newline at end of file diff --git a/src/__tests__/fixtures/tag-var-comma-concise/input.marko b/src/__tests__/fixtures/tag-var-comma-concise/input.marko new file mode 100644 index 00000000..801eb4a6 --- /dev/null +++ b/src/__tests__/fixtures/tag-var-comma-concise/input.marko @@ -0,0 +1 @@ +div/v, a=1 diff --git a/src/__tests__/fixtures/template-string-dollar/__snapshots__/template-string-dollar.expected.txt b/src/__tests__/fixtures/template-string-dollar/__snapshots__/template-string-dollar.expected.txt new file mode 100644 index 00000000..213f9725 --- /dev/null +++ b/src/__tests__/fixtures/template-string-dollar/__snapshots__/template-string-dollar.expected.txt @@ -0,0 +1,7 @@ +1╭─
+ │ ││ │││ ╰─ openTagEnd:selfClosed "/>" + │ ││ ││╰─ attrValue.value "`x$y`" + │ ││ │╰─ attrValue "=`x$y`" + │ ││ ╰─ attrName + │ │╰─ tagName "div" + ╰─ ╰─ openTagStart \ No newline at end of file diff --git a/src/__tests__/fixtures/template-string-dollar/input.marko b/src/__tests__/fixtures/template-string-dollar/input.marko new file mode 100644 index 00000000..45c83e92 --- /dev/null +++ b/src/__tests__/fixtures/template-string-dollar/input.marko @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/__tests__/fixtures/template-string-empty-placeholder/__snapshots__/template-string-empty-placeholder.expected.txt b/src/__tests__/fixtures/template-string-empty-placeholder/__snapshots__/template-string-empty-placeholder.expected.txt new file mode 100644 index 00000000..95e15ecd --- /dev/null +++ b/src/__tests__/fixtures/template-string-empty-placeholder/__snapshots__/template-string-empty-placeholder.expected.txt @@ -0,0 +1,5 @@ +1╭─
+ │ ││ │ ╰─ error(MALFORMED_PLACEHOLDER:Invalid placeholder, the expression cannot be missing) + │ ││ ╰─ attrName + │ │╰─ tagName "div" + ╰─ ╰─ openTagStart \ No newline at end of file diff --git a/src/__tests__/fixtures/template-string-empty-placeholder/input.marko b/src/__tests__/fixtures/template-string-empty-placeholder/input.marko new file mode 100644 index 00000000..f5f03b50 --- /dev/null +++ b/src/__tests__/fixtures/template-string-empty-placeholder/input.marko @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/src/__tests__/fixtures/text-block-eof/__snapshots__/text-block-eof.expected.txt b/src/__tests__/fixtures/text-block-eof/__snapshots__/text-block-eof.expected.txt new file mode 100644 index 00000000..a98e1be7 --- /dev/null +++ b/src/__tests__/fixtures/text-block-eof/__snapshots__/text-block-eof.expected.txt @@ -0,0 +1 @@ +1╰─ -- \ No newline at end of file diff --git a/src/__tests__/fixtures/text-block-eof/input.marko b/src/__tests__/fixtures/text-block-eof/input.marko new file mode 100644 index 00000000..7489accb --- /dev/null +++ b/src/__tests__/fixtures/text-block-eof/input.marko @@ -0,0 +1 @@ +-- \ No newline at end of file diff --git a/src/__tests__/fixtures/text-block-trailing-space/__snapshots__/text-block-trailing-space.expected.txt b/src/__tests__/fixtures/text-block-trailing-space/__snapshots__/text-block-trailing-space.expected.txt new file mode 100644 index 00000000..68f2e0db --- /dev/null +++ b/src/__tests__/fixtures/text-block-trailing-space/__snapshots__/text-block-trailing-space.expected.txt @@ -0,0 +1,11 @@ +1╭─ div -- + │ │ ╰─ openTagEnd + ╰─ ╰─ tagName "div" +2╭─ hello + ╰─ ╰─ text "hello" +3╭─ div2 + │ ├─ closeTagEnd(div) + ╰─ ╰─ tagName "div2" +4╭─ + │ ├─ openTagEnd + ╰─ ╰─ closeTagEnd(div2) \ No newline at end of file diff --git a/src/__tests__/fixtures/text-block-trailing-space/input.marko b/src/__tests__/fixtures/text-block-trailing-space/input.marko new file mode 100644 index 00000000..b224eb9b --- /dev/null +++ b/src/__tests__/fixtures/text-block-trailing-space/input.marko @@ -0,0 +1,3 @@ +div -- + hello +div2 diff --git a/src/__tests__/validate.test.ts b/src/__tests__/validate.test.ts index 59f8f1a6..d2652da3 100644 --- a/src/__tests__/validate.test.ts +++ b/src/__tests__/validate.test.ts @@ -26,6 +26,14 @@ describe("validation helpers", () => { it("rejects mismatched closing groups", () => { assert.equal(isValidStatement(")"), 0); }); + + it("treats newlines in template literals as unguarded", () => { + assert.equal(isValidStatement("`foo\nbar`"), 1); + }); + + it("treats newlines in enclosed template literals as guarded", () => { + assert.equal(isValidStatement("(`foo\nbar`)"), 2); + }); }); describe("isValidScriptlet", () => { @@ -102,5 +110,13 @@ describe("validation helpers", () => { it("accepts continued multiline enclosed logical expression", () => { assert.equal(isValidAttrValue("a && (\nb\n)", true), 2); }); + + it("accepts keyword operator operand ending the input", () => { + assert.equal(isValidAttrValue("a as b", true), 2); + }); + + it("rejects keyword operator with no operand", () => { + assert.equal(isValidAttrValue("a as ", true), 0); + }); }); }); diff --git a/src/core/Parser.ts b/src/core/Parser.ts index 6e0ff88e..6835024d 100644 --- a/src/core/Parser.ts +++ b/src/core/Parser.ts @@ -23,9 +23,7 @@ export interface StateDefinition

{ pos: number, ) => Partial

; exit: (this: Parser, activeRange: P) => void; - char: (this: Parser, code: number, activeRange: P) => void; - eol: (this: Parser, length: number, activeRange: P) => void; - eof: (this: Parser, activeRange: P) => void; + parse: (this: Parser, data: string, maxPos: number, activeRange: P) => void; return: (this: Parser, child: Meta, activeRange: P) => void; } @@ -35,7 +33,6 @@ export class Parser { declare public data: string; declare public activeState: StateDefinition; declare public activeRange: Meta; - declare public forward: number; declare public activeTag: STATE.OpenTagMeta | undefined; // Used to reference the closest open tag declare public activeAttr: STATE.AttrMeta | undefined; // Used to reference the current attribute that is being parsed declare public indent: string; // Used to build the indent for the current concise line @@ -79,7 +76,6 @@ export class Parser { const { activeRange, activeState } = this; const parent = (this.activeRange = activeRange.parent); this.activeState = parent.state; - this.forward = 0; activeRange.end = this.pos; activeState.exit.call(this, activeRange); this.activeState.return.call(this, activeRange, parent); @@ -113,14 +109,6 @@ export class Parser { return true; } - matchAnyAtPos(a: Range, list: (Range | string)[]) { - for (const item of list) { - if (this.matchAtPos(a, item)) return true; - } - - return false; - } - /** * Look ahead to see if the given str matches the substring sequence * beyond @@ -215,7 +203,6 @@ export class Parser { if (this.lookAheadFor(str, cur)) { this.pos = cur; - if (this.forward > 1) this.forward = 1; return true; } @@ -228,7 +215,7 @@ export class Parser { return this.lookAtCharCodeAhead(behind); } - onlyWhitespaceRemainsOnLine(start = 1) { + onlyWhitespaceRemainsOnLine(start: number) { const maxOffset = this.maxPos - this.pos; let ahead = start; @@ -292,10 +279,12 @@ export class Parser { this.data = data; this.indent = ""; this.textPos = -1; - this.forward = 1; this.isConcise = true; this.beginMixedMode = this.endingMixedModeAtEOL = false; this.lines = this.activeTag = this.activeAttr = undefined; + // Drop any state left over from a previous parse so reusing a parser + // does not chain (and retain) the old state metas via parent references. + this.activeRange = undefined as unknown as Meta; // Skip the byte order mark (BOM) sequence // at the beginning of the file if there is one: @@ -304,30 +293,8 @@ export class Parser { this.pos = data.charCodeAt(0) === 0xfeff ? 1 : 0; this.enterState(STATE.CONCISE_HTML_CONTENT); - while (this.pos < maxPos) { - const code = data.charCodeAt(this.pos); - - if (code === CODE.NEWLINE) { - this.forward = 1; - this.activeState.eol.call(this, 1, this.activeRange); - } else if ( - code === CODE.CARRIAGE_RETURN && - data.charCodeAt(this.pos + 1) === CODE.NEWLINE - ) { - this.forward = 2; - this.activeState.eol.call(this, 2, this.activeRange); - } else { - this.forward = 1; - this.activeState.char.call(this, code, this.activeRange); - } - - this.pos += this.forward; - } - - while (this.pos === this.maxPos) { - this.forward = 1; - this.activeState.eof.call(this, this.activeRange); - if (this.forward !== 0) break; + while (this.pos <= maxPos) { + this.activeState.parse.call(this, data, maxPos, this.activeRange); } } } diff --git a/src/states/ATTRIBUTE.ts b/src/states/ATTRIBUTE.ts index 080e7b50..9beb47fa 100644 --- a/src/states/ATTRIBUTE.ts +++ b/src/states/ATTRIBUTE.ts @@ -58,94 +58,109 @@ export const ATTRIBUTE: StateDefinition = { this.activeAttr = undefined; }, - char(code, attr) { - if (isWhitespaceCode(code)) { - return; - } else if ( - code === CODE.EQUAL || - (code === CODE.COLON && this.lookAtCharCodeAhead(1) === CODE.EQUAL) || - (code === CODE.PERIOD && this.lookAheadFor("..")) - ) { - attr.valueStart = this.pos; - this.forward = 0; - - if (code === CODE.COLON) { - ensureAttrName(this, attr); - attr.bound = true; - this.pos += 2; // skip := - this.consumeWhitespace(); - } else if (code === CODE.PERIOD) { - attr.spread = true; - this.pos += 3; // skip ... - } else { - ensureAttrName(this, attr); - this.pos++; // skip = - this.consumeWhitespace(); + parse(data, maxPos, attr) { + while (this.pos < maxPos) { + const code = data.charCodeAt(this.pos); + + if (code === CODE.NEWLINE || code === CODE.CARRIAGE_RETURN) { + if (this.isConcise) { + this.exitState(); + return; // parent handles newline + } + this.pos += + code === CODE.CARRIAGE_RETURN && + data.charCodeAt(this.pos + 1) === CODE.NEWLINE + ? 2 + : 1; + continue; } - attr.stage = ATTR_STAGE.VALUE; - const expr = this.enterState(STATE.EXPRESSION); - expr.operators = true; - expr.terminatedByWhitespace = true; - expr.shouldTerminate = this.isConcise - ? this.activeTag!.stage === TAG_STAGE.ATTR_GROUP - ? shouldTerminateConciseGroupedAttrValue - : shouldTerminateConciseAttrValue - : shouldTerminateHtmlAttrValue; - } else if (code === CODE.OPEN_PAREN) { - ensureAttrName(this, attr); - attr.stage = ATTR_STAGE.ARGUMENT; - this.pos++; // skip ( - this.forward = 0; - this.enterState(STATE.EXPRESSION).shouldTerminate = matchesCloseParen; - } else if ( - code === CODE.OPEN_ANGLE_BRACKET && - attr.stage === ATTR_STAGE.NAME - ) { - attr.stage = ATTR_STAGE.TYPE_PARAMS; - this.pos++; // skip < - this.forward = 0; - const expr = this.enterState(STATE.EXPRESSION); - expr.inType = true; - expr.forceType = true; - expr.shouldTerminate = matchesCloseAngleBracket; - } else if (code === CODE.OPEN_CURLY_BRACE && attr.args) { - ensureAttrName(this, attr); - attr.stage = ATTR_STAGE.BLOCK; - this.pos++; // skip { - this.forward = 0; - this.enterState(STATE.EXPRESSION).shouldTerminate = - matchesCloseCurlyBrace; - } else if (attr.stage === ATTR_STAGE.UNKNOWN) { - if (code === CODE.OPEN_ANGLE_BRACKET) { - return this.emitError( - this.pos, - ErrorCode.INVALID_ATTRIBUTE_NAME, - 'Invalid attribute name. Attribute name cannot begin with the "<" character.', - ); + if (isWhitespaceCode(code)) { + this.pos++; + continue; } - attr.stage = ATTR_STAGE.NAME; - this.forward = 0; - const expr = this.enterState(STATE.EXPRESSION); - expr.terminatedByWhitespace = true; - expr.shouldTerminate = this.isConcise - ? this.activeTag!.stage === TAG_STAGE.ATTR_GROUP - ? shouldTerminateConciseGroupedAttrName - : shouldTerminateConciseAttrName - : shouldTerminateHtmlAttrName; - } else { - this.exitState(); - } - }, + if ( + code === CODE.EQUAL || + (code === CODE.COLON && data.charCodeAt(this.pos + 1) === CODE.EQUAL) || + (code === CODE.PERIOD && this.lookAheadFor("..")) + ) { + attr.valueStart = this.pos; - eol() { - if (this.isConcise) { - this.exitState(); + if (code === CODE.COLON) { + ensureAttrName(this, attr); + attr.bound = true; + this.pos += 2; // skip := + this.consumeWhitespace(); + } else if (code === CODE.PERIOD) { + attr.spread = true; + this.pos += 3; // skip ... + } else { + ensureAttrName(this, attr); + this.pos++; // skip = + this.consumeWhitespace(); + } + + attr.stage = ATTR_STAGE.VALUE; + const expr = this.enterState(STATE.EXPRESSION); + expr.operators = true; + expr.terminatedByWhitespace = true; + expr.shouldTerminate = this.isConcise + ? this.activeTag!.stage === TAG_STAGE.ATTR_GROUP + ? shouldTerminateConciseGroupedAttrValue + : shouldTerminateConciseAttrValue + : shouldTerminateHtmlAttrValue; + return; + } else if (code === CODE.OPEN_PAREN) { + ensureAttrName(this, attr); + attr.stage = ATTR_STAGE.ARGUMENT; + this.pos++; // skip ( + this.enterState(STATE.EXPRESSION).shouldTerminate = matchesCloseParen; + return; + } else if ( + code === CODE.OPEN_ANGLE_BRACKET && + attr.stage === ATTR_STAGE.NAME + ) { + attr.stage = ATTR_STAGE.TYPE_PARAMS; + this.pos++; // skip < + const expr = this.enterState(STATE.EXPRESSION); + expr.inType = true; + expr.forceType = true; + expr.shouldTerminate = matchesCloseAngleBracket; + return; + } else if (code === CODE.OPEN_CURLY_BRACE && attr.args) { + ensureAttrName(this, attr); + attr.stage = ATTR_STAGE.BLOCK; + this.pos++; // skip { + this.enterState(STATE.EXPRESSION).shouldTerminate = + matchesCloseCurlyBrace; + return; + } else if (attr.stage === ATTR_STAGE.UNKNOWN) { + if (code === CODE.OPEN_ANGLE_BRACKET) { + return this.emitError( + this.pos, + ErrorCode.INVALID_ATTRIBUTE_NAME, + 'Invalid attribute name. Attribute name cannot begin with the "<" character.', + ); + } + + attr.stage = ATTR_STAGE.NAME; + // Don't advance pos: EXPRESSION starts at current char + const expr = this.enterState(STATE.EXPRESSION); + expr.terminatedByWhitespace = true; + expr.shouldTerminate = this.isConcise + ? this.activeTag!.stage === TAG_STAGE.ATTR_GROUP + ? shouldTerminateConciseGroupedAttrName + : shouldTerminateConciseAttrName + : shouldTerminateHtmlAttrName; + return; + } else { + this.exitState(); + return; + } } - }, - eof(attr) { + // EOF if (this.isConcise) { this.exitState(); } else { diff --git a/src/states/BEGIN_DELIMITED_HTML_BLOCK.ts b/src/states/BEGIN_DELIMITED_HTML_BLOCK.ts index b0655bac..bf0928c2 100644 --- a/src/states/BEGIN_DELIMITED_HTML_BLOCK.ts +++ b/src/states/BEGIN_DELIMITED_HTML_BLOCK.ts @@ -33,28 +33,48 @@ export const BEGIN_DELIMITED_HTML_BLOCK: StateDefinition exit() {}, - char(code, block) { - if (code === CODE.HYPHEN) { - block.delimiter += "-"; - } else { + parse(data, maxPos, block) { + if (this.pos === maxPos) { + htmlEOF.call(this); + this.pos++; + return; + } + + while (this.pos < maxPos) { + const code = data.charCodeAt(this.pos); + + if (code === CODE.NEWLINE || code === CODE.CARRIAGE_RETURN) { + const len = + code === CODE.CARRIAGE_RETURN && + data.charCodeAt(this.pos + 1) === CODE.NEWLINE + ? 2 + : 1; + const prevPos = this.pos; + this.beginHtmlBlock(block.delimiter, false); + handleDelimitedBlockEOL(this, true, len, block); + if (this.pos === prevPos) this.pos += len; // advance past newline if not already advanced + return; + } + + if (code === CODE.HYPHEN) { + block.delimiter += "-"; + this.pos++; + continue; + } + + // Non-hyphen, non-newline: check if whitespace-only remains on line const startPos = this.pos; if (!this.consumeWhitespaceOnLine()) { + // Non-whitespace content on this line: start single-line HTML block this.pos = startPos + 1; - this.forward = 0; this.beginHtmlBlock(undefined, true); + return; } + // Only whitespace to EOL: consumeWhitespaceOnLine set pos to newline + // Continue to let the newline trigger EOL handling above } }, - eol(len, block) { - // We have reached the end of the first delimiter... we need to skip over any indentation on the next - // line and we might also find that the multi-line, delimited block is immediately ended - this.beginHtmlBlock(block.delimiter, false); - handleDelimitedBlockEOL(this, true, len, block); - }, - - eof: htmlEOF, - return() {}, }; @@ -99,7 +119,6 @@ function handleDelimitedBlockEOL( if (parser.lookAheadFor(endHtmlBlockLookahead, parser.pos + newLineLength)) { parser.endText(); parser.pos += newLineLength + endHtmlBlockLookahead.length; - parser.forward = 0; if (parser.consumeWhitespaceOnLine(0)) { parser.exitState(); @@ -119,7 +138,6 @@ function handleDelimitedBlockEOL( // is any indentation that we need to skip over as we continue parsing the HTML in this // multiline HTML block parser.pos += indent.length; - parser.forward = 0; parser.startText(); // We stay in the same state since we are still parsing a multiline, delimited HTML block } else if (indent && !parser.onlyWhitespaceRemainsOnLine(newLineLength)) { diff --git a/src/states/CDATA.ts b/src/states/CDATA.ts index 4fceb28b..f2f896ce 100644 --- a/src/states/CDATA.ts +++ b/src/states/CDATA.ts @@ -1,4 +1,4 @@ -import { CODE, ErrorCode, Parser, type StateDefinition } from "../internal"; +import { ErrorCode, Parser, type StateDefinition } from "../internal"; // We enter STATE.CDATA after we see "")) { - this.pos += 3; // skip ]]> - this.exitState(); - return; + parse(data, maxPos, cdata) { + const idx = data.indexOf("]]>", this.pos); + if (idx === -1) { + return this.emitError( + cdata, + ErrorCode.MALFORMED_CDATA, + "EOF reached while parsing CDATA", + ); } - }, - - eol() {}, - eof(cdata) { - this.emitError( - cdata, - ErrorCode.MALFORMED_CDATA, - "EOF reached while parsing CDATA", - ); + this.pos = idx + 3; // skip ]]> + this.exitState(); }, + /* c8 ignore next -- never has child states */ return() {}, }; @@ -49,7 +46,7 @@ export function checkForCDATA(parser: Parser) { if (parser.lookAheadFor("![CDATA[")) { parser.endText(); parser.enterState(CDATA); - parser.pos += 8; // skip ![CDATA[ + parser.pos += 9; // skip - this.exitState(); - ensureExpectedCloseTag(this, closeTag); + parse(data, maxPos, closeTag) { + const idx = data.indexOf(">", this.pos); + if (idx === -1) { + return this.emitError( + closeTag, + ErrorCode.MALFORMED_CLOSE_TAG, + "EOF reached while parsing closing tag", + ); } - }, - - eol() {}, - eof(closeTag) { - this.emitError( - closeTag, - ErrorCode.MALFORMED_CLOSE_TAG, - "EOF reached while parsing closing tag", - ); + this.pos = idx + 1; // skip > + this.exitState(); + ensureExpectedCloseTag(this, closeTag); }, + /* c8 ignore next -- never has child states */ return() {}, }; @@ -54,17 +51,15 @@ export function checkForClosingTag(parser: Parser) { if (!match) { const { tagName } = parser.activeTag!; const tagNameLen = tagName.end - tagName.start; - if (tagNameLen) { - skip += tagNameLen; // skip - match = - (parser.lookAheadFor("/", curPos) && - parser.lookAheadFor(">", 1 + curPos + tagNameLen) && - parser.matchAtPos(tagName, { - start: 1 + curPos, - end: 1 + curPos + tagNameLen, - })) || - false; - } + skip += tagNameLen; // skip + match = + (parser.lookAheadFor("/", curPos) && + parser.lookAheadFor(">", 1 + curPos + tagNameLen) && + parser.matchAtPos(tagName, { + start: 1 + curPos, + end: 1 + curPos + tagNameLen, + })) || + false; } if (match) { @@ -74,14 +69,12 @@ export function checkForClosingTag(parser: Parser) { end: curPos + 1, }); - if ( - ensureExpectedCloseTag(parser, { - start: parser.pos, - end: (parser.pos += skip), - }) - ) { - parser.exitState(); - } + // Always succeeds since the closing tag name was matched above. + ensureExpectedCloseTag(parser, { + start: parser.pos, + end: (parser.pos += skip), + }); + parser.exitState(); return true; } diff --git a/src/states/CONCISE_HTML_CONTENT.ts b/src/states/CONCISE_HTML_CONTENT.ts index 6a916418..75d06fe5 100644 --- a/src/states/CONCISE_HTML_CONTENT.ts +++ b/src/states/CONCISE_HTML_CONTENT.ts @@ -23,12 +23,46 @@ export const CONCISE_HTML_CONTENT: StateDefinition = { }; }, + /* c8 ignore next -- the root state never exits */ exit() {}, - char(code) { - if (isWhitespaceCode(code)) { - this.indent += this.data[this.pos]; - } else { + parse(data, maxPos) { + if (this.pos === maxPos) { + htmlEOF.call(this); + this.pos++; + return; + } + + while (this.pos < maxPos) { + const code = data.charCodeAt(this.pos); + + if (code === CODE.NEWLINE || code === CODE.CARRIAGE_RETURN) { + this.indent = ""; + this.pos += + code === CODE.CARRIAGE_RETURN && + data.charCodeAt(this.pos + 1) === CODE.NEWLINE + ? 2 + : 1; + continue; + } + + if (isWhitespaceCode(code)) { + // Eagerly consume the indent up to the end of the line. + const start = this.pos; + let next: number; + do { + this.pos++; + } while ( + this.pos < maxPos && + isWhitespaceCode((next = data.charCodeAt(this.pos))) && + next !== CODE.NEWLINE && + next !== CODE.CARRIAGE_RETURN + ); + this.indent += data.slice(start, this.pos); + continue; + } + + // Non-whitespace character: dispatch based on current indent level const curIndent = this.indent.length; const indentStart = this.pos - curIndent - 1; let parentTag = this.activeTag; @@ -74,38 +108,36 @@ export const CONCISE_HTML_CONTENT: StateDefinition = { switch (code) { case CODE.OPEN_ANGLE_BRACKET: this.beginMixedMode = true; - this.pos--; - this.beginHtmlBlock(undefined, false); + this.beginHtmlBlock(undefined, false); // pos stays at < return; case CODE.DOLLAR: - if (isWhitespaceCode(this.lookAtCharCodeAhead(1))) { - this.pos++; // skip space after $ + if (isWhitespaceCode(data.charCodeAt(this.pos + 1))) { + this.pos++; // skip $, INLINE_SCRIPT starts at space this.enterState(STATE.INLINE_SCRIPT); return; } - break; + break; // fall through to enter OPEN_TAG case CODE.HYPHEN: - if (this.lookAtCharCodeAhead(1) === CODE.HYPHEN) { + if (data.charCodeAt(this.pos + 1) === CODE.HYPHEN) { this.enterState(STATE.BEGIN_DELIMITED_HTML_BLOCK); - this.pos--; + return; // pos stays at the first -, BEGIN_DELIMITED_HTML_BLOCK parses it } else { this.emitError( this.pos, ErrorCode.INVALID_LINE_START, 'A line in concise mode cannot start with a single hyphen. Use "--" instead. See: https://github.com/marko-js/htmljs-parser/issues/43', ); + return; } - return; case CODE.FORWARD_SLASH: - // Check next character to see if we are in a comment - switch (this.lookAtCharCodeAhead(1)) { + switch (data.charCodeAt(this.pos + 1)) { case CODE.FORWARD_SLASH: this.enterState(STATE.JS_COMMENT_LINE); - this.pos++; // skip / + this.pos += 2; // skip // return; case CODE.ASTERISK: this.enterState(STATE.JS_COMMENT_BLOCK); - this.pos++; // skip * + this.pos += 2; // skip /* return; default: this.emitError( @@ -118,16 +150,10 @@ export const CONCISE_HTML_CONTENT: StateDefinition = { } this.enterState(STATE.OPEN_TAG); - this.forward = 0; // START_TAG_NAME expects to start at the first character + return; // pos stays at current char, OPEN_TAG sees it } }, - eol() { - this.indent = ""; - }, - - eof: htmlEOF, - return(child) { this.indent = ""; this.isConcise = true; diff --git a/src/states/DECLARATION.ts b/src/states/DECLARATION.ts index b683ddd8..aa3d10d5 100644 --- a/src/states/DECLARATION.ts +++ b/src/states/DECLARATION.ts @@ -25,26 +25,26 @@ export const DECLARATION: StateDefinition = { exit() {}, - char(code, declaration) { - if (code === CODE.QUESTION) { - if (this.lookAtCharCodeAhead(1) === CODE.CLOSE_ANGLE_BRACKET) { - exitDeclaration(this, declaration, 2); // will skip ?> - } - } else if (code === CODE.CLOSE_ANGLE_BRACKET) { - exitDeclaration(this, declaration, 1); // will skip > + parse(data, maxPos, declaration) { + const idx = data.indexOf(">", this.pos); + if (idx === -1) { + return this.emitError( + declaration, + ErrorCode.MALFORMED_DECLARATION, + "EOF reached while parsing declaration", + ); } - }, - - eol() {}, - eof(declaration) { - this.emitError( - declaration, - ErrorCode.MALFORMED_DECLARATION, - "EOF reached while parsing declaration", - ); + if (idx > this.pos && data.charCodeAt(idx - 1) === CODE.QUESTION) { + this.pos = idx - 1; + exitDeclaration(this, declaration, 2); // skip ?> + } else { + this.pos = idx; + exitDeclaration(this, declaration, 1); // skip > + } }, + /* c8 ignore next -- never has child states */ return() {}, }; diff --git a/src/states/DTD.ts b/src/states/DTD.ts index 945921d6..b27d385a 100644 --- a/src/states/DTD.ts +++ b/src/states/DTD.ts @@ -1,4 +1,4 @@ -import { CODE, ErrorCode, type StateDefinition } from "../internal"; +import { ErrorCode, type StateDefinition } from "../internal"; // We enter STATE.DTD after we encounter a "". @@ -26,22 +26,20 @@ export const DTD: StateDefinition = { }); }, - char(code) { - if (code === CODE.CLOSE_ANGLE_BRACKET) { - this.pos++; // skip > - this.exitState(); + parse(data, maxPos, documentType) { + const idx = data.indexOf(">", this.pos); + if (idx === -1) { + return this.emitError( + documentType, + ErrorCode.MALFORMED_DOCUMENT_TYPE, + "EOF reached while parsing document type", + ); } - }, - - eol() {}, - eof(documentType) { - this.emitError( - documentType, - ErrorCode.MALFORMED_DOCUMENT_TYPE, - "EOF reached while parsing document type", - ); + this.pos = idx + 1; // skip > + this.exitState(); }, + /* c8 ignore next -- never has child states */ return() {}, }; diff --git a/src/states/EXPRESSION.ts b/src/states/EXPRESSION.ts index 6e02e694..a874ec16 100644 --- a/src/states/EXPRESSION.ts +++ b/src/states/EXPRESSION.ts @@ -13,6 +13,7 @@ export interface ExpressionMeta extends Meta { groupStack: number[]; operators: boolean; wasComment: boolean; + hadUnguardedNewline: boolean; inType: boolean; forceType: boolean; ternaryDepth: number; @@ -71,6 +72,7 @@ export const EXPRESSION: StateDefinition = { shouldTerminate, operators: false, wasComment: false, + hadUnguardedNewline: false, inType: false, forceType: false, ternaryDepth: 0, @@ -82,245 +84,282 @@ export const EXPRESSION: StateDefinition = { exit() {}, - char(code, expression) { - if (!expression.groupStack.length) { - if (expression.terminatedByWhitespace && isWhitespaceCode(code)) { - if (!checkForOperators(this, expression, false)) { + parse(data, maxPos, expression) { + while (this.pos < maxPos) { + const code = data.charCodeAt(this.pos); + + // EOL handling + if (code === CODE.NEWLINE || code === CODE.CARRIAGE_RETURN) { + const len = + code === CODE.CARRIAGE_RETURN && + data.charCodeAt(this.pos + 1) === CODE.NEWLINE + ? 2 + : 1; + + const prevPos = this.pos; + if ( + !expression.groupStack.length && + (expression.terminatedByEOL || expression.terminatedByWhitespace) && + (expression.wasComment || + !checkForOperators(this, expression, true)) && + !( + expression.consumeIndentedContent && + isIndentCode(data.charCodeAt(prevPos + len)) + ) + ) { + // Don't advance past the newline. this.exitState(); + return; } - return; + + expression.wasComment = false; + if (!expression.groupStack.length) + expression.hadUnguardedNewline = true; + // checkForOperators may have advanced pos; only advance by len if it didn't + if (this.pos === prevPos) this.pos += len; + continue; } - if (expression.shouldTerminate(code, this.data, this.pos, expression)) { - let wasExpression = false; - if (expression.operators) { - const prevNonWhitespacePos = lookBehindWhile( - isWhitespaceCode, - this.data, - this.pos - 1, - ); - if (prevNonWhitespacePos > expression.start) { - wasExpression = - lookBehindForOperator( - expression, - this.data, - prevNonWhitespacePos, - ) !== -1; + // Termination checks (no groupStack) + if (!expression.groupStack.length) { + if (expression.terminatedByWhitespace && isWhitespaceCode(code)) { + if (!checkForOperators(this, expression, false)) { + this.exitState(); + return; } + // checkForOperators already advanced this.pos + continue; } - if (!wasExpression) { - this.exitState(); - return; + if (expression.shouldTerminate(code, data, this.pos, expression)) { + let wasExpression = false; + if (expression.operators) { + const prevNonWhitespacePos = lookBehindWhile( + isWhitespaceCode, + data, + this.pos - 1, + ); + if (prevNonWhitespacePos > expression.start) { + wasExpression = + lookBehindForOperator( + expression, + data, + prevNonWhitespacePos, + ) !== -1; + } + } + + if (!wasExpression) { + this.exitState(); + return; + } } } - } - switch (code) { - case CODE.DOUBLE_QUOTE: - this.enterState(STATE.STRING); - break; - case CODE.SINGLE_QUOTE: - this.enterState(STATE.STRING).quoteCharCode = code; - break; - case CODE.BACKTICK: - this.enterState(STATE.TEMPLATE_STRING); - break; - case CODE.QUESTION: - if (expression.operators && !expression.groupStack.length) { - expression.ternaryDepth++; + switch (code) { + case CODE.DOUBLE_QUOTE: + this.enterState(STATE.STRING); + this.pos++; // skip " + return; + case CODE.SINGLE_QUOTE: + this.enterState(STATE.STRING).quoteCharCode = code; + this.pos++; // skip ' + return; + case CODE.BACKTICK: + this.enterState(STATE.TEMPLATE_STRING); + this.pos++; // skip ` + return; + case CODE.QUESTION: + if (expression.operators && !expression.groupStack.length) { + expression.ternaryDepth++; + this.pos++; // skip ? + this.consumeWhitespace(); + continue; + } this.pos++; - this.forward = 0; - this.consumeWhitespace(); - } - break; - case CODE.COLON: - if (expression.operators && !expression.groupStack.length) { - if (expression.ternaryDepth) { - expression.ternaryDepth--; - } else { - expression.inType = true; + break; + case CODE.COLON: + if (expression.operators && !expression.groupStack.length) { + if (expression.ternaryDepth) { + expression.ternaryDepth--; + } else { + expression.inType = true; + } + this.pos++; // skip : + this.consumeWhitespace(); + continue; } - this.pos++; - this.forward = 0; - this.consumeWhitespace(); - } - break; - case CODE.EQUAL: - if (expression.operators) { - if (this.lookAtCharCodeAhead(1) === CODE.CLOSE_ANGLE_BRACKET) { - if ( - expression.inType && - !expression.forceType && - this.getPreviousNonWhitespaceCharCode() !== CODE.CLOSE_PAREN + break; + case CODE.EQUAL: + if (expression.operators) { + if (data.charCodeAt(this.pos + 1) === CODE.CLOSE_ANGLE_BRACKET) { + if ( + expression.inType && + !expression.forceType && + this.getPreviousNonWhitespaceCharCode() !== CODE.CLOSE_PAREN + ) { + expression.inType = false; + } + this.pos++; // skip =, outer iteration handles > + } else if ( + !(expression.forceType || expression.groupStack.length) ) { expression.inType = false; } - this.pos++; - } else if (!(expression.forceType || expression.groupStack.length)) { - expression.inType = false; + this.pos++; // skip = (or the char after =>) + this.consumeWhitespace(); + continue; } - this.pos++; - this.forward = 0; - this.consumeWhitespace(); - } - break; - case CODE.FORWARD_SLASH: - // Check next character to see if we are in a comment or regexp - switch (this.lookAtCharCodeAhead(1)) { - case CODE.FORWARD_SLASH: - this.enterState(STATE.JS_COMMENT_LINE); + break; + case CODE.FORWARD_SLASH: + switch (data.charCodeAt(this.pos + 1)) { + case CODE.FORWARD_SLASH: + this.enterState(STATE.JS_COMMENT_LINE); + this.pos += 2; // skip // + return; + case CODE.ASTERISK: + this.enterState(STATE.JS_COMMENT_BLOCK); + this.pos += 2; // skip /* + return; + default: + if (canFollowDivision(this.getPreviousNonWhitespaceCharCode())) { + this.pos++; + this.consumeWhitespace(); + continue; + } else { + this.enterState(STATE.REGULAR_EXPRESSION); + this.pos++; // skip /, REGULAR_EXPRESSION starts after + return; + } + } + case CODE.OPEN_PAREN: + expression.groupStack.push(CODE.CLOSE_PAREN); + this.pos++; + break; + case CODE.OPEN_SQUARE_BRACKET: + expression.groupStack.push(CODE.CLOSE_SQUARE_BRACKET); + this.pos++; + break; + case CODE.OPEN_CURLY_BRACE: + expression.groupStack.push(CODE.CLOSE_CURLY_BRACE); + this.pos++; + break; + case CODE.OPEN_ANGLE_BRACKET: + if (expression.inType) { + expression.groupStack.push(CODE.CLOSE_ANGLE_BRACKET); this.pos++; - break; - case CODE.ASTERISK: - this.enterState(STATE.JS_COMMENT_BLOCK); + } else if (expression.operators && !expression.groupStack.length) { this.pos++; - break; - default: { - if (canFollowDivision(this.getPreviousNonWhitespaceCharCode())) { + this.consumeWhitespace(); + continue; + } else { + this.pos++; + } + break; + + case CODE.CLOSE_PAREN: + case CODE.CLOSE_SQUARE_BRACKET: + case CODE.CLOSE_CURLY_BRACE: + case CODE.CLOSE_ANGLE_BRACKET: { + if (code === CODE.CLOSE_ANGLE_BRACKET) { + if ( + !expression.inType || + data.charCodeAt(this.pos - 1) === CODE.EQUAL + ) { this.pos++; - this.forward = 0; - this.consumeWhitespace(); - } else { - this.enterState(STATE.REGULAR_EXPRESSION); + break; } - break; } - } - break; - case CODE.OPEN_PAREN: - expression.groupStack.push(CODE.CLOSE_PAREN); - break; - case CODE.OPEN_SQUARE_BRACKET: - expression.groupStack.push(CODE.CLOSE_SQUARE_BRACKET); - break; - case CODE.OPEN_CURLY_BRACE: - expression.groupStack.push(CODE.CLOSE_CURLY_BRACE); - break; - case CODE.OPEN_ANGLE_BRACKET: - if (expression.inType) { - expression.groupStack.push(CODE.CLOSE_ANGLE_BRACKET); - } else if (expression.operators && !expression.groupStack.length) { - this.pos++; - this.forward = 0; - this.consumeWhitespace(); - } - break; - - case CODE.CLOSE_PAREN: - case CODE.CLOSE_SQUARE_BRACKET: - case CODE.CLOSE_CURLY_BRACE: - case CODE.CLOSE_ANGLE_BRACKET: { - if (code === CODE.CLOSE_ANGLE_BRACKET) { - if ( - !expression.inType || - this.lookAtCharCodeAhead(-1) === CODE.EQUAL - ) { - break; + + if (!expression.groupStack.length) { + return this.emitError( + expression, + ErrorCode.INVALID_EXPRESSION, + 'Mismatched group. A closing "' + + String.fromCharCode(code) + + '" character was found but it is not matched with a corresponding opening character.', + ); } - } - if (!expression.groupStack.length) { - return this.emitError( - expression, - ErrorCode.INVALID_EXPRESSION, - 'Mismatched group. A closing "' + - String.fromCharCode(code) + - '" character was found but it is not matched with a corresponding opening character.', - ); - } + const expectedCode = expression.groupStack.pop()!; + if (expectedCode !== code) { + return this.emitError( + expression, + ErrorCode.INVALID_EXPRESSION, + 'Mismatched group. A "' + + String.fromCharCode(code) + + '" character was found when "' + + String.fromCharCode(expectedCode) + + '" was expected.', + ); + } - const expectedCode = expression.groupStack.pop()!; - if (expectedCode !== code) { - return this.emitError( - expression, - ErrorCode.INVALID_EXPRESSION, - 'Mismatched group. A "' + - String.fromCharCode(code) + - '" character was found when "' + - String.fromCharCode(expectedCode) + - '" was expected.', - ); + this.pos++; + break; } - break; + default: + this.pos++; + break; } } - }, - - eol(len, expression) { - if ( - !expression.groupStack.length && - (expression.terminatedByEOL || expression.terminatedByWhitespace) && - (expression.wasComment || !checkForOperators(this, expression, true)) && - !( - expression.consumeIndentedContent && - isIndentCode(this.lookAtCharCodeAhead(len)) - ) - ) { - this.exitState(); - } - expression.wasComment = false; - }, - eof(expression) { + // EOF if ( !expression.groupStack.length && (this.isConcise || expression.terminatedByEOL) ) { this.exitState(); - } else { - const { parent } = expression; + return; + } - switch (parent.state) { - case STATE.ATTRIBUTE: { - const attr = parent as STATE.AttrMeta; - if (!attr.spread && !attr.name) { - return this.emitError( - expression, - ErrorCode.MALFORMED_OPEN_TAG, - 'EOF reached while parsing attribute name for the "' + - this.read(this.activeTag!.tagName) + - '" tag', - ); - } + const { parent } = expression; + switch (parent.state) { + case STATE.ATTRIBUTE: { + const attr = parent as STATE.AttrMeta; + if (!attr.spread && !attr.name) { return this.emitError( expression, ErrorCode.MALFORMED_OPEN_TAG, - `EOF reached while parsing attribute value for the ${ - attr.spread - ? "..." - : attr.name - ? `"${this.read(attr.name)}"` - : `"default"` - } attribute`, + 'EOF reached while parsing attribute name for the "' + + this.read(this.activeTag!.tagName) + + '" tag', ); } - case STATE.TAG_NAME: - return this.emitError( - expression, - ErrorCode.MALFORMED_OPEN_TAG, - "EOF reached while parsing tag name", - ); - - case STATE.PLACEHOLDER: - return this.emitError( - expression, - ErrorCode.MALFORMED_PLACEHOLDER, - "EOF reached while parsing placeholder", - ); + return this.emitError( + expression, + ErrorCode.MALFORMED_OPEN_TAG, + `EOF reached while parsing attribute value for the ${ + // A missing name was reported above, so this is a spread or named attribute. + attr.spread ? "..." : `"${this.read(attr.name!)}"` + } attribute`, + ); } - return this.emitError( - expression, - ErrorCode.INVALID_EXPRESSION, - "EOF reached while parsing expression", - ); + case STATE.TAG_NAME: + return this.emitError( + expression, + ErrorCode.MALFORMED_OPEN_TAG, + "EOF reached while parsing tag name", + ); + + case STATE.PLACEHOLDER: + return this.emitError( + expression, + ErrorCode.MALFORMED_PLACEHOLDER, + "EOF reached while parsing placeholder", + ); } + + return this.emitError( + expression, + ErrorCode.INVALID_EXPRESSION, + "EOF reached while parsing expression", + ); }, return(child, expression) { @@ -340,7 +379,6 @@ function checkForOperators( const { pos, data } = parser; if (lookBehindForOperator(expression, data, pos) !== -1) { parser.consumeWhitespace(); - parser.forward = 0; return true; } @@ -363,7 +401,6 @@ function checkForOperators( const lookAheadPos = lookAheadForOperator(expression, data, nextNonSpace); if (lookAheadPos !== -1) { parser.pos = lookAheadPos; - parser.forward = 0; return true; } } @@ -478,9 +515,10 @@ function lookAheadForOperator( if (keywordPos === -1) continue; if (!isWhitespaceCode(data.charCodeAt(keywordPos + 1))) break; - // skip any whitespace after the operator + // skip any whitespace after the operator; + // there must be an operand before the end of the input. const nextPos = lookAheadWhile(isWhitespaceCode, data, keywordPos + 2); - if (nextPos === data.length - 1) break; + if (nextPos === data.length) break; // finally check that this is not followed by a terminator. switch (data.charCodeAt(nextPos)) { @@ -552,7 +590,7 @@ function lookAheadWhile( if (!match(data.charCodeAt(i))) return i; } - return max - 1; + return max; } function lookBehindWhile( diff --git a/src/states/HTML_COMMENT.ts b/src/states/HTML_COMMENT.ts index 6edada38..28f38c16 100644 --- a/src/states/HTML_COMMENT.ts +++ b/src/states/HTML_COMMENT.ts @@ -1,4 +1,4 @@ -import { CODE, ErrorCode, type StateDefinition } from "../internal"; +import { ErrorCode, type StateDefinition } from "../internal"; // We enter STATE.HTML_COMMENT after we encounter a "<--" // while in the STATE.HTML_CONTENT. @@ -27,28 +27,22 @@ export const HTML_COMMENT: StateDefinition = { }); }, - char(code) { - if (code === CODE.HYPHEN) { - let offset = 1; - let next: number; - while ((next = this.lookAtCharCodeAhead(offset++)) === CODE.HYPHEN); - this.pos += offset; // skip all - - - if (next === CODE.CLOSE_ANGLE_BRACKET) { - this.exitState(); - } + parse(data, maxPos, comment) { + // The comment ends at the first "-" directly followed by ">", which also + // matches the final hyphens of "-->", "--->", etc. + const idx = data.indexOf("->", this.pos); + if (idx === -1) { + return this.emitError( + comment, + ErrorCode.MALFORMED_COMMENT, + "EOF reached while parsing comment", + ); } - }, - - eol() {}, - eof(comment) { - this.emitError( - comment, - ErrorCode.MALFORMED_COMMENT, - "EOF reached while parsing comment", - ); + this.pos = idx + 2; // skip -> + this.exitState(); }, + /* c8 ignore next -- never has child states */ return() {}, }; diff --git a/src/states/HTML_CONTENT.ts b/src/states/HTML_CONTENT.ts index a2b67efe..12339973 100644 --- a/src/states/HTML_CONTENT.ts +++ b/src/states/HTML_CONTENT.ts @@ -34,107 +34,147 @@ export const HTML_CONTENT: StateDefinition = { exit() {}, - char(code) { - if (code === CODE.OPEN_ANGLE_BRACKET) { - if (STATE.checkForCDATA(this)) return; + parse(data, maxPos, content) { + if (this.pos === maxPos) { + htmlEOF.call(this); + this.pos++; + return; + } + + while (this.pos < maxPos) { + const code = data.charCodeAt(this.pos); + + if (code === CODE.NEWLINE || code === CODE.CARRIAGE_RETURN) { + const len = + code === CODE.CARRIAGE_RETURN && + data.charCodeAt(this.pos + 1) === CODE.NEWLINE + ? 2 + : 1; + + if (this.beginMixedMode) { + this.beginMixedMode = false; + this.endText(); + this.exitState(); + return; // parent handles newline at same pos + } - const nextCode = this.lookAtCharCodeAhead(1); + if (this.endingMixedModeAtEOL) { + this.endingMixedModeAtEOL = false; + this.endText(); + this.exitState(); + return; // parent handles newline at same pos + } + + const prevState = this.activeState; + const prevPos = this.pos; + if (STATE.handleDelimitedEOL(this, len, content)) { + if (this.activeState !== prevState) return; + // Still in this delimited block; skip the newline if it wasn't consumed (eg a blank line). + if (this.pos === prevPos) this.pos += len; + continue; + } - if (nextCode === CODE.EXCLAMATION) { - if ( - this.lookAtCharCodeAhead(2) === CODE.HYPHEN && - this.lookAtCharCodeAhead(3) === CODE.HYPHEN + this.startText(); + this.pos += len; + continue; + } + + if (code === CODE.OPEN_ANGLE_BRACKET) { + if (STATE.checkForCDATA(this)) return; + + const nextCode = data.charCodeAt(this.pos + 1); + + if (nextCode === CODE.EXCLAMATION) { + if ( + data.charCodeAt(this.pos + 2) === CODE.HYPHEN && + data.charCodeAt(this.pos + 3) === CODE.HYPHEN + ) { + this.enterState(STATE.HTML_COMMENT); + this.pos += 4; // skip