diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..bfc06f0 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,46 @@ +name: Test Suite + +on: + push: + branches: + - develop + pull_request_target: + +jobs: + test_package: + name: Test + runs-on: ubuntu-latest + strategy: + matrix: + node_version: [12, 18, 20] + fail-fast: false + + steps: + - uses: actions/checkout@v4 + name: Checkout source + + - name: Run Rust tests + run: | + cargo install grcov + cargo clean + cargo test + grcov . --binary-path ./target/debug -s . -t lcov --branch -o ./coverage.lcov + env: + RUSTFLAGS: -Cinstrument-coverage + LLVM_PROFILE_FILE: "%p-%m.profraw" + + - name: Publish coverage report + uses: codecov/codecov-action@v3 + with: + file: ./coverage.lcov + + - uses: actions/setup-node@v3 + name: Setup Node.JS + with: + node-version: ${{ matrix.node_version }} + + - name: Run WASM tests + run: | + cargo install wasm-pack + cargo clean + npm test \ No newline at end of file diff --git a/.gitignore b/.gitignore index b7e7d95..b1cdaf5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target +/simd-target /Cargo.lock /node_modules \ No newline at end of file diff --git a/.mocha.setup.js b/.mocha.setup.js index 004578b..231688f 100644 --- a/.mocha.setup.js +++ b/.mocha.setup.js @@ -1,3 +1,5 @@ // Inject jest's assertion (expect) into global scope for the Mocha // to use same assertion between node-swc & rest. -global.expect = require("expect"); +require('@jymfony/util'); +global.expect = require('expect'); +global.__jymfony.JObject = class {}; diff --git a/.mocharc.js b/.mocharc.js index 745fd02..e76bc20 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -8,5 +8,5 @@ * This config is for the mocha test runner invoked by cargo to resolve its global setup file. */ module.exports = { - require: require.resolve("./.mocha.setup.js"), + require: require.resolve('./.mocha.setup.js'), }; diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..8921e26 --- /dev/null +++ b/.npmignore @@ -0,0 +1,14 @@ +/node_modules/ +/pkg/.gitignore +/pkg/package.json +/simd/.gitignore +/simd/package.json +/src/ +/simd-target/ +/target/ +/tests/ +/.gitignore +/.mocha* +/.yarnrc.yml +/Cargo.* +/yarn.lock diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..804d1b6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +**/*.rs +node_modules/ +pkg/ +simd/ +simd-target/ +target/ +Cargo.lock +Cargo.toml +package.json +package-lock.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..dcb73d9 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": true, + "tabWidth": 4, + "singleQuote": true +} diff --git a/Cargo.toml b/Cargo.toml index d18328e..911e242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,24 @@ [package] name = "jymfony-compiler" +description = "Jymfony compiler" +repository = "https://github.com/jymfony/compiler.git" +license = "MIT" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib", "rlib"] +[features] +simd = [] + [dependencies] anyhow = { version = "1", features = ["backtrace"] } base64 = "0.21.4" getrandom = { version = "0.2.10", features = ["js"] } js-sys = "0.3" lazy_static = "1.4.0" +moka = { version = "0.12.1", features = ["js", "sync"] } rand = "0.8.5" rustc-hash = "1.1.0" serde = { version = "1.0", features = ["derive"] } @@ -19,17 +26,19 @@ serde-wasm-bindgen = "0.6" sourcemap = "6.4.1" swc_atoms = "0.6.0" swc_common = { version = "0.33.0", features = ["anyhow", "sourcemap"] } -swc_ecma_ast = "0.110.0" +swc_ecma_ast = { version = "0.110.0", features = ["default", "serde"] } swc_ecma_codegen = "0.146.1" swc_ecma_parser = "0.141.1" swc_ecma_transforms_base = "0.134.3" swc_ecma_transforms_compat = "0.160.4" swc_ecma_transforms_module = "0.177.4" swc_ecma_transforms_proposal = "0.168.6" -swc_ecma_transforms_typescript = "0.184.6" +swc_ecma_transforms_typescript = "0.185.4" swc_ecma_visit = "0.96.0" swc_ecma_utils = "0.124.3" url = "2.4" +uuid = { version = "1.5.0", features = ["v4"] } +uuid-simd = "0.8.0" wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } wasm-bindgen-derive = "0.2" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d881ad9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2023 Alessandro Chitolina + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..02566a4 --- /dev/null +++ b/index.d.ts @@ -0,0 +1 @@ +export * from './pkg/compiler'; diff --git a/index.js b/index.js new file mode 100644 index 0000000..28515aa --- /dev/null +++ b/index.js @@ -0,0 +1,19 @@ +/* From wasm-feature-detect (https://www.npmjs.com/package/wasm-feature-detect) */ +const isSimdSupported = WebAssembly.validate( + new Uint8Array([ + 0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 10, + 1, 8, 0, 65, 0, 253, 15, 253, 98, 11, + ]), +); +const { compile, prepareStackTrace, start } = isSimdSupported + ? require('./simd/compiler') + : require('./pkg/compiler'); + +exports._isSimdSupported = isSimdSupported; +exports.compile = compile; +exports.prepareStackTrace = prepareStackTrace; +exports.start = start; +exports.getReflectionData = require('./lib/reflection').getReflectionData; + +global._apply_decs_2203_r = require('./lib/_apply_decs_2203_r')._; +global.__jymfony_reflect = require('./lib/reflection')._; diff --git a/lib/_apply_decs_2203_r.js b/lib/_apply_decs_2203_r.js index 9ae0adc..a158e38 100644 --- a/lib/_apply_decs_2203_r.js +++ b/lib/_apply_decs_2203_r.js @@ -22,8 +22,8 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { function createAddInitializerMethod(initializers, decoratorFinishedRef) { return function addInitializer(initializer) { - assertNotFinished(decoratorFinishedRef, "addInitializer"); - assertCallable(initializer, "An initializer"); + assertNotFinished(decoratorFinishedRef, 'addInitializer'); + assertCallable(initializer, 'An initializer'); initializers.push(initializer); }; } @@ -37,30 +37,30 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { isStatic, isPrivate, metadata, - value + value, ) { var kindStr; switch (kind) { case 1 /* ACCESSOR */: - kindStr = "accessor"; + kindStr = 'accessor'; break; case 2 /* METHOD */: - kindStr = "method"; + kindStr = 'method'; break; case 3 /* GETTER */: - kindStr = "getter"; + kindStr = 'getter'; break; case 4 /* SETTER */: - kindStr = "setter"; + kindStr = 'setter'; break; default: - kindStr = "field"; + kindStr = 'field'; } var ctx = { kind: kindStr, - name: isPrivate ? "#" + name : name, + name: isPrivate ? '#' + name : name, static: isStatic, private: isPrivate, metadata: metadata, @@ -71,7 +71,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { if (kind !== 0 /* FIELD */) { ctx.addInitializer = createAddInitializerMethod( initializers, - decoratorFinishedRef + decoratorFinishedRef, ); } @@ -107,7 +107,11 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { } } ctx.access = - get && set ? { get: get, set: set } : get ? { get: get } : { set: set }; + get && set + ? { get: get, set: set } + : get + ? { get: get } + : { set: set }; try { return dec(value, ctx); @@ -119,14 +123,16 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { function assertNotFinished(decoratorFinishedRef, fnName) { if (decoratorFinishedRef.v) { throw new Error( - "attempted to call " + fnName + " after decoration was finished" + 'attempted to call ' + + fnName + + ' after decoration was finished', ); } } function assertCallable(fn, hint) { - if (typeof fn !== "function") { - throw new TypeError(hint + " must be a function"); + if (typeof fn !== 'function') { + throw new TypeError(hint + ' must be a function'); } } @@ -134,31 +140,31 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { var type = typeof value; if (kind === 1 /* ACCESSOR */) { - if (type !== "object" || value === null) { + if (type !== 'object' || value === null) { throw new TypeError( - "accessor decorators must return an object with get, set, or init properties or void 0" + 'accessor decorators must return an object with get, set, or init properties or void 0', ); } if (value.get !== undefined) { - assertCallable(value.get, "accessor.get"); + assertCallable(value.get, 'accessor.get'); } if (value.set !== undefined) { - assertCallable(value.set, "accessor.set"); + assertCallable(value.set, 'accessor.set'); } if (value.init !== undefined) { - assertCallable(value.init, "accessor.init"); + assertCallable(value.init, 'accessor.init'); } - } else if (type !== "function") { + } else if (type !== 'function') { var hint; if (kind === 0 /* FIELD */) { - hint = "field"; + hint = 'field'; } else if (kind === 20 /* CLASS */) { - hint = "class"; + hint = 'class'; } else { - hint = "method"; + hint = 'method'; } throw new TypeError( - hint + " decorators must return a function or void 0" + hint + ' decorators must return a function or void 0', ); } } @@ -172,7 +178,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { isStatic, isPrivate, initializers, - metadata + metadata, ) { var decs = decInfo[0]; @@ -216,7 +222,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { var newValue, get, set; - if (typeof decs === "function") { + if (typeof decs === 'function') { newValue = memberDec( decs, name, @@ -226,7 +232,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { isStatic, isPrivate, metadata, - value + value, ); if (newValue !== void 0) { @@ -257,7 +263,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { isStatic, isPrivate, metadata, - value + value, ); if (newValue !== void 0) { @@ -279,7 +285,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { if (newInit !== void 0) { if (init === void 0) { init = newInit; - } else if (typeof init === "function") { + } else if (typeof init === 'function') { init = [init, newInit]; } else { init.push(newInit); @@ -295,7 +301,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { init = function (instance, init) { return init; }; - } else if (typeof init !== "function") { + } else if (typeof init !== 'function') { var ownInitializers = init; init = function (instance, init) { @@ -362,17 +368,17 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { methodIsPrivate, methodIsStatic, metadata, - value + value, ) { - var funcKindStr = "method"; + var funcKindStr = 'method'; if (methodKind === 4 /* SETTER */) { - funcKindStr = "setter"; + funcKindStr = 'setter'; } else if (methodKind === 20 /* CLASS */) { - funcKindStr = "class"; + funcKindStr = 'class'; } var ctx = { - kind: "parameter", + kind: 'parameter', name: name, index: index, rest: rest > 0, @@ -386,7 +392,10 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { }; var decoratorFinishedRef = { v: false }; - ctx.addInitializer = createAddInitializerMethod(initializers, decoratorFinishedRef); + ctx.addInitializer = createAddInitializerMethod( + initializers, + decoratorFinishedRef, + ); try { return dec(value, ctx); @@ -405,19 +414,19 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { funcInfo, initializers, metadata, - value + value, ) { var decs = decInfo[0]; var init; var funcKind = funcInfo[0]; - var isPrivate = funcInfo[2]; + var isPrivate = !!funcInfo[2]; var isStatic = funcKind !== 20 && funcKind >= 6; if (isStatic) funcKind -= 6; var newValue; - if (typeof decs === "function") { + if (typeof decs === 'function') { newValue = parameterDec( decs, name, @@ -429,7 +438,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { isPrivate, isStatic, metadata, - value + value, ); if (newValue !== void 0) { @@ -451,14 +460,14 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { isPrivate, isStatic, metadata, - value + value, ); if (newValue !== void 0) { assertValidReturnValue(5, newValue); if (init === void 0) { init = newValue; - } else if (typeof init === "function") { + } else if (typeof init === 'function') { init = [init, newValue]; } else { init.push(newValue); @@ -471,7 +480,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { init = function (instance, init) { return init; }; - } else if (typeof init !== "function") { + } else if (typeof init !== 'function') { var ownInitializers = init; init = function (instance, init) { @@ -559,7 +568,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { isRest, funcInfo, initializers, - metadata + metadata, ); } else { if (kind !== 0 /* FIELD */ && !isPrivate) { @@ -571,12 +580,14 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { if ( existingKind === true || - (existingKind === 3 /* GETTER */ && kind !== 4) /* SETTER */ || - (existingKind === 4 /* SETTER */ && kind !== 3) /* GETTER */ + (existingKind === 3 /* GETTER */ && + kind !== 4) /* SETTER */ || + (existingKind === 4 /* SETTER */ && + kind !== 3) /* GETTER */ ) { throw new Error( - "Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + - name + 'Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: ' + + name, ); } else if (!existingKind && kind > 2 /* METHOD */) { existingNonFields.set(name, kind); @@ -594,7 +605,7 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { isStatic, isPrivate, initializers, - metadata + metadata, ); } } @@ -626,11 +637,11 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { try { var nextNewClass = classDecs[i](newClass, { - kind: "class", + kind: 'class', name: name, addInitializer: createAddInitializerMethod( initializers, - decoratorFinishedRef + decoratorFinishedRef, ), metadata, }); @@ -660,8 +671,8 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { function defineMetadata(Class, metadata) { return Object.defineProperty( Class, - Symbol.metadata || Symbol.for("Symbol.metadata"), - { configurable: false, enumerable: false, value: metadata } + Symbol.metadata || Symbol.for('Symbol.metadata'), + { configurable: false, enumerable: false, value: metadata }, ); } @@ -811,13 +822,18 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { initializeClass(Class); */ - _apply_decs_2203_r = function(targetClass, memberDecs, classDecs, parentClass) { + _apply_decs_2203_r = function ( + targetClass, + memberDecs, + classDecs, + parentClass, + ) { if (parentClass !== void 0) { var parentMetadata = - parentClass[Symbol.metadata || Symbol.for("Symbol.metadata")]; + parentClass[Symbol.metadata || Symbol.for('Symbol.metadata')]; } var metadata = Object.create( - parentMetadata === void 0 ? null : parentMetadata + parentMetadata === void 0 ? null : parentMetadata, ); var e = applyMemberDecs(targetClass, memberDecs, metadata); if (!classDecs.length) defineMetadata(targetClass, metadata); @@ -833,4 +849,4 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { return _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass); } -exports._ = exports._apply_decs_2203_r = _apply_decs_2203_r; \ No newline at end of file +exports._ = exports._apply_decs_2203_r = _apply_decs_2203_r; diff --git a/lib/reflection.js b/lib/reflection.js new file mode 100644 index 0000000..8ce5349 --- /dev/null +++ b/lib/reflection.js @@ -0,0 +1,102 @@ +const { getInternalReflectionData } = require('..')._isSimdSupported + ? require('../simd/compiler') + : require('../pkg/compiler'); + +const reflectionDataMap = new Map(); +const reflectionSymbol = Symbol('jymfony.reflect'); + +function docblockGetter(classId, memberIndex) { + const data = getInternalReflectionData(classId); + if (data === void 0) { + return null; + } + + const member = data.members[memberIndex]; + + return member.docblock || null; +} + +/** + * @param {string} classId + * @param {number|undefined} memberIndex + */ +exports._ = function __jymfony_reflect(classId, memberIndex = undefined) { + return (value, context) => { + const c = reflectionDataMap.get(classId) || { members: [] }; + if (context.kind === 'class') { + context.metadata[reflectionSymbol] = classId; + return; + } + + if (!context.name) { + return; + } + + const data = getInternalReflectionData(classId); + if (data === void 0) { + return; + } + + if (context.kind === 'method') { + const getter = (() => { + if (context.access) { + return context.access.get; + } + + return () => value; + })(); + + c.members.push({ + memberIndex, + kind: context.kind, + name: context.name, + static: context.static, + private: context.private, + access: { get: getter }, + get parameters() { + const data = getInternalReflectionData(classId); + if (data === void 0) { + return []; + } + + const member = data.members[memberIndex]; + + return member.params.map((p) => ({ ...p })); + }, + get docblock() { + return docblockGetter(classId, memberIndex); + }, + }); + } + + if (context.kind === 'field' || context.kind === 'accessor') { + c.members.push({ + memberIndex, + kind: context.kind, + name: context.name, + static: context.static, + private: context.private, + access: context.access, + get docblock() { + return docblockGetter(classId, memberIndex); + }, + }); + } + + reflectionDataMap.set(classId, c); + }; +}; + +exports.getReflectionData = function getReflectionData(classIdOrValue) { + if (classIdOrValue === void 0 || classIdOrValue === null) { + return undefined; + } + + const metadata = + classIdOrValue[Symbol.metadata || Symbol.for('Symbol.metadata')]; + if (metadata !== void 0 && metadata[reflectionSymbol] !== void 0) { + classIdOrValue = metadata[reflectionSymbol]; + } + + return reflectionDataMap.get(classIdOrValue); +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..cadcb97 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1387 @@ +{ + "name": "@jymfony/compiler", + "version": "0.5.0-alpha.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@jymfony/compiler", + "version": "0.5.0-alpha.1", + "license": "MIT", + "dependencies": { + "wasm-feature-detect": "^1.5.1" + }, + "devDependencies": { + "@jymfony/util": "^0.1.0-alpha.31", + "expect": "^27.4.2", + "mocha": "^10.2.0", + "prettier": "^3.0.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@jymfony/exceptions": { + "version": "0.1.0-alpha.31", + "resolved": "https://registry.npmjs.org/@jymfony/exceptions/-/exceptions-0.1.0-alpha.31.tgz", + "integrity": "sha512-HljHvREmPafAZhmrpqBaEgIZWfxCMOJTMoyqDBc+CBV2Huu/yhnBVwYeAQacPlJgTx6V1ebDuMFgt4ZrxxWing==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@jymfony/util": { + "version": "0.1.0-alpha.31", + "resolved": "https://registry.npmjs.org/@jymfony/util/-/util-0.1.0-alpha.31.tgz", + "integrity": "sha512-GPGh2mpGYoLHc+VBEyg+cVf6nuPAs1UrLIa+VSlfHI/AiWTMNWeiMUypDUlptjyur3a0nhATiAOTJvGg7c+lgg==", + "dev": true, + "dependencies": { + "@jymfony/exceptions": "0.1.0-alpha.31" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.2.tgz", + "integrity": "sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.3.tgz", + "integrity": "sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "20.8.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", + "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.2.tgz", + "integrity": "sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "16.0.7", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.7.tgz", + "integrity": "sha512-lQcYmxWuOfJq4IncK88/nwud9rwr1F04CFc5xzk0k4oKVyz/AI35TfsXmhjf6t8zp8mpCOi17BfvuNWx+zrYkg==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz", + "integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "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 + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dev": true, + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.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", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "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 + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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" + } + ] + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/wasm-feature-detect": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.5.1.tgz", + "integrity": "sha512-GHr23qmuehNXHY4902/hJ6EV5sUANIJC3R/yMfQ7hWDg3nfhlcJfnIL96R2ohpIwa62araN6aN4bLzzzq5GXkg==" + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "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, + "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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index a84d1b3..efb9400 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,24 @@ { "name": "@jymfony/compiler", + "version": "0.5.0-alpha.1", + "type": "commonjs", + "author": "Alessandro Chitolina ", + "license": "MIT", + "scripts": { + "build-release": "wasm-pack build --out-name compiler --target nodejs --release && RUSTFLAGS=\"-C target-feature=+simd128\" wasm-pack build --out-dir simd --out-name compiler --target nodejs --release --features=simd --target-dir=simd-target", + "build": "wasm-pack build --out-name compiler --target nodejs --dev && RUSTFLAGS=\"-C target-feature=+simd128\" wasm-pack build --out-dir simd --out-name compiler --target nodejs --dev --features=simd --target-dir=simd-target", + "pretest": "npm run build", + "test": "mocha tests/wasm/" + }, + "main": "index.js", + "types": "index.d.ts", "devDependencies": { - "expect": "^27.4.2" + "@jymfony/util": "^0.1.0-alpha.31", + "expect": "^27.4.2", + "mocha": "^10.2.0", + "prettier": "^3.0.3" + }, + "dependencies": { + "wasm-feature-detect": "^1.5.1" } -} \ No newline at end of file +} diff --git a/src/err.rs b/src/err.rs index 6bb5553..85be9c2 100644 --- a/src/err.rs +++ b/src/err.rs @@ -63,6 +63,6 @@ impl SyntaxError { column ); - Self::new(message.as_str()).into() + Self::new(message.as_str()) } } diff --git a/src/lib.rs b/src/lib.rs index 00a6fe7..6cf409c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,9 +3,30 @@ mod parser; mod stack; mod wasm; - +mod reflection; #[cfg(test)] pub mod testing; pub(crate) use err::SyntaxError; pub(crate) use stack::*; +use uuid::Uuid; +#[cfg(feature = "simd")] +use uuid_simd::UuidExt; + +pub(crate) fn generate_uuid() -> Uuid { + #[cfg(test)] + return testing::uuid::generate_test_uuid(); + + #[cfg(not(test))] + return Uuid::new_v4(); +} + +pub(crate) fn parse_uuid(text: &str) -> anyhow::Result { + #[cfg(not(feature = "simd"))] + let parsed = Uuid::parse_str(text)?; + + #[cfg(feature = "simd")] + let parsed = Uuid::parse(text.as_bytes())?; + + Ok(parsed) +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 25992ef..083436d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,6 +1,9 @@ use crate::SyntaxError; +use anyhow::Result; +pub use program::CompileOptions; use program::Program; use std::path::PathBuf; +use std::rc::Rc; use swc_common::comments::SingleThreadedComments; use swc_common::input::StringInput; use swc_common::sync::Lrc; @@ -16,7 +19,7 @@ mod util; pub const ES_VERSION: EsVersion = EsVersion::EsNext; -pub fn parse(code: String, filename: Option<&str>) -> Result> { +pub fn parse(code: String, filename: Option<&str>) -> Result { let source_map: Lrc = Default::default(); let source_file = source_map.new_source_file( filename @@ -72,7 +75,7 @@ pub fn parse(code: String, filename: Option<&str>) -> Result) -> Result) -> Box { + fn create_pass() -> Box { let unresolved_mark = Mark::new(); let top_level_mark = Mark::new(); let static_block_mark = Mark::new(); @@ -132,10 +134,148 @@ mod tests { resolver(unresolved_mark, top_level_mark, false), decorator_2022_03(), static_blocks(static_block_mark), - class_properties(Some(comments), Default::default()), )) } + #[test] + pub fn test_resolve_self_identifiers() { + reset_test_uuid(); + + let code = r#" +const p = class { + t() { + return __self.x; + } +}; + +export class x { + m() { + return __self.y; + } +} + +export class y { + f() { + return new class { + c() { + return __self; + } + } + } +} +"#; + + let result = parse(code.into(), None).expect("failed to parse"); + let compiled = result + .compile(Default::default()) + .expect("failed to compile"); + + assert_eq!( + compiled, + r#""use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +function _export(target, all) { + for(var name in all)Object.defineProperty(target, name, { + enumerable: true, + get: all[name] + }); +} +_export(exports, { + x: function() { + return _x; + }, + y: function() { + return _y; + } +}); +var _initClass, __anonymous_xΞ1, _dec, __jymfony_JObject, _dec1, _initProto, _dec2, _initClass1, __jymfony_JObject1, _dec3, _initProto1, _dec4, _initClass2, __jymfony_JObject2, _dec5, _initProto2; +_dec = __jymfony_reflect("00000000-0000-0000-0000-000000000000"), _dec1 = __jymfony_reflect("00000000-0000-0000-0000-000000000000", 0); +const p = (class _anonymous_xΞ1 extends (__jymfony_JObject = __jymfony.JObject) { + static #_ = { e: [_initProto], c: [__anonymous_xΞ1, _initClass] } = _apply_decs_2203_r(this, [ + [ + _dec1, + 2, + "t" + ] + ], [ + _dec + ], __jymfony_JObject); + constructor(...args){ + super(...args); + _initProto(this); + } + t() { + return __anonymous_xΞ1.x; + } + static #_2 = _initClass(); +}, __anonymous_xΞ1); +let _x; +_dec2 = __jymfony_reflect("00000000-0000-0000-0000-000000000001"), _dec3 = __jymfony_reflect("00000000-0000-0000-0000-000000000001", 0); +class x extends (__jymfony_JObject1 = __jymfony.JObject) { + static #_ = { e: [_initProto1], c: [_x, _initClass1] } = _apply_decs_2203_r(this, [ + [ + _dec3, + 2, + "m" + ] + ], [ + _dec2 + ], __jymfony_JObject1); + constructor(...args){ + super(...args); + _initProto1(this); + } + m() { + return _x.y; + } + static #_2 = _initClass1(); +} +let _y; +_dec4 = __jymfony_reflect("00000000-0000-0000-0000-000000000002"), _dec5 = __jymfony_reflect("00000000-0000-0000-0000-000000000002", 0); +class y extends (__jymfony_JObject2 = __jymfony.JObject) { + static #_ = { e: [_initProto2], c: [_y, _initClass2] } = _apply_decs_2203_r(this, [ + [ + _dec5, + 2, + "f" + ] + ], [ + _dec4 + ], __jymfony_JObject2); + constructor(...args){ + super(...args); + _initProto2(this); + } + f() { + var _initClass, __anonymous_xΞ2, _dec, __jymfony_JObject, _dec1, _initProto; + _dec = __jymfony_reflect("00000000-0000-0000-0000-000000000003"), _dec1 = __jymfony_reflect("00000000-0000-0000-0000-000000000003", 0); + return new (class _anonymous_xΞ2 extends (__jymfony_JObject = __jymfony.JObject) { + static #_ = { e: [_initProto], c: [__anonymous_xΞ2, _initClass] } = _apply_decs_2203_r(this, [ + [ + _dec1, + 2, + "c" + ] + ], [ + _dec + ], __jymfony_JObject); + constructor(...args){ + super(...args); + _initProto(this); + } + c() { + return __anonymous_xΞ2; + } + static #_2 = _initClass(); + }, __anonymous_xΞ2); + } + static #_2 = _initClass2(); +} +"# + ); + } #[test] pub fn parse_error() { @@ -237,6 +377,8 @@ export default @logger.logged class x { .as_module_decl() .is_some_and(|s| s.is_export_default_decl()))); - let _ = parsed.compile().expect("Should compile with no error"); + let _ = parsed + .compile(Default::default()) + .expect("Should compile with no error"); } } diff --git a/src/parser/program.rs b/src/parser/program.rs index 25677c1..d32cb0f 100644 --- a/src/parser/program.rs +++ b/src/parser/program.rs @@ -1,33 +1,39 @@ use crate::parser::transformers::{ anonymous_expr, class_jobject, class_reflection_decorators, decorator_2022_03, + remove_assert_calls, resolve_self_identifiers, static_blocks, }; use crate::stack::register_source_map; +use base64::prelude::BASE64_STANDARD; +use base64::Engine; use sourcemap::SourceMap; use std::fmt::{Debug, Formatter}; -use std::sync::Arc; +use std::rc::Rc; use swc_common::comments::SingleThreadedComments; +use swc_common::sync::Lrc; use swc_common::{chain, BytePos, LineCol, Mark, GLOBALS}; use swc_ecma_codegen::text_writer::JsWriter; use swc_ecma_codegen::Emitter; use swc_ecma_transforms_base::feature::FeatureFlag; +use swc_ecma_transforms_base::fixer::fixer; use swc_ecma_transforms_base::hygiene::hygiene; use swc_ecma_transforms_base::resolver; -use swc_ecma_transforms_compat::es2022::static_blocks; +use swc_ecma_transforms_compat::es2020::{nullish_coalescing, optional_chaining}; use swc_ecma_transforms_module::common_js; use swc_ecma_transforms_typescript::strip; -use swc_ecma_visit::FoldWith; +use swc_ecma_visit::{Fold, FoldWith}; -#[derive(Debug)] +#[derive(Default)] pub struct CompileOptions { - debug: bool, + pub debug: bool, + pub namespace: Option, } pub struct Program { - pub(crate) source_map: Arc, + pub(crate) source_map: Lrc, pub(crate) orig_srcmap: Option, pub(crate) filename: Option, pub(crate) program: swc_ecma_ast::Program, - pub(crate) comments: SingleThreadedComments, + pub(crate) comments: Rc, pub(crate) is_typescript: bool, } @@ -50,29 +56,36 @@ impl Program { let static_block_mark = Mark::new(); let available_set = FeatureFlag::all(); - let mut transformers = chain!( + let mut transformers: Box = Box::new(chain!( resolver(unresolved_mark, top_level_mark, self.is_typescript), + class_reflection_decorators( + self.filename.as_deref(), + opts.namespace.as_deref(), + self.comments.clone() + ), strip(top_level_mark), + nullish_coalescing(Default::default()), + optional_chaining(Default::default(), unresolved_mark), anonymous_expr(), - class_reflection_decorators(), + resolve_self_identifiers(unresolved_mark), class_jobject(), decorator_2022_03(), static_blocks(static_block_mark), - hygiene(), common_js( unresolved_mark, Default::default(), available_set, Some(&self.comments) ), - ); + hygiene(), + fixer(Some(&self.comments)) + )); if !opts.debug { - // TODO - // transformers = chain!(transformers, remove_assert_calls()); + transformers = Box::new(chain!(transformers, remove_assert_calls())); } - let program = self.program.fold_with(&mut transformers); + let program = self.program.fold_with(transformers.as_mut()); let mut buf = vec![]; let mut sm: Vec<(BytePos, LineCol)> = vec![]; @@ -87,14 +100,23 @@ impl Program { emitter.emit_program(&program)? }; - if let Some(f) = self.filename { + let mut src = String::from_utf8(buf).expect("non-utf8?"); + if let Some(f) = self.filename.as_deref() { let srcmap = self .source_map .build_source_map_from(&sm, self.orig_srcmap.as_ref()); - register_source_map(f, srcmap); + + register_source_map(f.to_string(), srcmap.clone()); + + let mut buf = vec![]; + srcmap.to_writer(&mut buf).ok(); + + let res = BASE64_STANDARD.encode(buf); + src += "\n\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,"; + src += &res; } - Ok(String::from_utf8(buf).expect("non-utf8?")) + Ok(src) }) } } diff --git a/src/parser/transformers/anonymous_expr.rs b/src/parser/transformers/anonymous_expr.rs index 121be60..d6366bd 100644 --- a/src/parser/transformers/anonymous_expr.rs +++ b/src/parser/transformers/anonymous_expr.rs @@ -1,38 +1,51 @@ -use rand::Rng; +#[cfg(not(test))] +use rand::prelude::*; use swc_ecma_ast::*; use swc_ecma_utils::private_ident; -use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut}; +use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; pub fn anonymous_expr() -> impl VisitMut + Fold { as_folder(AnonymousExpr::default()) } #[derive(Default)] -struct AnonymousExpr {} +struct AnonymousExpr { + #[cfg(test)] + test_current_id: i32, +} + +impl AnonymousExpr { + fn gen_anonymous_ident(&mut self) -> Ident { + #[cfg(not(test))] + let rnd = thread_rng().gen_range(0..1000000); + #[cfg(test)] + let rnd = { + self.test_current_id += 1; + self.test_current_id + }; -fn gen_anonymous_ident() -> Ident { - let rnd = rand::thread_rng().gen_range(0..1000000); - let ident = format!("_anonymous_xΞ{:X}", rnd); + let ident = format!("_anonymous_xΞ{:X}", rnd); - private_ident!(ident) + private_ident!(ident) + } } impl VisitMut for AnonymousExpr { noop_visit_mut_type!(); - fn visit_mut_expr(&mut self, e: &mut Expr) { - match e { - Expr::Class(c) => { - if c.ident.is_none() { - c.ident = Some(gen_anonymous_ident()); - } - } - Expr::Fn(f) => { - if f.ident.is_none() { - f.ident = Some(gen_anonymous_ident()); - } - } - _ => {} + fn visit_mut_class_expr(&mut self, n: &mut ClassExpr) { + n.visit_mut_children_with(self); + + if n.ident.is_none() { + n.ident = Some(self.gen_anonymous_ident()); + } + } + + fn visit_mut_fn_expr(&mut self, n: &mut FnExpr) { + n.visit_mut_children_with(self); + + if n.ident.is_none() { + n.ident = Some(self.gen_anonymous_ident()); } } } diff --git a/src/parser/transformers/class_jobject.rs b/src/parser/transformers/class_jobject.rs index 813a228..a07143c 100644 --- a/src/parser/transformers/class_jobject.rs +++ b/src/parser/transformers/class_jobject.rs @@ -3,7 +3,7 @@ use lazy_static::lazy_static; use swc_common::util::take::Take; use swc_common::DUMMY_SP; use swc_ecma_ast::*; -use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut}; +use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; lazy_static! { static ref JOBJECT_ACCESSOR: MemberExpr = { @@ -39,6 +39,7 @@ impl VisitMut for ClassJObject { noop_visit_mut_type!(); fn visit_mut_class(&mut self, n: &mut Class) { + n.visit_mut_children_with(self); if n.super_class.is_some() { return; } @@ -132,9 +133,7 @@ impl VisitMut for ClassJObject { block_stmts.push(if_block); for p in initializers.drain(..) { - let ClassMember::ClassProp(mut p) = p else { - unreachable!() - }; + let mut p = p.expect_class_prop(); assert!(!p.is_static); let value = p.value.take(); diff --git a/src/parser/transformers/class_reflection_decorators.rs b/src/parser/transformers/class_reflection_decorators.rs index 4ba7c58..3de751b 100644 --- a/src/parser/transformers/class_reflection_decorators.rs +++ b/src/parser/transformers/class_reflection_decorators.rs @@ -1,63 +1,132 @@ +use crate::generate_uuid; use crate::parser::util::ident; -use swc_common::DUMMY_SP; +use crate::reflection::{register_class, ReflectionData}; +use std::collections::HashMap; +use std::rc::Rc; +use swc_common::comments::{CommentKind, Comments}; +use swc_common::{Span, Spanned, DUMMY_SP}; use swc_ecma_ast::*; -use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut}; +use swc_ecma_utils::ExprFactory; +use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; -pub fn class_reflection_decorators() -> impl VisitMut + Fold { - as_folder(ClassReflectionDecorators::default()) +pub fn class_reflection_decorators<'a, C: Comments + 'a>( + filename: Option<&'a str>, + namespace: Option<&'a str>, + comments: Rc, +) -> impl VisitMut + Fold + 'a { + as_folder(ClassReflectionDecorators { + filename, + namespace, + comments, + }) } -#[derive(Default)] -struct ClassReflectionDecorators {} - -fn visit_mut_class(n: &mut Class) { - let reflect_ident = Expr::Ident(ident("__jymfony_reflect")); - for member in n.body.iter_mut() { - match member { - ClassMember::Method(m) => { - m.function.decorators.push(Decorator { - span: DUMMY_SP, - expr: Box::new(reflect_ident.clone()), - }); - } - ClassMember::PrivateMethod(m) => { - m.function.decorators.push(Decorator { - span: DUMMY_SP, - expr: Box::new(reflect_ident.clone()), - }); - } - ClassMember::ClassProp(p) => { - p.decorators.push(Decorator { - span: DUMMY_SP, - expr: Box::new(reflect_ident.clone()), - }); - } - ClassMember::PrivateProp(p) => { - p.decorators.push(Decorator { - span: DUMMY_SP, - expr: Box::new(reflect_ident.clone()), - }); - } - ClassMember::AutoAccessor(a) => { - a.decorators.push(Decorator { - span: DUMMY_SP, - expr: Box::new(reflect_ident.clone()), - }); +struct ClassReflectionDecorators<'a, C: Comments> { + filename: Option<&'a str>, + namespace: Option<&'a str>, + comments: Rc, +} + +impl<'a, C: Comments> ClassReflectionDecorators<'a, C> { + fn get_element_docblock(&self, span: Span) -> Option { + self.comments + .get_leading(span.lo) + .iter() + .flatten() + .rev() + .find_map(|cmt| { + if cmt.kind == CommentKind::Block && cmt.text.starts_with("*") { + Some(format!("/*{}*/", cmt.text)) + } else { + None + } + }) + } + + fn process_class(&self, n: &mut Class, name: Ident) { + let id = generate_uuid(); + let mut docblock = HashMap::new(); + if n.span != DUMMY_SP { + docblock.insert(n.span, self.get_element_docblock(n.span)); + } + + n.decorators.push(Decorator { + span: DUMMY_SP, + expr: Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: ident("__jymfony_reflect").as_callee(), + args: vec![id.to_string().as_arg()], + type_args: None, + })), + }); + + for (idx, member) in n.body.iter_mut().enumerate() { + let reflect_ident = Expr::Call(CallExpr { + span: DUMMY_SP, + callee: ident("__jymfony_reflect").as_callee(), + args: vec![id.to_string().as_arg(), idx.as_arg()], + type_args: None, + }); + + let span = member.span(); + if span != DUMMY_SP { + docblock.insert(member.span(), self.get_element_docblock(member.span())); } - _ => { // Do nothing } + + match member { + ClassMember::Method(m) => { + m.function.decorators.push(Decorator { + span: DUMMY_SP, + expr: Box::new(reflect_ident.clone()), + }); + } + ClassMember::PrivateMethod(m) => { + m.function.decorators.push(Decorator { + span: DUMMY_SP, + expr: Box::new(reflect_ident.clone()), + }); + } + ClassMember::ClassProp(p) => { + p.decorators.push(Decorator { + span: DUMMY_SP, + expr: Box::new(reflect_ident.clone()), + }); + } + ClassMember::PrivateProp(p) => { + p.decorators.push(Decorator { + span: DUMMY_SP, + expr: Box::new(reflect_ident.clone()), + }); + } + ClassMember::AutoAccessor(a) => { + a.decorators.push(Decorator { + span: DUMMY_SP, + expr: Box::new(reflect_ident.clone()), + }); + } + _ => { + // Do nothing + } } } + + register_class( + &id, + ReflectionData::new(n, name, self.filename, self.namespace, docblock), + ); } } -impl VisitMut for ClassReflectionDecorators { +impl VisitMut for ClassReflectionDecorators<'_, C> { noop_visit_mut_type!(); fn visit_mut_class_decl(&mut self, n: &mut ClassDecl) { - visit_mut_class(&mut n.class) + self.process_class(&mut n.class, n.ident.clone()); + n.visit_mut_children_with(self); } fn visit_mut_class_expr(&mut self, n: &mut ClassExpr) { - visit_mut_class(&mut n.class) + self.process_class(&mut n.class, n.ident.clone().unwrap()); + n.visit_mut_children_with(self); } } diff --git a/src/parser/transformers/decorator_2022_03.rs b/src/parser/transformers/decorator_2022_03.rs index 43d3ef0..d24016d 100644 --- a/src/parser/transformers/decorator_2022_03.rs +++ b/src/parser/transformers/decorator_2022_03.rs @@ -7,8 +7,8 @@ use crate::parser::util::{ident, undefined}; use rustc_hash::FxHashMap; use swc_atoms::JsWord; use swc_common::{util::take::Take, Spanned, SyntaxContext, DUMMY_SP}; -use swc_ecma_ast::*; use swc_ecma_ast::Expr::Bin; +use swc_ecma_ast::*; use swc_ecma_utils::{ alias_ident_for, constructor::inject_after_super, default_constructor, prepend_stmt, private_ident, prop_name_to_expr_value, quote_ident, replace_ident, ExprFactory, IdentExt, @@ -40,6 +40,7 @@ struct Decorator202203 { state: ClassState, /// Prepended before the class + #[allow(clippy::vec_box)] pre_class_inits: Vec>, rename_map: FxHashMap, @@ -120,18 +121,14 @@ impl Decorator202203 { let name = if let Some(ident) = p.pat.as_ident() { Some(ident.id.clone()) } else if let Some(rest) = p.pat.as_rest().map(|r| &r.arg) { - if let Some(ident) = rest.as_ident() { - Some(ident.id.clone()) - } else { - None - } + rest.as_ident().map(|ident| ident.id.clone()) } else { None }; let assign_right = if let Pat::Assign(assign) = &p.pat { let r = assign.right.clone(); - let span = assign.span.clone(); + let span = assign.span; p.pat = assign.left.as_ref().clone(); Some((r, span)) } else { @@ -140,97 +137,81 @@ impl Decorator202203 { let param_ident = match &mut p.pat { Pat::Ident(i) => i.id.clone(), - Pat::Rest(r) => { - match r.arg.as_mut() { - Pat::Ident(i) => i.id.clone(), - Pat::Array(a) => { - let ident = private_ident!(format!("_rest_param{}", i)); - pre_stmts.push( - Stmt::Decl(Decl::Var(Box::new(VarDecl { - span: DUMMY_SP, - kind: VarDeclKind::Let, - declare: false, - decls: vec![ - VarDeclarator { - span: DUMMY_SP, - name: Pat::Array(a.clone()), - init: Some(Box::new(Expr::Ident(ident.clone()))), - definite: false, - } - ], - }))) - ); + Pat::Rest(r) => match r.arg.as_mut() { + Pat::Ident(i) => i.id.clone(), + Pat::Array(a) => { + let ident = private_ident!(format!("_rest_param{}", i)); + pre_stmts.push(Stmt::Decl(Decl::Var(Box::new(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Let, + declare: false, + decls: vec![VarDeclarator { + span: DUMMY_SP, + name: Pat::Array(a.clone()), + init: Some(Box::new(Expr::Ident(ident.clone()))), + definite: false, + }], + })))); - r.arg = Box::new(Pat::Ident(ident.clone().into())); - ident - } - Pat::Object(o) => { - let ident = private_ident!(format!("_rest_param{}", i)); - pre_stmts.push( - Stmt::Decl(Decl::Var(Box::new(VarDecl { - span: DUMMY_SP, - kind: VarDeclKind::Let, - declare: false, - decls: vec![ - VarDeclarator { - span: DUMMY_SP, - name: Pat::Object(o.clone()), - init: Some(Box::new(Expr::Ident(ident.clone()))), - definite: false, - } - ], - }))) - ); + r.arg = Box::new(Pat::Ident(ident.clone().into())); + ident + } + Pat::Object(o) => { + let ident = private_ident!(format!("_rest_param{}", i)); + pre_stmts.push(Stmt::Decl(Decl::Var(Box::new(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Let, + declare: false, + decls: vec![VarDeclarator { + span: DUMMY_SP, + name: Pat::Object(o.clone()), + init: Some(Box::new(Expr::Ident(ident.clone()))), + definite: false, + }], + })))); - r.arg = Box::new(Pat::Ident(ident.clone().into())); - ident - } - _ => unreachable!(), + r.arg = Box::new(Pat::Ident(ident.clone().into())); + ident } - } + _ => unreachable!("{}:{}", file!(), line!()), + }, Pat::Array(a) => { let ident = private_ident!(format!("_param{}", i)); - pre_stmts.push( - Stmt::Decl(Decl::Var(Box::new(VarDecl { + pre_stmts.push(Stmt::Decl(Decl::Var(Box::new(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Let, + declare: false, + decls: vec![VarDeclarator { span: DUMMY_SP, - kind: VarDeclKind::Let, - declare: false, - decls: vec![ - VarDeclarator { - span: DUMMY_SP, - name: Pat::Array(a.clone()), - init: Some(Box::new(Expr::Ident(ident.clone()))), - definite: false, - } - ], - }))) - ); + name: Pat::Array(a.clone()), + init: Some(Box::new(Expr::Ident(ident.clone()))), + definite: false, + }], + })))); p.pat = Pat::Ident(ident.clone().into()); ident } Pat::Object(o) => { let ident = private_ident!(format!("_param{}", i)); - pre_stmts.push( - Stmt::Decl(Decl::Var(Box::new(VarDecl { + pre_stmts.push(Stmt::Decl(Decl::Var(Box::new(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Let, + declare: false, + decls: vec![VarDeclarator { span: DUMMY_SP, - kind: VarDeclKind::Let, - declare: false, - decls: vec![ - VarDeclarator { - span: DUMMY_SP, - name: Pat::Object(o.clone()), - init: Some(Box::new(Expr::Ident(ident.clone()))), - definite: false, - } - ], - }))) - ); + name: Pat::Object(o.clone()), + init: Some(Box::new(Expr::Ident(ident.clone()))), + definite: false, + }], + })))); p.pat = Pat::Ident(ident.clone().into()); ident } - Pat::Assign(..) | Pat::Invalid(..) | Pat::Expr(..) => unreachable!(), + Pat::Assign(..) | Pat::Invalid(..) | Pat::Expr(..) => { + unreachable!("{}:{}", file!(), line!()) + } }; if let Some((right, span)) = assign_right { @@ -261,7 +242,8 @@ impl Decorator202203 { span: DUMMY_SP, obj: Box::new(Expr::Ident(init)), prop: MemberProp::Ident(ident("call")), - }).as_callee(), + }) + .as_callee(), args: vec![ if is_constructor && self.state.super_class.is_some() { undefined().as_arg() @@ -294,14 +276,14 @@ impl Decorator202203 { (match cm.kind { MethodKind::Setter => SETTER, MethodKind::Method => METHOD, - _ => unreachable!(), + _ => unreachable!("{}:{}", file!(), line!()), } + if cm.is_static { STATIC } else { 0 }) .as_arg(), ), Some(match &cm.key { PropName::Ident(i) => i.sym.clone().as_arg(), PropName::Computed(c) => c.expr.clone().as_arg(), - _ => unreachable!(), + _ => unreachable!("{}:{}", file!(), line!()), }), Some(0_usize.as_arg()), ] @@ -312,7 +294,7 @@ impl Decorator202203 { (match cm.kind { MethodKind::Setter => SETTER, MethodKind::Method => METHOD, - _ => unreachable!(), + _ => unreachable!("{}:{}", file!(), line!()), } + if cm.is_static { STATIC } else { 0 }) .as_arg(), ), @@ -320,7 +302,7 @@ impl Decorator202203 { Some(1_usize.as_arg()), ] } - _ => unreachable!(), + _ => unreachable!("{}:{}", file!(), line!()), }, } .as_arg(), @@ -332,7 +314,8 @@ impl Decorator202203 { elems: vec![ dec, Some(PARAM.as_arg()), - name.map(|n| n.sym.as_arg()).or_else(|| Some(undefined().as_arg())), + name.map(|n| n.sym.as_arg()) + .or_else(|| Some(undefined().as_arg())), Some(i.as_arg()), Some(if p.pat.is_rest() { 1_usize } else { 0_usize }.as_arg()), func, @@ -351,21 +334,21 @@ impl Decorator202203 { b.stmts.splice(0..0, pre_stmts.drain(..)); b.stmts.splice(0..0, init_calls.drain(..)); } - }, + } ClassMember::PrivateMethod(m) => { m.function.params = n; if let Some(b) = &mut m.function.body { b.stmts.splice(0..0, pre_stmts.drain(..)); b.stmts.splice(0..0, init_calls.drain(..)); } - }, + } ClassMember::Constructor(c) => { if let Some(b) = &mut c.body { b.stmts.splice(0..0, pre_stmts.drain(..)); b.stmts.splice(0..0, init_calls.drain(..)); } } - _ => unreachable!(), + _ => unreachable!("{}:{}", file!(), line!()), }; } @@ -510,7 +493,7 @@ impl Decorator202203 { }))), right: Box::new(Expr::Call(CallExpr { span: DUMMY_SP, - callee: Expr::Ident(ident("_apply_decs_2203_r")).as_callee(), + callee: ident("_apply_decs_2203_r").as_callee(), args: combined_args, type_args: Default::default(), })), @@ -551,7 +534,7 @@ impl Decorator202203 { Ident::new(format!("_{prefix}_{}", i.sym).into(), i.span.private()), ), _ => { - unreachable!() + unreachable!("{}:{}", file!(), line!()) } }, _ => { @@ -603,7 +586,7 @@ impl Decorator202203 { } } - unreachable!() + unreachable!("{}:{}", file!(), line!()) } fn ensure_identity_constructor<'a>(&mut self, c: &'a mut Class) -> &'a mut Constructor { @@ -637,7 +620,7 @@ impl Decorator202203 { } } - unreachable!() + unreachable!("{}:{}", file!(), line!()) } fn handle_super_class(&mut self, class: &mut Class) { @@ -1200,6 +1183,7 @@ impl VisitMut for Decorator202203 { }); p.kind = MethodKind::Getter; + p.function.params = vec![]; p.function.body = Some(BlockStmt { span: DUMMY_SP, stmts: vec![call_stmt], @@ -1798,7 +1782,10 @@ impl VisitMut for Decorator202203 { span: _, decl: DefaultDecl::Class(c), })) => { - self.handle_class_expr(&mut c.class, c.ident.as_ref()); + if !c.class.decorators.is_empty() { + self.handle_class_expr(&mut c.class, c.ident.as_ref()); + } + s.visit_mut_children_with(self); } _ => { @@ -1871,6 +1858,57 @@ impl VisitMut for Decorator202203 { self.extra_lets = old_extra_lets; } + fn visit_mut_script(&mut self, n: &mut Script) { + let old_extra_lets = self.extra_lets.take(); + + let mut new = Vec::with_capacity(n.body.len()); + + for mut n in n.body.take() { + n.visit_mut_with(self); + if !self.extra_lets.is_empty() { + new.push( + Stmt::Decl(Decl::Var(Box::new(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Let, + decls: self.extra_lets.take(), + declare: false, + }))) + .into(), + ) + } + if !self.pre_class_inits.is_empty() { + new.push( + Stmt::Expr(ExprStmt { + span: DUMMY_SP, + expr: Expr::from_exprs(self.pre_class_inits.take()), + }) + .into(), + ) + } + new.push(n.take()); + } + + if !self.extra_vars.is_empty() { + prepend_stmt( + &mut new, + VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Var, + decls: self.extra_vars.take(), + declare: false, + } + .into(), + ); + } + + n.body = new; + if !self.rename_map.is_empty() { + n.visit_mut_with(&mut IdentRenamer::new(&self.rename_map)); + } + + self.extra_lets = old_extra_lets; + } + fn visit_mut_private_prop(&mut self, p: &mut PrivateProp) { p.visit_mut_children_with(self); diff --git a/src/parser/transformers/mod.rs b/src/parser/transformers/mod.rs index 88ae1a1..d494367 100644 --- a/src/parser/transformers/mod.rs +++ b/src/parser/transformers/mod.rs @@ -2,8 +2,14 @@ mod anonymous_expr; mod class_jobject; mod class_reflection_decorators; mod decorator_2022_03; +mod remove_assert_calls; +mod resolve_self_identifiers; +mod static_blocks; pub(crate) use anonymous_expr::anonymous_expr; pub(crate) use class_jobject::class_jobject; pub(crate) use class_reflection_decorators::class_reflection_decorators; pub(crate) use decorator_2022_03::decorator_2022_03; +pub(crate) use remove_assert_calls::remove_assert_calls; +pub(crate) use resolve_self_identifiers::resolve_self_identifiers; +pub(crate) use static_blocks::static_blocks; diff --git a/src/parser/transformers/remove_assert_calls.rs b/src/parser/transformers/remove_assert_calls.rs new file mode 100644 index 0000000..ecb9c13 --- /dev/null +++ b/src/parser/transformers/remove_assert_calls.rs @@ -0,0 +1,42 @@ +use std::mem::replace; +use swc_common::DUMMY_SP; +use swc_ecma_ast::*; +use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut}; + +pub fn remove_assert_calls() -> impl VisitMut + Fold { + as_folder(RemoveAssertCalls::default()) +} + +#[derive(Default)] +struct RemoveAssertCalls {} + +impl VisitMut for RemoveAssertCalls { + noop_visit_mut_type!(); + + fn visit_mut_expr(&mut self, n: &mut Expr) { + let Expr::Call(call) = n else { + return; + }; + let Callee::Expr(expr) = &mut call.callee else { + return; + }; + let Expr::Ident(id) = expr.as_ref() else { + return; + }; + + if id.sym == "__assert" { + let _ = replace( + n, + Expr::Unary(UnaryExpr { + span: DUMMY_SP, + op: UnaryOp::Void, + arg: Box::new(Expr::Lit(Lit::Num(Number { + span: DUMMY_SP, + value: 0.0, + raw: Some("0".into()), + }))), + }), + ); + } + } +} diff --git a/src/parser/transformers/resolve_self_identifiers.rs b/src/parser/transformers/resolve_self_identifiers.rs new file mode 100644 index 0000000..907bc73 --- /dev/null +++ b/src/parser/transformers/resolve_self_identifiers.rs @@ -0,0 +1,57 @@ +use std::mem::replace; +use swc_common::{Mark, SyntaxContext}; +use swc_ecma_ast::*; +use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; + +pub fn resolve_self_identifiers(unresolved_mark: Mark) -> impl VisitMut + Fold { + as_folder(ResolveSelfIdentifiers { + unresolved: SyntaxContext::empty().apply_mark(unresolved_mark), + class_stack: vec![], + }) +} + +#[derive(Default)] +struct ResolveSelfIdentifiers { + unresolved: SyntaxContext, + class_stack: Vec, +} + +impl VisitMut for ResolveSelfIdentifiers { + noop_visit_mut_type!(); + + fn visit_mut_class_decl(&mut self, n: &mut ClassDecl) { + self.class_stack.push(n.ident.clone()); + n.visit_mut_children_with(self); + let _ = self.class_stack.pop(); + } + + fn visit_mut_class_expr(&mut self, n: &mut ClassExpr) { + self.class_stack.push( + n.ident + .clone() + .expect("anonymous_expr transformer needs to be called before this"), + ); + n.visit_mut_children_with(self); + let _ = self.class_stack.pop(); + } + + fn visit_mut_ident(&mut self, n: &mut Ident) { + if n.span.ctxt == self.unresolved && n.sym == "__self" { + let Some(current_class) = self.class_stack.last() else { + return; + }; + let _ = replace(n, current_class.clone()); + } + + // match expr { + // Expr::Ident(i) if i.span.ctxt != self.unresolved => false, + // _ => { + // if is_call && self.c.pure_getter { + // !is_simple_member(expr) + // } else { + // true + // } + // } + // } + } +} diff --git a/src/parser/transformers/static_blocks.rs b/src/parser/transformers/static_blocks.rs new file mode 100644 index 0000000..8cf1e4f --- /dev/null +++ b/src/parser/transformers/static_blocks.rs @@ -0,0 +1,121 @@ +use swc_atoms::JsWord; +use swc_common::{collections::AHashSet, util::take::Take, Mark, DUMMY_SP}; +use swc_ecma_ast::*; +use swc_ecma_utils::ExprFactory; +use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; + +struct ClassStaticBlock { + mark: Mark, +} + +pub fn static_blocks(mark: Mark) -> impl Fold + VisitMut { + as_folder(ClassStaticBlock { mark }) +} + +impl ClassStaticBlock { + fn transform_static_block( + &mut self, + mut static_block: StaticBlock, + private_id: JsWord, + ) -> PrivateProp { + let mut stmts = static_block.body.stmts.take(); + let span = static_block.span; + + // We special-case the single expression case to avoid the iife, since it's + // common. + let value = if stmts.len() == 1 && stmts[0].is_expr() { + stmts[0].take().expr().map(|expr_stmt| expr_stmt.expr) + } else { + static_block.body.stmts = stmts; + + let expr = Expr::Call(CallExpr { + span: DUMMY_SP, + callee: ParenExpr { + span: DUMMY_SP, + expr: Box::new(Expr::Arrow(ArrowExpr { + span: DUMMY_SP, + params: Vec::new(), + is_async: false, + is_generator: false, + type_params: None, + return_type: None, + body: Box::new(BlockStmtOrExpr::BlockStmt(static_block.body)), + })), + } + .as_callee(), + args: Vec::new(), + type_args: None, + }); + + Some(Box::new(expr)) + }; + + PrivateProp { + span: span.apply_mark(self.mark), + is_static: true, + is_optional: false, + is_override: false, + readonly: false, + type_ann: None, + decorators: Vec::new(), + accessibility: None, + key: PrivateName { + span: DUMMY_SP, + id: Ident { + span: DUMMY_SP, + sym: private_id, + optional: false, + }, + }, + value, + definite: false, + } + } +} + +impl VisitMut for ClassStaticBlock { + noop_visit_mut_type!(); + + fn visit_mut_class(&mut self, class: &mut Class) { + class.visit_mut_children_with(self); + + let mut private_names = AHashSet::default(); + for member in &class.body { + if let ClassMember::PrivateProp(private_property) = member { + private_names.insert(private_property.key.id.sym.clone()); + } + } + + let mut count = 0; + for member in class.body.iter_mut() { + if let ClassMember::StaticBlock(static_block) = member { + if static_block.body.stmts.is_empty() { + *member = ClassMember::dummy(); + continue; + } + + let static_block_private_id = generate_uid(&private_names, &mut count); + *member = self + .transform_static_block(static_block.take(), static_block_private_id) + .into(); + }; + } + } +} + +fn generate_uid(deny_list: &AHashSet, i: &mut u32) -> JsWord { + *i += 1; + + let mut uid: JsWord = if *i == 1 { + "_".to_string() + } else { + format!("_{i}") + } + .into(); + while deny_list.contains(&uid) { + *i += 1; + uid = format!("_{i}").into(); + } + + uid +} diff --git a/src/reflection/mod.rs b/src/reflection/mod.rs new file mode 100644 index 0000000..282de08 --- /dev/null +++ b/src/reflection/mod.rs @@ -0,0 +1,82 @@ +#[cfg(not(test))] +use lazy_static::lazy_static; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; +use swc_common::Span; +use swc_ecma_ast::{Class, Ident}; +use uuid::Uuid; + +#[cfg(test)] +thread_local! { + static CLASS_REGISTRY: RwLock>> = RwLock::new(Default::default()); +} + +#[cfg(not(test))] +lazy_static! { + static ref CLASS_REGISTRY: RwLock>> = + RwLock::new(Default::default()); +} + +pub struct ReflectionData { + pub class: Class, + pub name: Ident, + pub filename: Option, + pub namespace: Option, + pub docblock: HashMap>, +} + +impl ReflectionData { + pub fn new( + class: &Class, + name: Ident, + filename: Option<&str>, + namespace: Option<&str>, + docblock: HashMap>, + ) -> Self { + Self { + class: class.clone(), + name, + filename: filename.map(|s| s.to_string()), + namespace: namespace.map(|s| s.to_string()), + docblock, + } + } +} + +pub(crate) fn register_class(class_id: &Uuid, data: ReflectionData) { + #[cfg(not(test))] + { + let mut registry = CLASS_REGISTRY.write().unwrap(); + + debug_assert!(registry.get(class_id).is_none()); + registry.insert(class_id.clone(), Arc::new(data)); + } + + #[cfg(test)] + { + CLASS_REGISTRY.with(|lock| { + let mut registry = lock.write().unwrap(); + + debug_assert!(registry.get(class_id).is_none()); + registry.insert(class_id.clone(), Arc::new(data)); + }); + } +} + +pub(crate) fn get_reflection_data<'a>(class_id: &Uuid) -> Option> { + #[cfg(not(test))] + let data = { + let registry = CLASS_REGISTRY.read().unwrap(); + registry.get(class_id).map(Clone::clone) + }; + + #[cfg(test)] + let data = { + CLASS_REGISTRY.with(|lock| { + let registry = lock.read().unwrap(); + registry.get(class_id).map(Clone::clone) + }) + }; + + data +} diff --git a/src/stack/mod.rs b/src/stack/mod.rs index 2fe60c4..5e8530c 100644 --- a/src/stack/mod.rs +++ b/src/stack/mod.rs @@ -15,6 +15,7 @@ lazy_static! { RwLock::new(HashMap::new()); } +#[derive(Debug)] pub(crate) struct Frame { pub filename: Option, pub line_no: u32, diff --git a/src/stack/trace.rs b/src/stack/trace.rs index f5d03d8..738243a 100644 --- a/src/stack/trace.rs +++ b/src/stack/trace.rs @@ -3,7 +3,7 @@ use super::{Frame, FILE_MAPPINGS}; /// Prepares stack trace using V8 stack trace API. pub(crate) fn remap_stack_trace( error_message: &str, - stack: Box<[Frame]>, + stack: &[Frame], previous: Option, ) -> String { let mut processed = false; @@ -20,23 +20,25 @@ pub(crate) fn remap_stack_trace( return frame.string_repr.clone(); }; - let Some(token) = source_map.0.lookup_token(frame.line_no, frame.col_no) else { + let line_no = frame.line_no - 1; + let col_no = frame.col_no - 1; + let Some(token) = source_map.0.lookup_token(line_no, col_no) else { return frame.string_repr.clone(); }; let file_location = format!( "{}:{}:{}", file_name, - token.get_src_line(), - token.get_src_col() + token.get_src_line() + 1, + token.get_src_col() + 1, ); let mut function_name = frame.function_name.as_ref().cloned(); if let Some(sv) = source_map.0.get_source_view(token.get_src_id()) { function_name = source_map .0 .get_original_function_name( - frame.line_no, - frame.col_no, + line_no, + col_no, function_name.unwrap_or_default().as_str(), sv, ) @@ -113,6 +115,7 @@ pub(crate) fn remap_stack_trace( }) .collect::>(); + #[allow(clippy::unnecessary_unwrap)] if previous.is_some() && !processed { previous.unwrap() } else { diff --git a/src/testing/inject_helpers.rs b/src/testing/inject_helpers.rs index 27ff9e3..c570cc4 100644 --- a/src/testing/inject_helpers.rs +++ b/src/testing/inject_helpers.rs @@ -1,13 +1,11 @@ use std::path::Path; -use swc_common::{DUMMY_SP, Mark}; +use swc_common::{Mark, DUMMY_SP}; use swc_ecma_ast::*; -use swc_ecma_utils::{ExprFactory, prepend_stmts, quote_ident}; -use swc_ecma_visit::{as_folder, Fold, noop_visit_mut_type, VisitMut, VisitMutWith}; +use swc_ecma_utils::{prepend_stmts, quote_ident, ExprFactory}; +use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith}; pub fn inject_helpers(global_mark: Mark) -> impl Fold + VisitMut { - as_folder(InjectHelpers { - global_mark, - }) + as_folder(InjectHelpers { global_mark }) } struct InjectHelpers { @@ -26,10 +24,7 @@ impl InjectHelpers { fn build_import(&self, name: &str, mark: Mark) -> ModuleItem { let s = ImportSpecifier::Named(ImportNamedSpecifier { span: DUMMY_SP, - local: Ident::new( - format!("_{}", name).into(), - DUMMY_SP.apply_mark(mark), - ), + local: Ident::new(format!("_{}", name).into(), DUMMY_SP.apply_mark(mark)), imported: Some(quote_ident!("_").into()), is_type_only: false, }); @@ -53,13 +48,13 @@ impl InjectHelpers { sym: "require".into(), optional: false, }) - .as_callee(), + .as_callee(), args: vec![Str { span: DUMMY_SP, value: Self::build_helper_path(name).into(), raw: None, } - .as_arg()], + .as_arg()], type_args: None, }; let decl = Decl::Var( @@ -76,7 +71,7 @@ impl InjectHelpers { definite: false, }], } - .into(), + .into(), ); Stmt::Decl(decl) } diff --git a/src/testing/mod.rs b/src/testing/mod.rs index e1f2af0..a627a34 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -1,28 +1,26 @@ mod inject_helpers; +pub mod uuid; -use std::{env, fs}; +use ansi_term::Color; +pub use inject_helpers::inject_helpers; +use sha1::{Digest, Sha1}; use std::fs::{create_dir_all, OpenOptions}; use std::io::Write; use std::path::Path; use std::process::Command; -use ansi_term::Color; -use sha1::{Digest, Sha1}; -use swc_common::{Mark}; +use std::{env, fs}; +use swc_common::Mark; use swc_ecma_parser::Syntax; -use swc_ecma_transforms_base::{ - fixer, - hygiene, -}; +use swc_ecma_transforms_base::{fixer, hygiene}; use swc_ecma_transforms_testing::{HygieneVisualizer, Tester}; use swc_ecma_visit::{Fold, FoldWith}; use tempfile::tempdir_in; use testing::find_executable; -pub use inject_helpers::inject_helpers; fn make_tr(op: F, tester: &mut Tester<'_>) -> impl Fold - where - F: FnOnce(&mut Tester<'_>) -> P, - P: Fold, +where + F: FnOnce(&mut Tester<'_>) -> P, + P: Fold, { op(tester) } @@ -37,9 +35,9 @@ fn calc_hash(s: &str) -> String { /// Execute `jest` after transpiling `input` using `tr`. pub fn exec_tr(test_name: &str, syntax: Syntax, tr: F, input: &str) - where - F: FnOnce(&mut Tester<'_>) -> P, - P: Fold, +where + F: FnOnce(&mut Tester<'_>) -> P, + P: Fold, { Tester::run(|tester| { let tr = make_tr(tr, tester); @@ -149,4 +147,4 @@ fn exec_with_node_test_runner(test_name: &str, src: &str) -> Result<(), ()> { let dir_name = path.display().to_string(); ::std::mem::forget(tmp_dir); panic!("Execution failed: {dir_name}") -} \ No newline at end of file +} diff --git a/src/testing/uuid.rs b/src/testing/uuid.rs new file mode 100644 index 0000000..d3f728d --- /dev/null +++ b/src/testing/uuid.rs @@ -0,0 +1,21 @@ +use std::cell::RefCell; +use uuid::Uuid; + +thread_local!( + static UUID_LOW_BITS: RefCell = RefCell::new(0); +); + +pub fn generate_test_uuid() -> Uuid { + let low_bits = UUID_LOW_BITS.with(|lb| { + let val = *lb.borrow(); + UUID_LOW_BITS.set(val + 1); + + val + }); + + Uuid::from_u64_pair(0, low_bits) +} + +pub fn reset_test_uuid() { + UUID_LOW_BITS.set(0); +} diff --git a/src/wasm/compile.rs b/src/wasm/compile.rs new file mode 100644 index 0000000..20aebfd --- /dev/null +++ b/src/wasm/compile.rs @@ -0,0 +1,103 @@ +use crate::parser::{parse, CompileOptions}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(typescript_custom_section)] +const ITEXT_STYLE: &'static str = r#" +interface CompileOptions { + debug?: boolean; + namespace?: string; +} +"#; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "CompileOptions")] + pub type WasmCompileOptions; + + #[wasm_bindgen(structural, method, getter)] + fn debug(this: &WasmCompileOptions) -> Option; + + #[wasm_bindgen(structural, method, getter)] + fn namespace(this: &WasmCompileOptions) -> Option; +} + +#[wasm_bindgen(js_name = compile)] +pub fn compile( + source: String, + filename: Option, + opts: Option, +) -> Result { + let debug = opts.as_ref().and_then(|c| c.debug()).unwrap_or_default(); + let namespace = opts.as_ref().and_then(|c| c.namespace()); + + let program = match parse(source, filename.as_deref()) { + Ok(p) => p, + Err(e) => { + return Err(JsError::new(&e.to_string())); + } + }; + + Ok(program.compile(CompileOptions { debug, namespace })?) +} + +#[cfg(test)] +mod tests { + use crate::parse_uuid; + use crate::parser::{parse, CompileOptions}; + use crate::reflection::get_reflection_data; + use crate::testing::uuid::reset_test_uuid; + use crate::wasm::reflection::get_js_reflection_data; + + #[test] + pub fn should_compile_program_correctly() -> Result<(), Box> { + reset_test_uuid(); + + let code = r#" +const a = () => Symbol('test'); +const type = t => { + return function (value, context) { + console.log(value, context); + }; +}; + +/** class docblock */ +export default class x { + static #staticPrivateField; + #privateField; + accessor #privateAccessor; + static staticPublicField; + publicField; + accessor publicAccessor; + + /** + * computed method docblock + */ + [a()]() {} + #privateMethod(a, b = 1, [c, d], {f, g}) {} + publicMethod({a, b} = {}, c = new Object(), ...x) {} + static #staticPrivateMethod() {} + static staticPublicMethod() {} + + get [a()]() {} + set b(v) {} + + get #ap() {} + set #bp(v) {} + + act(@type(String) param1) {} + [a()](@type(String) param1) {} + [Symbol.for('xtest')](@type(String) param1) {} +} +"#; + + let program = parse(code.to_string(), Some("x.js"))?; + let compiled = program.compile(CompileOptions { + debug: true, + namespace: None, + })?; + + let data = get_js_reflection_data("00000000-0000-0000-0000-000000000000"); + + Ok(()) + } +} diff --git a/src/wasm/mod.rs b/src/wasm/mod.rs index 26f32cc..4a7d2ca 100644 --- a/src/wasm/mod.rs +++ b/src/wasm/mod.rs @@ -1,156 +1,61 @@ +mod compile; +mod reflection; +mod stack_trace; + extern crate alloc; -use crate::stack::remap_stack_trace; -use crate::Frame; -use js_sys::*; -use std::iter::Iterator; +use crate::wasm::stack_trace::{prepare_stack_trace, CallSite, Error}; +use js_sys::{Array, Function}; use wasm_bindgen::prelude::*; #[wasm_bindgen] +#[cfg(debug_assertions)] extern "C" { - #[wasm_bindgen(typescript_type = "Error")] - pub type Error; - - #[wasm_bindgen(method, getter)] - pub fn name(this: &Error) -> String; - - #[wasm_bindgen(method, getter)] - pub fn message(this: &Error) -> JsValue; - - #[wasm_bindgen(method, getter)] - pub fn stack(this: &Error) -> Option; - - #[wasm_bindgen(static_method_of = Error, getter = prepareStackTrace)] - pub fn prepare_stack_trace() -> Option; - #[wasm_bindgen(static_method_of = Error, setter = prepareStackTrace)] - pub fn set_prepare_stack_trace(closure: &Function); - - #[wasm_bindgen(method, js_name = toString)] - pub fn to_string(this: &Error) -> String; + #[wasm_bindgen(js_namespace = console)] + pub fn log(s: &str); } -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "NodeJS.CallSite")] - pub type CallSite; - - /// Value of "this" - #[wasm_bindgen(method, js_name = getThis)] - pub fn get_this(this: &CallSite) -> JsValue; - - /// Type of "this" as a string. - /// This is the name of the function stored in the constructor field of - /// "this", if available. Otherwise the object's [[Class]] internal - /// property. - #[wasm_bindgen(method, js_name = getTypeName)] - pub fn get_type_name(this: &CallSite) -> Option; - - /// Current function - #[wasm_bindgen(method, js_name = getFunction)] - pub fn get_function(this: &CallSite) -> Option; - - /// Name of the current function, typically its name property. - /// If a name property is not available an attempt will be made to try - /// to infer a name from the function's context. - #[wasm_bindgen(method, js_name = getFunctionName)] - pub fn get_function_name(this: &CallSite) -> Option; - - /// Name of the property [of "this" or one of its prototypes] that holds - /// the current function - #[wasm_bindgen(method, js_name = getMethodName)] - pub fn get_method_name(this: &CallSite) -> Option; - - /// Name of the script [if this function was defined in a script] - #[wasm_bindgen(method, js_name = getFileName)] - pub fn get_file_name(this: &CallSite) -> Option; - - /// Current line number [if this function was defined in a script] - #[wasm_bindgen(method, js_name = getLineNumber)] - pub fn get_line_number(this: &CallSite) -> Option; - - /// Current column number [if this function was defined in a script] - #[wasm_bindgen(method, js_name = getColumnNumber)] - pub fn get_column_number(this: &CallSite) -> Option; - - /// A call site object representing the location where eval was called - /// [if this function was created using a call to eval] - #[wasm_bindgen(method, js_name = getEvalOrigin)] - pub fn get_eval_origin(this: &CallSite) -> Option; - - /// Is this a toplevel invocation, that is, is "this" the global object? - #[wasm_bindgen(method, js_name = isToplevel)] - pub fn is_top_level(this: &CallSite) -> bool; - - /// Does this call take place in code defined by a call to eval? - #[wasm_bindgen(method, js_name = isEval)] - pub fn is_eval(this: &CallSite) -> bool; - - /// Is this call in native V8 code? - #[wasm_bindgen(method, js_name = isNative)] - pub fn is_native(this: &CallSite) -> bool; - - /// Is this a constructor call? - #[wasm_bindgen(method, js_name = isConstructor)] - pub fn is_constructor(this: &CallSite) -> bool; - - /// Is this an async call (i.e. await, Promise.all(), or Promise.any())? - #[wasm_bindgen(method, js_name = isAsync)] - pub fn is_async(this: &CallSite) -> bool; - - /// Is this an async call to Promise.all()? - #[wasm_bindgen(method, js_name = isPromiseAll)] - pub fn is_promise_all(this: &CallSite) -> bool; - - /// Is this an async call to Promise.any()? - #[wasm_bindgen(method, js_name = isPromiseAny)] - pub fn is_promise_any(this: &CallSite) -> bool; - - /// Returns the index of the promise element that was followed in - /// Promise.all() or Promise.any() for async stack traces, or null if the - /// CallSite is not an async Promise.all() or Promise.any() call. - #[wasm_bindgen(method, js_name = getPromiseIndex)] - pub fn get_promise_index(this: &CallSite) -> Option; - - #[wasm_bindgen(method, js_name = toString)] - pub fn to_string(this: &CallSite) -> String; +#[cfg(debug_assertions)] +#[macro_export] +macro_rules! console_log { + // Note that this is using the `log` function imported above during + // `bare_bones` + ($($t:tt)*) => ($crate::wasm::log(&format_args!($($t)*).to_string())) } -impl From<&CallSite> for Frame { - fn from(value: &CallSite) -> Self { - Self { - filename: value.get_file_name(), - line_no: value.get_line_number().unwrap_or_default() as u32, - col_no: value.get_column_number().unwrap_or_default() as u32, - function_name: value.get_function_name(), - method_name: value.get_method_name(), - type_name: value.get_type_name(), - is_native: value.is_native(), - is_top_level: value.is_top_level(), - is_constructor: value.is_constructor(), - is_async: value.is_async(), - is_promise_all: value.is_promise_all(), - promise_index: value.get_promise_index().unwrap_or_default(), - string_repr: value.to_string(), - } - } +#[cfg(not(debug_assertions))] +#[macro_export] +macro_rules! console_log { + // Note that this is using the `log` function imported above during + // `bare_bones` + ($($t:tt)*) => {}; } -#[wasm_bindgen(js_name = prepareStackTrace)] -pub fn prepare_stack_trace( - error: Error, - stack: Box<[CallSite]>, - previous: Option, -) -> String { - let message: JsValue = error.message(); - let message: String = message - .dyn_ref::() - .cloned() - .unwrap_or_else(|| JsString::from("")) - .into(); - - let stack = stack - .into_iter() - .map(|cs| Frame::from(cs)) - .collect::>(); - remap_stack_trace(&message, stack.into_boxed_slice(), previous) +#[wasm_bindgen(start)] +pub fn start() { + let previous: Option = Error::prepare_stack_trace(); + + let a = Closure::) -> String>::new( + move |error: Error, stack: Box<[CallSite]>| -> String { + let prev = if let Some(func) = &previous { + let this = &JsValue::null(); + let e = JsValue::from(&error); + let s = Array::from_iter(stack.iter()); + + if let Ok(val) = func.call2(this, &e, &s) { + val.as_string() + } else { + None + } + } else { + None + }; + + prepare_stack_trace(error, stack, prev) + }, + ); + + Error::set_stack_trace_limit(0); + Error::set_prepare_stack_trace(a.as_ref().unchecked_ref()); + a.forget(); } diff --git a/src/wasm/reflection.rs b/src/wasm/reflection.rs new file mode 100644 index 0000000..8e8de57 --- /dev/null +++ b/src/wasm/reflection.rs @@ -0,0 +1,220 @@ +use crate::parse_uuid; +use crate::reflection::get_reflection_data; +use serde::{Deserialize, Serialize}; +use swc_ecma_ast::*; +use wasm_bindgen::prelude::*; + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct JsMethodParameter { + pub name: Option, + pub index: usize, + pub has_default: bool, + pub is_object_pattern: bool, + pub is_array_pattern: bool, + pub is_rest_element: bool, +} + +impl From<&Param> for JsMethodParameter { + fn from(value: &Param) -> Self { + let (is_rest, pat) = if let Pat::Rest(r) = &value.pat { + (true, r.arg.clone()) + } else { + (false, Box::new(value.pat.clone())) + }; + + JsMethodParameter { + name: pat.as_ident().map(|i| i.sym.to_string()), + index: 0, + has_default: pat.is_assign(), + is_object_pattern: pat.is_object(), + is_array_pattern: pat.is_array(), + is_rest_element: is_rest, + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct JsFieldData { + pub index: usize, + pub docblock: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct JsMethodData { + pub params: Vec, + pub index: usize, + pub docblock: Option, +} + +#[derive(Serialize, Deserialize)] +#[serde(tag = "kind")] +pub enum JsMemberData { + Method(JsMethodData), + Field(JsFieldData), +} + +#[derive(Serialize, Deserialize)] +pub struct JsReflectionData { + pub fqcn: String, + pub class_name: String, + pub namespace: Option, + pub filename: Option, + pub members: Vec, + pub docblock: Option, +} + +#[wasm_bindgen(js_name = getInternalReflectionData)] +pub fn get_js_reflection_data(class_id: &str) -> Result { + let Ok(class_id) = parse_uuid(class_id) else { + return Ok(JsValue::undefined()); + }; + let Some(reflection_data) = get_reflection_data(&class_id) else { + return Ok(JsValue::undefined()); + }; + + let class = &reflection_data.class; + let namespace = reflection_data.namespace.clone(); + + let members = class + .body + .iter() + .enumerate() + .filter_map(|(index, n)| match n { + ClassMember::Constructor(c) => { + let params = c + .params + .iter() + .enumerate() + .map(|(i, p)| match p { + ParamOrTsParamProp::TsParamProp(tp) => JsMethodParameter { + name: tp.param.as_ident().map(|i| i.sym.to_string()), + index: i, + has_default: tp.param.is_assign(), + is_object_pattern: tp + .param + .as_assign() + .map(|a| a.left.is_object()) + .unwrap_or(false), + is_array_pattern: tp + .param + .as_assign() + .map(|a| a.left.is_array()) + .unwrap_or(false), + is_rest_element: false, + }, + ParamOrTsParamProp::Param(p) => { + let mut p = JsMethodParameter::from(p); + p.index = i; + + p + } + }) + .collect(); + + Some(JsMemberData::Method(JsMethodData { + params, + index, + docblock: reflection_data + .docblock + .get(&c.span) + .cloned() + .unwrap_or_default(), + })) + } + ClassMember::Method(m) => { + let params = m + .function + .params + .iter() + .enumerate() + .map(|(i, p)| { + let mut p = JsMethodParameter::from(p); + p.index = i; + + p + }) + .collect(); + + Some(JsMemberData::Method(JsMethodData { + params, + index, + docblock: reflection_data + .docblock + .get(&m.span) + .cloned() + .unwrap_or_default(), + })) + } + ClassMember::PrivateMethod(m) => { + let params = m + .function + .params + .iter() + .enumerate() + .map(|(i, p)| { + let mut p = JsMethodParameter::from(p); + p.index = i; + + p + }) + .collect(); + + Some(JsMemberData::Method(JsMethodData { + params, + index, + docblock: reflection_data + .docblock + .get(&m.span) + .cloned() + .unwrap_or_default(), + })) + } + ClassMember::ClassProp(p) => Some(JsMemberData::Field(JsFieldData { + index, + docblock: reflection_data + .docblock + .get(&p.span) + .cloned() + .unwrap_or_default(), + })), + ClassMember::PrivateProp(p) => Some(JsMemberData::Field(JsFieldData { + index, + docblock: reflection_data + .docblock + .get(&p.span) + .cloned() + .unwrap_or_default(), + })), + ClassMember::AutoAccessor(a) => Some(JsMemberData::Field(JsFieldData { + index, + docblock: reflection_data + .docblock + .get(&a.span) + .cloned() + .unwrap_or_default(), + })), + _ => None, + }) + .collect(); + + let class_name = reflection_data.name.sym.to_string(); + let fqcn = if let Some(ns) = namespace.as_deref() { + format!("{}.{}", ns, class_name) + } else { + class_name.clone() + }; + + Ok(serde_wasm_bindgen::to_value(&JsReflectionData { + fqcn, + class_name, + namespace, + filename: reflection_data.filename.clone(), + members, + docblock: reflection_data + .docblock + .get(&class.span) + .cloned() + .unwrap_or_default(), + })?) +} diff --git a/src/wasm/stack_trace.rs b/src/wasm/stack_trace.rs new file mode 100644 index 0000000..f187de8 --- /dev/null +++ b/src/wasm/stack_trace.rs @@ -0,0 +1,155 @@ +use crate::stack::remap_stack_trace; +use crate::Frame; +use js_sys::*; +use std::iter::Iterator; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "Error")] + pub type Error; + + #[wasm_bindgen(method, getter)] + pub fn name(this: &Error) -> String; + + #[wasm_bindgen(method, getter)] + pub fn message(this: &Error) -> JsValue; + + #[wasm_bindgen(method, getter)] + pub fn stack(this: &Error) -> Option; + + #[wasm_bindgen(static_method_of = Error, getter = prepareStackTrace)] + pub fn prepare_stack_trace() -> Option; + #[wasm_bindgen(static_method_of = Error, setter = prepareStackTrace)] + pub fn set_prepare_stack_trace(closure: &Function); + + #[wasm_bindgen(static_method_of = Error, setter = stackTraceLimit)] + pub fn set_stack_trace_limit(val: usize); + + #[wasm_bindgen(method, js_name = toString)] + pub fn to_string(this: &Error) -> String; +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "NodeJS.CallSite")] + pub type CallSite; + + /// Value of "this" + #[wasm_bindgen(method, js_name = getThis)] + pub fn get_this(this: &CallSite) -> JsValue; + + /// Type of "this" as a string. + /// This is the name of the function stored in the constructor field of + /// "this", if available. Otherwise the object's [[Class]] internal + /// property. + #[wasm_bindgen(method, js_name = getTypeName)] + pub fn get_type_name(this: &CallSite) -> Option; + + /// Current function + #[wasm_bindgen(method, js_name = getFunction)] + pub fn get_function(this: &CallSite) -> Option; + + /// Name of the current function, typically its name property. + /// If a name property is not available an attempt will be made to try + /// to infer a name from the function's context. + #[wasm_bindgen(method, js_name = getFunctionName)] + pub fn get_function_name(this: &CallSite) -> Option; + + /// Name of the property [of "this" or one of its prototypes] that holds + /// the current function + #[wasm_bindgen(method, js_name = getMethodName)] + pub fn get_method_name(this: &CallSite) -> Option; + + /// Name of the script [if this function was defined in a script] + #[wasm_bindgen(method, js_name = getFileName)] + pub fn get_file_name(this: &CallSite) -> Option; + + /// Current line number [if this function was defined in a script] + #[wasm_bindgen(method, js_name = getLineNumber)] + pub fn get_line_number(this: &CallSite) -> Option; + + /// Current column number [if this function was defined in a script] + #[wasm_bindgen(method, js_name = getColumnNumber)] + pub fn get_column_number(this: &CallSite) -> Option; + + /// A call site object representing the location where eval was called + /// [if this function was created using a call to eval] + #[wasm_bindgen(method, js_name = getEvalOrigin)] + pub fn get_eval_origin(this: &CallSite) -> Option; + + /// Is this a toplevel invocation, that is, is "this" the global object? + #[wasm_bindgen(method, js_name = isToplevel)] + pub fn is_top_level(this: &CallSite) -> bool; + + /// Does this call take place in code defined by a call to eval? + #[wasm_bindgen(method, js_name = isEval)] + pub fn is_eval(this: &CallSite) -> bool; + + /// Is this call in native V8 code? + #[wasm_bindgen(method, js_name = isNative)] + pub fn is_native(this: &CallSite) -> bool; + + /// Is this a constructor call? + #[wasm_bindgen(method, js_name = isConstructor)] + pub fn is_constructor(this: &CallSite) -> bool; + + /// Is this an async call (i.e. await, Promise.all(), or Promise.any())? + #[wasm_bindgen(method, js_name = isAsync)] + pub fn is_async(this: &CallSite) -> bool; + + /// Is this an async call to Promise.all()? + #[wasm_bindgen(method, js_name = isPromiseAll)] + pub fn is_promise_all(this: &CallSite) -> bool; + + /// Is this an async call to Promise.any()? + #[wasm_bindgen(method, js_name = isPromiseAny)] + pub fn is_promise_any(this: &CallSite) -> bool; + + /// Returns the index of the promise element that was followed in + /// Promise.all() or Promise.any() for async stack traces, or null if the + /// CallSite is not an async Promise.all() or Promise.any() call. + #[wasm_bindgen(method, js_name = getPromiseIndex)] + pub fn get_promise_index(this: &CallSite) -> Option; + + #[wasm_bindgen(method, js_name = toString)] + pub fn to_string(this: &CallSite) -> String; +} + +impl From<&CallSite> for Frame { + fn from(value: &CallSite) -> Self { + Self { + filename: value.get_file_name(), + line_no: value.get_line_number().unwrap_or_default() as u32, + col_no: value.get_column_number().unwrap_or_default() as u32, + function_name: value.get_function_name(), + method_name: value.get_method_name(), + type_name: value.get_type_name(), + is_native: value.is_native(), + is_top_level: value.is_top_level(), + is_constructor: value.is_constructor(), + is_async: value.is_async(), + is_promise_all: value.is_promise_all(), + promise_index: value.get_promise_index().unwrap_or_default(), + string_repr: value.to_string(), + } + } +} + +#[wasm_bindgen(js_name = prepareStackTrace)] +pub fn prepare_stack_trace( + error: Error, + #[allow(clippy::boxed_local)] stack: Box<[CallSite]>, + previous: Option, +) -> String { + let message: JsValue = error.message(); + let message: String = message + .dyn_ref::() + .cloned() + .unwrap_or_else(|| JsString::from("")) + .into(); + + let stack = stack.iter().map(Frame::from).collect::>(); + + remap_stack_trace(&message, &stack.into_boxed_slice(), previous) +} diff --git a/tests/decorators/2022-03-metadata/class/exec.js b/tests/decorators/2022-03-metadata/class/exec.js index 267e022..f114ea4 100644 --- a/tests/decorators/2022-03-metadata/class/exec.js +++ b/tests/decorators/2022-03-metadata/class/exec.js @@ -1,5 +1,5 @@ function dec(_, ctx) { - ctx.metadata.foo = 3; + ctx.metadata.foo = 3; } Symbol.metadata = Symbol(); diff --git a/tests/decorators/2022-03-metadata/element/exec.js b/tests/decorators/2022-03-metadata/element/exec.js index c70d840..4d63769 100644 --- a/tests/decorators/2022-03-metadata/element/exec.js +++ b/tests/decorators/2022-03-metadata/element/exec.js @@ -1,13 +1,13 @@ function dec(_, ctx) { - console.error(ctx); - ctx.metadata.foo = 3; + console.error(ctx); + ctx.metadata.foo = 3; } Symbol.metadata = Symbol(); class A { - @dec - foo; + @dec + foo; } expect(A[Symbol.metadata]).toEqual({ foo: 3 }); diff --git a/tests/decorators/2022-03-metadata/subclass-no-super-decorators/exec.js b/tests/decorators/2022-03-metadata/subclass-no-super-decorators/exec.js index 3949427..f58c887 100644 --- a/tests/decorators/2022-03-metadata/subclass-no-super-decorators/exec.js +++ b/tests/decorators/2022-03-metadata/subclass-no-super-decorators/exec.js @@ -1,7 +1,7 @@ function dec(v) { - return (_, ctx) => { - ctx.metadata.foo = v; - }; + return (_, ctx) => { + ctx.metadata.foo = v; + }; } Symbol.metadata = Symbol(); diff --git a/tests/decorators/2022-03-metadata/subclass-super-decorators/exec.js b/tests/decorators/2022-03-metadata/subclass-super-decorators/exec.js index c77dbb2..f682338 100644 --- a/tests/decorators/2022-03-metadata/subclass-super-decorators/exec.js +++ b/tests/decorators/2022-03-metadata/subclass-super-decorators/exec.js @@ -1,7 +1,7 @@ function dec(v) { - return (_, ctx) => { - ctx.metadata.foo = v; - }; + return (_, ctx) => { + ctx.metadata.foo = v; + }; } Symbol.metadata = Symbol(); diff --git a/tests/decorators/2022-03-parameter/constructor/exec.js b/tests/decorators/2022-03-parameter/constructor/exec.js index 4cda948..0ece42e 100644 --- a/tests/decorators/2022-03-parameter/constructor/exec.js +++ b/tests/decorators/2022-03-parameter/constructor/exec.js @@ -1,20 +1,22 @@ function dec(_, ctx) { - expect(ctx.function.kind).toEqual('class'); - expect(ctx.function.name).toBeUndefined(); - ctx.metadata[Symbol.parameters] = {}; - ctx.metadata[Symbol.parameters][ctx.index] = { - rest: ctx.rest, - name: ctx.name, - foo: 5, - }; + expect(ctx.function.kind).toEqual('class'); + expect(ctx.function.name).toBeUndefined(); + ctx.metadata[Symbol.parameters] = {}; + ctx.metadata[Symbol.parameters][ctx.index] = { + rest: ctx.rest, + name: ctx.name, + foo: 5, + }; } Symbol.metadata = Symbol(); Symbol.parameters = Symbol(); class A { - constructor(@dec a) {} + constructor(@dec a) {} } -expect(A[Symbol.metadata][Symbol.parameters]).toEqual({ 0: { rest: false, name: 'a', foo: 5 } }); +expect(A[Symbol.metadata][Symbol.parameters]).toEqual({ + 0: { rest: false, name: 'a', foo: 5 }, +}); expect(Object.getPrototypeOf(A[Symbol.metadata])).toBe(null); diff --git a/tests/decorators/2022-03-parameter/method-computed-key-do-not-call-twice/exec.js b/tests/decorators/2022-03-parameter/method-computed-key-do-not-call-twice/exec.js index db7fa42..297ac2b 100644 --- a/tests/decorators/2022-03-parameter/method-computed-key-do-not-call-twice/exec.js +++ b/tests/decorators/2022-03-parameter/method-computed-key-do-not-call-twice/exec.js @@ -1,27 +1,29 @@ let calls = 0; function generateMethodName() { - return 'testMethod' + (++calls); + return 'testMethod' + ++calls; } function dec(_, ctx) { - expect(ctx.function.kind).toEqual('method'); - expect(ctx.function.name).toEqual('testMethod1'); - ctx.metadata[ctx.function.name] = {}; - ctx.metadata[ctx.function.name][Symbol.parameters] = {}; - ctx.metadata[ctx.function.name][Symbol.parameters][ctx.index] = { - rest: ctx.rest, - name: ctx.name, - foo: 5, - }; + expect(ctx.function.kind).toEqual('method'); + expect(ctx.function.name).toEqual('testMethod1'); + ctx.metadata[ctx.function.name] = {}; + ctx.metadata[ctx.function.name][Symbol.parameters] = {}; + ctx.metadata[ctx.function.name][Symbol.parameters][ctx.index] = { + rest: ctx.rest, + name: ctx.name, + foo: 5, + }; } Symbol.metadata = Symbol(); Symbol.parameters = Symbol(); class A { - [generateMethodName()](@dec a = 'x') {} + [generateMethodName()](@dec a = 'x') {} } expect(calls).toBe(1); -expect(A[Symbol.metadata].testMethod1[Symbol.parameters]).toEqual({ 0: { rest: false, name: undefined, foo: 5 } }); +expect(A[Symbol.metadata].testMethod1[Symbol.parameters]).toEqual({ + 0: { rest: false, name: undefined, foo: 5 }, +}); diff --git a/tests/decorators/2022-03-parameter/method-with-computed-key/exec.js b/tests/decorators/2022-03-parameter/method-with-computed-key/exec.js index d42f256..3104c21 100644 --- a/tests/decorators/2022-03-parameter/method-with-computed-key/exec.js +++ b/tests/decorators/2022-03-parameter/method-with-computed-key/exec.js @@ -1,22 +1,24 @@ const sym = Symbol(); function dec(_, ctx) { - expect(ctx.function.kind).toEqual('method'); - expect(ctx.function.name).toEqual(sym); - ctx.metadata[ctx.function.name] = {}; - ctx.metadata[ctx.function.name][Symbol.parameters] = {}; - ctx.metadata[ctx.function.name][Symbol.parameters][ctx.index] = { - rest: ctx.rest, - name: ctx.name, - foo: 5, - }; + expect(ctx.function.kind).toEqual('method'); + expect(ctx.function.name).toEqual(sym); + ctx.metadata[ctx.function.name] = {}; + ctx.metadata[ctx.function.name][Symbol.parameters] = {}; + ctx.metadata[ctx.function.name][Symbol.parameters][ctx.index] = { + rest: ctx.rest, + name: ctx.name, + foo: 5, + }; } Symbol.metadata = Symbol(); Symbol.parameters = Symbol(); class A { - [sym](@dec [a]) {} + [sym](@dec [a]) {} } -expect(A[Symbol.metadata][sym][Symbol.parameters]).toEqual({ 0: { rest: false, name: undefined, foo: 5 } }); +expect(A[Symbol.metadata][sym][Symbol.parameters]).toEqual({ + 0: { rest: false, name: undefined, foo: 5 }, +}); diff --git a/tests/wasm/debug.js b/tests/wasm/debug.js new file mode 100644 index 0000000..baa7e47 --- /dev/null +++ b/tests/wasm/debug.js @@ -0,0 +1,31 @@ +const { compile } = require('../..'); + +describe('Debug', () => { + it('should compile debug assertions correctly', () => { + const program = ` +function x() { + __assert(0 === 1); +} +`; + + const compiled = compile(program, null, { debug: true }); + expect(compiled).toEqual(`function x() { + __assert(0 === 1); +} +`); + }); + + it('should not compile debug assertions if debug flag is disabled', () => { + const program = ` +function x() { + __assert(0 === 1); +} +`; + + const compiled = compile(program, null, { debug: false }); + expect(compiled).toEqual(`function x() { + void 0; +} +`); + }); +}); diff --git a/tests/wasm/exports.js b/tests/wasm/exports.js new file mode 100644 index 0000000..2fa4846 --- /dev/null +++ b/tests/wasm/exports.js @@ -0,0 +1,33 @@ +const { compile } = require('../..'); + +describe('CommonJS', () => { + it('should compile exports correctly', () => { + const program = ` +export { x, y, z as ɵZ }; +`; + + const compiled = compile(program, null); + expect(compiled).toEqual(`"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +function _export(target, all) { + for(var name in all)Object.defineProperty(target, name, { + enumerable: true, + get: all[name] + }); +} +_export(exports, { + x: function() { + return x; + }, + y: function() { + return y; + }, + ɵZ: function() { + return z; + } +}); +`); + }); +}); diff --git a/tests/wasm/reflection.js b/tests/wasm/reflection.js new file mode 100644 index 0000000..3b0c81d --- /dev/null +++ b/tests/wasm/reflection.js @@ -0,0 +1,76 @@ +const { compile } = require('../..'); +const { runInThisContext } = require('node:vm'); + +describe('Reflection', () => { + it('should return class metadata', () => { + const { getReflectionData } = require('../../lib/reflection'); + + const program = ` +Symbol.metadata = Symbol(); +Symbol.parameters = Symbol(); + +const a = () => Symbol('test'); +const type = t => { + return function (value, context) { + const metadata = context.metadata; + metadata[context.function.name] = metadata[context.function.name] || {}; + metadata[context.function.name][Symbol.parameters] = metadata[context.function.name][Symbol.parameters] || {}; + metadata[context.function.name][Symbol.parameters][context.index] = metadata[context.function.name][Symbol.parameters][context.index] || {}; + metadata[context.function.name][Symbol.parameters][context.index].type = t; + }; +}; + +export default /** class docblock */ class x { + static #staticPrivateField; + #privateField; + accessor #privateAccessor; + static staticPublicField; + publicField; + accessor publicAccessor; + + /** + * computed method docblock + */ + [a()]() {} + #privateMethod(a, b = 1, [c, d], {f, g}) {} + + /** + * public method docblock + */ + publicMethod({a, b} = {}, c = new Object(), ...x) {} + static #staticPrivateMethod() {} + static staticPublicMethod() {} + + get [a()]() {} + set b(v) {} + + get #ap() {} + set #bp(v) {} + + act(@type(String) param1) {} + [a()](@type(String) param1) {} + [Symbol.for('xtest')](@type(String) param1) {} +} + +return x[Symbol.metadata].act[Symbol.parameters][0].type; +`; + + const compiled = compile(program, undefined, { debug: true }); + + const exports = {}; + const t = runInThisContext( + '(function(exports) {\n' + compiled + '\n})', + )(exports); + + expect(t).toStrictEqual(String); + + const data = getReflectionData(exports['default']); + const member = data.members.find((o) => o.name === 'publicMethod'); + + expect(member).not.toBeUndefined(); + expect(member.docblock).toEqual( + '/**\n * public method docblock\n */', + ); + expect(member.parameters).toHaveLength(3); + }); +}); diff --git a/tests/wasm/stack.js b/tests/wasm/stack.js new file mode 100644 index 0000000..1e8a1ce --- /dev/null +++ b/tests/wasm/stack.js @@ -0,0 +1,98 @@ +const { compile } = require('../..'); +const { runInNewContext } = require('node:vm'); + +describe('Error stack', () => { + it('should handle error stack correctly', () => { + const program = ` +class x { + constructor(shouldThrow = false) { + if (shouldThrow) { + throw new Error('Has to be thrown'); + } + } +} + +new x(); +new x(true); +`; + + const compiled = compile(program, 'x.js'); + try { + runInNewContext( + compiled, + { Symbol, __jymfony, __jymfony_reflect, _apply_decs_2203_r }, + { filename: 'x.js' }, + ); + throw new Error('FAIL'); + } catch (e) { + expect(e.stack).toContain(`x.js:11 + throw new Error('Has to be thrown'); + ^ + +Has to be thrown + + at new x (x.js:5:19) + at x.js:11:1`); + } + }); + + it('should read and rewrite multiple source map and handle error stack correctly', () => { + const program = ` +function x(shouldThrow = false) { + if (shouldThrow) { + throw new Error('Has to be thrown'); + } +} + +new x(); +new x(true); +`; + + const compiled = compile(program, 'x.ts'); + const recompiled = compile(compiled, 'x.ts'); + + try { + runInNewContext(recompiled, { Symbol }, { filename: 'x.ts' }); + throw new Error('FAIL'); + } catch (e) { + expect(e.stack).toContain(`x.ts:3 + throw new Error('Has to be thrown'); + ^ + +Has to be thrown + + at new x (x.ts:4:15) + at x.ts:9:1`); + } + }); + + it('should compile exports correctly', () => { + const program = ` +export { x, y, z as ɵZ }; +`; + + const compiled = compile(program, null); + expect(compiled).toEqual(`"use strict"; +Object.defineProperty(exports, "__esModule", { + value: true +}); +function _export(target, all) { + for(var name in all)Object.defineProperty(target, name, { + enumerable: true, + get: all[name] + }); +} +_export(exports, { + x: function() { + return x; + }, + y: function() { + return y; + }, + ɵZ: function() { + return z; + } +}); +`); + }); +}); diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index ef5f176..0000000 --- a/yarn.lock +++ /dev/null @@ -1,313 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.12.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" - integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== - dependencies: - "@babel/highlight" "^7.22.13" - chalk "^2.4.2" - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/highlight@^7.22.13": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" - integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - -"@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== - -"@types/istanbul-lib-report@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#412e0725ef41cde73bfa03e0e833eaff41e0fd63" - integrity sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.2.tgz#edc8e421991a3b4df875036d381fc0a5a982f549" - integrity sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/node@*": - version "20.8.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.6.tgz#0dbd4ebcc82ad0128df05d0e6f57e05359ee47fa" - integrity sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ== - dependencies: - undici-types "~5.25.1" - -"@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/yargs-parser@*": - version "21.0.1" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.1.tgz#07773d7160494d56aa882d7531aac7319ea67c3b" - integrity sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ== - -"@types/yargs@^16.0.0": - version "16.0.6" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.6.tgz#cc0c63684d68d23498cf0b5f32aa4c3fb437c638" - integrity sha512-oTP7/Q13GSPrgcwEwdlnkoZSQ1Hg9THe644qq8PG6hhJzjZ3qj1JjEFPIwWV/IXVs5XGIVqtkNOS9kh63WIJ+A== - dependencies: - "@types/yargs-parser" "*" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -braces@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -expect@^27.4.2: - version "27.5.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== - dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -graceful-fs@^4.2.9: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== - -jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== - dependencies: - chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^27.5.1" - slash "^3.0.0" - stack-utils "^2.0.3" - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== - dependencies: - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^17.0.1" - -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -stack-utils@^2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" - integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== - dependencies: - escape-string-regexp "^2.0.0" - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -undici-types@~5.25.1: - version "5.25.3" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.25.3.tgz#e044115914c85f0bcbb229f346ab739f064998c3" - integrity sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==