diff --git a/documentation/docs/develop/writing-functions.md b/documentation/docs/develop/writing-functions.md index da35083f..ef78918f 100644 --- a/documentation/docs/develop/writing-functions.md +++ b/documentation/docs/develop/writing-functions.md @@ -72,13 +72,17 @@ Functions can return any value that can be [(de)serialized](./data-sharing.md#au ### Arrow functions -Arrow functions are supported, but come with an important limitation. Jitar uses the function's `this` to pass context information (headers) for creating remote calls. Array functions do not have a `this` and can't provide any context. +Arrow functions are NOT supported by the segmentation system, but can be safely used inside a segment. ```ts -export const sayHello = (name: string) => { /* … */ }; +// src/domain/sayHello.ts +export const sayHello = async (name: string): Promise => +{ + return `Hello, ${name}!`; +}; ``` -This example works with the default Jitar setup, but will break when using [middleware](./middleware) that sets additional headers like authorization. To avoid this we recommend using normal functions in any situation. +Trying to segment this function results into an error a build level. ### Importing and calling diff --git a/documentation/docs/fundamentals/building-blocks.md b/documentation/docs/fundamentals/building-blocks.md index 385b50b1..597cac08 100644 --- a/documentation/docs/fundamentals/building-blocks.md +++ b/documentation/docs/fundamentals/building-blocks.md @@ -41,7 +41,7 @@ To ensure your application can be broken into segments and keeps working after d 1. Must be exported (named or as default) 1. Must be stateless / pure (don't depend on global values) -As long as you follow these rules, all will be fine. One thing to keep in mind is that arrow functions are supported, but can only be safely used in a non-distributed context. Meaning that the function does not call another function that might be in another segment. Besides that, we don't like to mix styles and recommend writing normal functions in any case. +As long as you follow these rules, all will be fine. One thing to keep in mind is that arrow functions are NOT supported for segmentation. Besides that, we don't like to mix styles and recommend writing normal functions in any case. More in depth information on writing functions and the rules can be found in the [DEVELOP section](../develop/writing-functions). diff --git a/documentation/docs/integrate/vite-plugin.md b/documentation/docs/integrate/vite-plugin.md index 9d487b4f..4db2ba91 100644 --- a/documentation/docs/integrate/vite-plugin.md +++ b/documentation/docs/integrate/vite-plugin.md @@ -43,9 +43,10 @@ const JITAR_SEGMENTS = ['frontend']; const JITAR_MIDDLEWARES = ['./requesterMiddleware']; const jitarConfig: JitarConfig = { - sourceDir: 'src', - targetDir: 'dist', - jitarDir: 'domain', + projectRoot: '../../', + sourceRoot: '../', + configurationFile: './jitar.json', + environmentFile: './dev.env', jitarUrl: JITAR_URL, segments: JITAR_SEGMENTS, middleware: JITAR_MIDDLEWARES @@ -63,11 +64,12 @@ export default defineConfig({ }); ``` -The plugin configuration has 6 parameters: +The plugin configuration has 5 parameters: -1. sourceDir - The directory of the app source files. In most cases this is the `src` folder. -1. targetDir - The directory of the output folder of the Vite build. This is the folder that Jitar uses as input for creating its cache. -1. jitarDir - The directory of the source files used by Jitar. This path is relative to the source root. We like to use `domain` (which points to `src/domain`), but feel free to use something else. +1. projectRoot - The root directory of the the project relative to the Vite configuration. +1. sourceRoot - The source directory relative to the Vite configuration. +1. configurationFile - The Jitar configuration file relative to the project root. Jitar uses `./jitar.json` by default. +1. environmentFile - The environment file relative to the project root. Is only loaded when configured. 1. jitarUrl - The URL of the Jitar instance. Jitar uses by default `http://localhost:3000`, but can be configured differently. 1. segments - The segments to use for the client app. This is an array of strings. The default is an empty array. 1. middlewares - The middlewares to use for calling remote procedures. This is an array of strings. The default is an empty array. diff --git a/examples/access-protection/tsconfig.json b/examples/access-protection/tsconfig.json index 9d1aec07..db3953f7 100644 --- a/examples/access-protection/tsconfig.json +++ b/examples/access-protection/tsconfig.json @@ -1,19 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "cache", - "dist", - "node_modules", - "segments" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/examples/data-transportation/tsconfig.json b/examples/data-transportation/tsconfig.json index 9d1aec07..db3953f7 100644 --- a/examples/data-transportation/tsconfig.json +++ b/examples/data-transportation/tsconfig.json @@ -1,19 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "cache", - "dist", - "node_modules", - "segments" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/examples/error-handling/tsconfig.json b/examples/error-handling/tsconfig.json index 9d1aec07..db3953f7 100644 --- a/examples/error-handling/tsconfig.json +++ b/examples/error-handling/tsconfig.json @@ -1,19 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "cache", - "dist", - "node_modules", - "segments" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/examples/health-checks/tsconfig.json b/examples/health-checks/tsconfig.json index 387be42e..db3953f7 100644 --- a/examples/health-checks/tsconfig.json +++ b/examples/health-checks/tsconfig.json @@ -1,15 +1,9 @@ { + "extends": "../../tsconfig.json", "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": ["cache", "dist", "node_modules"] -} + "include": ["./src"] +} \ No newline at end of file diff --git a/examples/hello-world/tsconfig.json b/examples/hello-world/tsconfig.json index 9d1aec07..db3953f7 100644 --- a/examples/hello-world/tsconfig.json +++ b/examples/hello-world/tsconfig.json @@ -1,19 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "cache", - "dist", - "node_modules", - "segments" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/examples/load-balancing/tsconfig.json b/examples/load-balancing/tsconfig.json index 9d1aec07..db3953f7 100644 --- a/examples/load-balancing/tsconfig.json +++ b/examples/load-balancing/tsconfig.json @@ -1,19 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "cache", - "dist", - "node_modules", - "segments" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/examples/middleware/tsconfig.json b/examples/middleware/tsconfig.json index 387be42e..db3953f7 100644 --- a/examples/middleware/tsconfig.json +++ b/examples/middleware/tsconfig.json @@ -1,15 +1,9 @@ { + "extends": "../../tsconfig.json", "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": ["cache", "dist", "node_modules"] -} + "include": ["./src"] +} \ No newline at end of file diff --git a/examples/multi-version/tsconfig.json b/examples/multi-version/tsconfig.json index 9d1aec07..db3953f7 100644 --- a/examples/multi-version/tsconfig.json +++ b/examples/multi-version/tsconfig.json @@ -1,19 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "cache", - "dist", - "node_modules", - "segments" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/examples/resources/tsconfig.json b/examples/resources/tsconfig.json index 9d1aec07..db3953f7 100644 --- a/examples/resources/tsconfig.json +++ b/examples/resources/tsconfig.json @@ -1,19 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "cache", - "dist", - "node_modules", - "segments" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/examples/segmentation/tsconfig.json b/examples/segmentation/tsconfig.json index 9d1aec07..db3953f7 100644 --- a/examples/segmentation/tsconfig.json +++ b/examples/segmentation/tsconfig.json @@ -1,19 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "cache", - "dist", - "node_modules", - "segments" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 877949cb..4ac98a33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ ], "devDependencies": { "@eslint/js": "^10.0.1", + "@types/node": "^25.6.2", "@typescript-eslint/eslint-plugin": "^8.57.0", "@vitest/coverage-v8": "^4.1.0", "auto-changelog": "^2.5.0", @@ -78,9 +79,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "dev": true, "license": "MIT", "dependencies": { @@ -118,46 +119,43 @@ } }, "node_modules/@emnapi/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", - "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.0", + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", - "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", - "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", - "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", "cpu": [ "ppc64" ], @@ -172,9 +170,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", - "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", "cpu": [ "arm" ], @@ -189,9 +187,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", - "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", "cpu": [ "arm64" ], @@ -206,9 +204,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", - "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", "cpu": [ "x64" ], @@ -223,9 +221,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", - "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", "cpu": [ "arm64" ], @@ -240,9 +238,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", - "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", "cpu": [ "x64" ], @@ -257,9 +255,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", - "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", "cpu": [ "arm64" ], @@ -274,9 +272,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", - "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", "cpu": [ "x64" ], @@ -291,9 +289,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", - "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", "cpu": [ "arm" ], @@ -308,9 +306,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", - "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", "cpu": [ "arm64" ], @@ -325,9 +323,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", - "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", "cpu": [ "ia32" ], @@ -342,9 +340,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", - "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", "cpu": [ "loong64" ], @@ -359,9 +357,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", - "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", "cpu": [ "mips64el" ], @@ -376,9 +374,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", - "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", "cpu": [ "ppc64" ], @@ -393,9 +391,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", - "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", "cpu": [ "riscv64" ], @@ -410,9 +408,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", - "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", "cpu": [ "s390x" ], @@ -427,9 +425,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", - "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", "cpu": [ "x64" ], @@ -444,9 +442,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", - "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", "cpu": [ "arm64" ], @@ -461,9 +459,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", - "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", "cpu": [ "x64" ], @@ -478,9 +476,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", - "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", "cpu": [ "arm64" ], @@ -495,9 +493,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", - "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", "cpu": [ "x64" ], @@ -512,9 +510,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", - "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", "cpu": [ "arm64" ], @@ -529,9 +527,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", - "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", "cpu": [ "x64" ], @@ -546,9 +544,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", - "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", "cpu": [ "arm64" ], @@ -563,9 +561,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", - "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", "cpu": [ "ia32" ], @@ -580,9 +578,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", - "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", "cpu": [ "x64" ], @@ -626,13 +624,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.23.3", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.3.tgz", - "integrity": "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==", + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^3.0.3", + "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", "minimatch": "^10.2.4" }, @@ -641,22 +639,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.3.tgz", - "integrity": "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.1" + "@eslint/core": "^1.2.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", - "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -688,9 +686,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.3.tgz", - "integrity": "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -698,13 +696,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", - "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.1", + "@eslint/core": "^1.2.1", "levn": "^0.4.1" }, "engines": { @@ -712,29 +710,43 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -886,9 +898,9 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", - "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", "optional": true, @@ -911,6 +923,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "optional": true, "os": [ "linux" @@ -920,9 +935,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "version": "0.128.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz", + "integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==", "dev": true, "license": "MIT", "funding": { @@ -930,9 +945,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==", "cpu": [ "arm64" ], @@ -947,9 +962,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==", "cpu": [ "arm64" ], @@ -964,9 +979,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.18.tgz", + "integrity": "sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==", "cpu": [ "x64" ], @@ -981,9 +996,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.18.tgz", + "integrity": "sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==", "cpu": [ "x64" ], @@ -998,9 +1013,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.18.tgz", + "integrity": "sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==", "cpu": [ "arm" ], @@ -1015,13 +1030,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1032,13 +1050,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.18.tgz", + "integrity": "sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1049,13 +1070,16 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1066,13 +1090,16 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1083,13 +1110,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1100,13 +1130,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.18.tgz", + "integrity": "sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1117,9 +1150,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==", "cpu": [ "arm64" ], @@ -1134,9 +1167,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.18.tgz", + "integrity": "sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==", "cpu": [ "wasm32" ], @@ -1144,16 +1177,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { - "node": ">=14.0.0" + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.18.tgz", + "integrity": "sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==", "cpu": [ "arm64" ], @@ -1168,9 +1203,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.18.tgz", + "integrity": "sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==", "cpu": [ "x64" ], @@ -1185,9 +1220,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz", + "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==", "dev": true, "license": "MIT" }, @@ -1319,9 +1354,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", - "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", "cpu": [ "arm" ], @@ -1333,9 +1368,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", - "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", "cpu": [ "arm64" ], @@ -1347,9 +1382,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", - "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", "cpu": [ "arm64" ], @@ -1361,9 +1396,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", - "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", "cpu": [ "x64" ], @@ -1375,9 +1410,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", - "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", "cpu": [ "arm64" ], @@ -1389,9 +1424,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", - "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", "cpu": [ "x64" ], @@ -1403,13 +1438,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", - "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1417,13 +1455,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", - "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1431,13 +1472,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", - "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1445,13 +1489,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", - "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1459,13 +1506,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", - "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1473,13 +1523,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", - "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1487,13 +1540,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", - "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1501,13 +1557,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", - "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1515,13 +1574,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", - "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1529,13 +1591,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", - "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1543,13 +1608,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", - "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1557,13 +1625,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", - "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1571,13 +1642,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", - "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1585,9 +1659,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", - "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", "cpu": [ "x64" ], @@ -1599,9 +1673,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", - "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", "cpu": [ "arm64" ], @@ -1613,9 +1687,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", - "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", "cpu": [ "arm64" ], @@ -1627,9 +1701,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", - "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", "cpu": [ "ia32" ], @@ -1641,9 +1715,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", - "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", "cpu": [ "x64" ], @@ -1655,9 +1729,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", - "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", "cpu": [ "x64" ], @@ -1676,9 +1750,9 @@ "license": "MIT" }, "node_modules/@turbo/darwin-64": { - "version": "2.8.21", - "resolved": "https://registry.npmjs.org/@turbo/darwin-64/-/darwin-64-2.8.21.tgz", - "integrity": "sha512-kfGoM0Iw8ZNZpbds+4IzOe0hjvHldqJwUPRAjXJi3KBxg/QOZL95N893SRoMtf2aJ+jJ3dk32yPkp8rvcIjP9g==", + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/@turbo/darwin-64/-/darwin-64-2.9.10.tgz", + "integrity": "sha512-5BVJnes8/zMPydF8ktfBBWqCCpUeWVxwZ6avYHRqLzk2PuTAsLz0TlaKdDe1nk1cz3/o0c+7CEf6zqNXdB2N7Q==", "cpu": [ "x64" ], @@ -1690,9 +1764,9 @@ ] }, "node_modules/@turbo/darwin-arm64": { - "version": "2.8.21", - "resolved": "https://registry.npmjs.org/@turbo/darwin-arm64/-/darwin-arm64-2.8.21.tgz", - "integrity": "sha512-o9HEflxUEyr987x0cTUzZBhDOyL6u95JmdmlkH2VyxAw7zq2sdtM5e72y9ufv2N5SIoOBw1fVn9UES5VY5H6vQ==", + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/@turbo/darwin-arm64/-/darwin-arm64-2.9.10.tgz", + "integrity": "sha512-MwaJl+vsxlkkDJYWN87GWKYD64kOvZFFlrDtGmCDeEx/488Kola5TVgS0VQkEUwnbDPjaDIB7kBMIzdzJRElbg==", "cpu": [ "arm64" ], @@ -1704,9 +1778,9 @@ ] }, "node_modules/@turbo/linux-64": { - "version": "2.8.21", - "resolved": "https://registry.npmjs.org/@turbo/linux-64/-/linux-64-2.8.21.tgz", - "integrity": "sha512-uTxlCcXWy5h1fSSymP8XSJ+AudzEHMDV3IDfKX7+DGB8kgJ+SLoTUAH7z4OFA7I/l2sznz0upPdbNNZs91YMag==", + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/@turbo/linux-64/-/linux-64-2.9.10.tgz", + "integrity": "sha512-HWrCKR+kUicEf4awj1EVRh5LI2hiDncpWZSXqhVj+wQT3SolBSXqXiSPYHgdh6hkmyfvz75Ex/+axXGcgQGdeA==", "cpu": [ "x64" ], @@ -1718,9 +1792,9 @@ ] }, "node_modules/@turbo/linux-arm64": { - "version": "2.8.21", - "resolved": "https://registry.npmjs.org/@turbo/linux-arm64/-/linux-arm64-2.8.21.tgz", - "integrity": "sha512-cdHIcxNcihHHkCHp0Y4Zb60K4Qz+CK4xw1gb6s/t/9o4SMeMj+hTBCtoW6QpPnl9xPYmxuTou8Zw6+cylTnREg==", + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/@turbo/linux-arm64/-/linux-arm64-2.9.10.tgz", + "integrity": "sha512-edJINoZcDn4g1zkOZHFrGtLX0EWTryoNJSlZK5SEVux4hITT6hTCzugVGtOUmdI+PuJ/xRRL3jKGa+JgjSoq5A==", "cpu": [ "arm64" ], @@ -1732,9 +1806,9 @@ ] }, "node_modules/@turbo/windows-64": { - "version": "2.8.21", - "resolved": "https://registry.npmjs.org/@turbo/windows-64/-/windows-64-2.8.21.tgz", - "integrity": "sha512-/iBj4OzbqEY8CX+eaeKbBTMZv2CLXNrt0692F7HnK7LcyYwyDecaAiSET6ZzL4opT7sbwkKvzAC/fhqT3Quu1A==", + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/@turbo/windows-64/-/windows-64-2.9.10.tgz", + "integrity": "sha512-rkASn89ATUtSyKvhGaWSyqVHBwtqFEUV1rFNKCtthSoix0T/kUHLJDKpepE/Wh6CtSnhxAfsY8cODtevD/hR7A==", "cpu": [ "x64" ], @@ -1746,9 +1820,9 @@ ] }, "node_modules/@turbo/windows-arm64": { - "version": "2.8.21", - "resolved": "https://registry.npmjs.org/@turbo/windows-arm64/-/windows-arm64-2.8.21.tgz", - "integrity": "sha512-95tMA/ZbIidJFUUtkmqioQ1gf3n3I1YbRP3ZgVdWTVn2qVbkodcIdGXBKRHHrIbRsLRl99SiHi/L7IxhpZDagQ==", + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/@turbo/windows-arm64/-/windows-arm64-2.9.10.tgz", + "integrity": "sha512-cblXqub7uABXKNMzvPB1IyOuSQpeMo7zZHSREB2C0mtIVn4lUSd2CfaGBtOrDqmkC9dsan3itxY4IejChQvfpg==", "cpu": [ "arm64" ], @@ -1760,9 +1834,9 @@ ] }, "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", "dev": true, "license": "MIT", "optional": true, @@ -1817,9 +1891,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -1870,19 +1944,19 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "version": "25.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.2.tgz", + "integrity": "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "undici-types": "~7.19.0" } }, "node_modules/@types/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", "dev": true, "license": "MIT" }, @@ -1922,20 +1996,20 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", - "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz", + "integrity": "sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/type-utils": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/type-utils": "8.59.2", + "@typescript-eslint/utils": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1945,22 +2019,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.59.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", - "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.2.tgz", + "integrity": "sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", "debug": "^4.4.3" }, "engines": { @@ -1972,18 +2046,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", - "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.2.tgz", + "integrity": "sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.2", - "@typescript-eslint/types": "^8.57.2", + "@typescript-eslint/tsconfig-utils": "^8.59.2", + "@typescript-eslint/types": "^8.59.2", "debug": "^4.4.3" }, "engines": { @@ -1994,18 +2068,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz", + "integrity": "sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2016,9 +2090,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", - "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz", + "integrity": "sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==", "dev": true, "license": "MIT", "engines": { @@ -2029,21 +2103,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", - "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz", + "integrity": "sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2", "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2054,13 +2128,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.2.tgz", + "integrity": "sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==", "dev": true, "license": "MIT", "engines": { @@ -2072,21 +2146,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz", + "integrity": "sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/project-service": "8.59.2", + "@typescript-eslint/tsconfig-utils": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/visitor-keys": "8.59.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2096,20 +2170,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.2.tgz", + "integrity": "sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "@typescript-eslint/scope-manager": "8.59.2", + "@typescript-eslint/types": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2120,17 +2194,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz", + "integrity": "sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.59.2", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2155,14 +2229,14 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.2.tgz", - "integrity": "sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.5.tgz", + "integrity": "sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.2", + "@vitest/utils": "4.1.5", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -2176,8 +2250,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.2", - "vitest": "4.1.2" + "@vitest/browser": "4.1.5", + "vitest": "4.1.5" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -2186,16 +2260,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", - "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -2204,13 +2278,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", - "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.2", + "@vitest/spy": "4.1.5", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -2231,9 +2305,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", - "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", "dev": true, "license": "MIT", "dependencies": { @@ -2244,13 +2318,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", - "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.2", + "@vitest/utils": "4.1.5", "pathe": "^2.0.3" }, "funding": { @@ -2258,14 +2332,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", - "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -2274,9 +2348,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", - "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", "dev": true, "license": "MIT", "funding": { @@ -2284,13 +2358,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", - "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.2", + "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -2335,9 +2409,9 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -2374,24 +2448,23 @@ } }, "node_modules/auto-changelog": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.5.0.tgz", - "integrity": "sha512-UTnLjT7I9U2U/xkCUH5buDlp8C7g0SGChfib+iDrJkamcj5kaMqNKHNfbKJw1kthJUq8sUo3i3q2S6FzO/l/wA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.5.1.tgz", + "integrity": "sha512-vpG10KkmGaeMHwvCK2G+4j6e1+ZxVnhDadHnbG0sFuiB2fzAPIybicp5782btmf4+UGfHklVCUcHWZyN3YiXKg==", "dev": true, "license": "MIT", "dependencies": { "commander": "^7.2.0", - "handlebars": "^4.7.7", + "handlebars": "^4.7.9", "import-cwd": "^3.0.0", - "node-fetch": "^2.6.1", - "parse-github-url": "^1.0.3", - "semver": "^7.3.5" + "parse-github-url": "^1.0.4", + "semver": "^7.7.4" }, "bin": { "auto-changelog": "src/index.js" }, "engines": { - "node": ">=8.3" + "node": ">= 10" } }, "node_modules/balanced-match": { @@ -2428,9 +2501,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -2505,9 +2578,9 @@ } }, "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -2683,9 +2756,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT" }, @@ -2702,9 +2775,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", - "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2715,32 +2788,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.4", - "@esbuild/android-arm": "0.27.4", - "@esbuild/android-arm64": "0.27.4", - "@esbuild/android-x64": "0.27.4", - "@esbuild/darwin-arm64": "0.27.4", - "@esbuild/darwin-x64": "0.27.4", - "@esbuild/freebsd-arm64": "0.27.4", - "@esbuild/freebsd-x64": "0.27.4", - "@esbuild/linux-arm": "0.27.4", - "@esbuild/linux-arm64": "0.27.4", - "@esbuild/linux-ia32": "0.27.4", - "@esbuild/linux-loong64": "0.27.4", - "@esbuild/linux-mips64el": "0.27.4", - "@esbuild/linux-ppc64": "0.27.4", - "@esbuild/linux-riscv64": "0.27.4", - "@esbuild/linux-s390x": "0.27.4", - "@esbuild/linux-x64": "0.27.4", - "@esbuild/netbsd-arm64": "0.27.4", - "@esbuild/netbsd-x64": "0.27.4", - "@esbuild/openbsd-arm64": "0.27.4", - "@esbuild/openbsd-x64": "0.27.4", - "@esbuild/openharmony-arm64": "0.27.4", - "@esbuild/sunos-x64": "0.27.4", - "@esbuild/win32-arm64": "0.27.4", - "@esbuild/win32-ia32": "0.27.4", - "@esbuild/win32-x64": "0.27.4" + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" } }, "node_modules/escape-html": { @@ -2763,18 +2836,18 @@ } }, "node_modules/eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.1.0.tgz", - "integrity": "sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.3", - "@eslint/config-helpers": "^0.5.3", - "@eslint/core": "^1.1.1", - "@eslint/plugin-kit": "^0.6.1", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -3303,9 +3376,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3419,13 +3492,13 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -3715,6 +3788,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3736,6 +3812,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3757,6 +3836,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3778,6 +3860,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -3850,9 +3935,9 @@ } }, "node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -3952,12 +4037,12 @@ } }, "node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -3992,9 +4077,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -4033,27 +4118,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -4156,9 +4220,9 @@ "license": "BlueOak-1.0.0" }, "node_modules/parse-github-url": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.3.tgz", - "integrity": "sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.4.tgz", + "integrity": "sha512-CEtCOt55fHmd6DpBc/N7H5NC4vJpcquhzzs9Iw2mRj8bVxo1O5TQI5MXKOMO7+yBOqD+5dKCCRK4Kj1KskZc6Q==", "dev": true, "license": "MIT", "bin": { @@ -4221,9 +4285,9 @@ } }, "node_modules/path-to-regexp": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.0.tgz", - "integrity": "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", @@ -4258,9 +4322,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "dev": true, "funding": [ { @@ -4320,9 +4384,9 @@ } }, "node_modules/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -4359,12 +4423,13 @@ } }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, "license": "MIT", "dependencies": { + "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" @@ -4410,14 +4475,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz", + "integrity": "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" + "@oxc-project/types": "=0.128.0", + "@rolldown/pluginutils": "1.0.0-rc.18" }, "bin": { "rolldown": "bin/cli.mjs" @@ -4426,27 +4491,27 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + "@rolldown/binding-android-arm64": "1.0.0-rc.18", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.18", + "@rolldown/binding-darwin-x64": "1.0.0-rc.18", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.18", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.18", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.18", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.18", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18" } }, "node_modules/rollup": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", - "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", "dev": true, "license": "MIT", "dependencies": { @@ -4460,31 +4525,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.0", - "@rollup/rollup-android-arm64": "4.60.0", - "@rollup/rollup-darwin-arm64": "4.60.0", - "@rollup/rollup-darwin-x64": "4.60.0", - "@rollup/rollup-freebsd-arm64": "4.60.0", - "@rollup/rollup-freebsd-x64": "4.60.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", - "@rollup/rollup-linux-arm-musleabihf": "4.60.0", - "@rollup/rollup-linux-arm64-gnu": "4.60.0", - "@rollup/rollup-linux-arm64-musl": "4.60.0", - "@rollup/rollup-linux-loong64-gnu": "4.60.0", - "@rollup/rollup-linux-loong64-musl": "4.60.0", - "@rollup/rollup-linux-ppc64-gnu": "4.60.0", - "@rollup/rollup-linux-ppc64-musl": "4.60.0", - "@rollup/rollup-linux-riscv64-gnu": "4.60.0", - "@rollup/rollup-linux-riscv64-musl": "4.60.0", - "@rollup/rollup-linux-s390x-gnu": "4.60.0", - "@rollup/rollup-linux-x64-gnu": "4.60.0", - "@rollup/rollup-linux-x64-musl": "4.60.0", - "@rollup/rollup-openbsd-x64": "4.60.0", - "@rollup/rollup-openharmony-arm64": "4.60.0", - "@rollup/rollup-win32-arm64-msvc": "4.60.0", - "@rollup/rollup-win32-ia32-msvc": "4.60.0", - "@rollup/rollup-win32-x64-gnu": "4.60.0", - "@rollup/rollup-win32-x64-msvc": "4.60.0", + "@rollup/rollup-android-arm-eabi": "4.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "@rollup/rollup-linux-arm-musleabihf": "4.60.3", + "@rollup/rollup-linux-arm64-gnu": "4.60.3", + "@rollup/rollup-linux-arm64-musl": "4.60.3", + "@rollup/rollup-linux-loong64-gnu": "4.60.3", + "@rollup/rollup-linux-loong64-musl": "4.60.3", + "@rollup/rollup-linux-ppc64-gnu": "4.60.3", + "@rollup/rollup-linux-ppc64-musl": "4.60.3", + "@rollup/rollup-linux-riscv64-gnu": "4.60.3", + "@rollup/rollup-linux-riscv64-musl": "4.60.3", + "@rollup/rollup-linux-s390x-gnu": "4.60.3", + "@rollup/rollup-linux-x64-gnu": "4.60.3", + "@rollup/rollup-linux-x64-musl": "4.60.3", + "@rollup/rollup-openbsd-x64": "4.60.3", + "@rollup/rollup-openharmony-arm64": "4.60.3", + "@rollup/rollup-win32-arm64-msvc": "4.60.3", + "@rollup/rollup-win32-ia32-msvc": "4.60.3", + "@rollup/rollup-win32-x64-gnu": "4.60.3", + "@rollup/rollup-win32-x64-msvc": "4.60.3", "fsevents": "~2.3.2" } }, @@ -4511,6 +4576,13 @@ "typescript": "^4.5 || ^5.0" } }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -4650,13 +4722,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -4767,9 +4839,9 @@ } }, "node_modules/std-env": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", - "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, @@ -4800,9 +4872,9 @@ } }, "node_modules/terser": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", - "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", + "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4833,9 +4905,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", - "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", "dev": true, "license": "MIT", "engines": { @@ -4843,14 +4915,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -4878,13 +4950,6 @@ "node": ">=0.6" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -4906,21 +4971,21 @@ "license": "0BSD" }, "node_modules/turbo": { - "version": "2.8.21", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.8.21.tgz", - "integrity": "sha512-FlJ8OD5Qcp0jTAM7E4a/RhUzRNds2GzKlyxHKA6N247VLy628rrxAGlMpIXSz6VB430+TiQDJ/SMl6PL1lu6wQ==", + "version": "2.9.10", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.9.10.tgz", + "integrity": "sha512-YBLeNT0wLoysGgQEkvBWE2GA1liGGZ1j13wa7xHTwELJx4ZhM+c2szeXj6wUOUGO86BmyhY0Q/ELWwU3WDXzZA==", "dev": true, "license": "MIT", "bin": { "turbo": "bin/turbo" }, "optionalDependencies": { - "@turbo/darwin-64": "2.8.21", - "@turbo/darwin-arm64": "2.8.21", - "@turbo/linux-64": "2.8.21", - "@turbo/linux-arm64": "2.8.21", - "@turbo/windows-64": "2.8.21", - "@turbo/windows-arm64": "2.8.21" + "@turbo/darwin-64": "2.9.10", + "@turbo/darwin-arm64": "2.9.10", + "@turbo/linux-64": "2.9.10", + "@turbo/linux-arm64": "2.9.10", + "@turbo/windows-64": "2.9.10", + "@turbo/windows-arm64": "2.9.10" } }, "node_modules/type-check": { @@ -4965,16 +5030,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", - "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", + "version": "8.59.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.2.tgz", + "integrity": "sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.2", - "@typescript-eslint/parser": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2" + "@typescript-eslint/eslint-plugin": "8.59.2", + "@typescript-eslint/parser": "8.59.2", + "@typescript-eslint/typescript-estree": "8.59.2", + "@typescript-eslint/utils": "8.59.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4985,7 +5050,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/uglify-js": { @@ -5003,9 +5068,9 @@ } }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "dev": true, "license": "MIT" }, @@ -5038,17 +5103,17 @@ } }, "node_modules/vite": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", - "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.11.tgz", + "integrity": "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", - "tinyglobby": "^0.2.15" + "postcss": "^8.5.14", + "rolldown": "1.0.0-rc.18", + "tinyglobby": "^0.2.16" }, "bin": { "vite": "bin/vite.js" @@ -5064,8 +5129,8 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", @@ -5116,19 +5181,19 @@ } }, "node_modules/vitest": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", - "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.2", - "@vitest/mocker": "4.1.2", - "@vitest/pretty-format": "4.1.2", - "@vitest/runner": "4.1.2", - "@vitest/snapshot": "4.1.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -5156,10 +5221,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.2", - "@vitest/browser-preview": "4.1.2", - "@vitest/browser-webdriverio": "4.1.2", - "@vitest/ui": "4.1.2", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -5183,6 +5250,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, @@ -5197,24 +5270,6 @@ } } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5504,6 +5559,9 @@ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5518,6 +5576,9 @@ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5532,6 +5593,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5546,6 +5610,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5560,6 +5627,9 @@ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5574,6 +5644,9 @@ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5588,6 +5661,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5602,6 +5678,9 @@ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5616,6 +5695,9 @@ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5630,6 +5712,9 @@ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5644,6 +5729,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5658,6 +5746,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5672,6 +5763,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5762,6 +5856,13 @@ "win32" ] }, + "packages/jitar/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, "packages/jitar/node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", @@ -5829,6 +5930,7 @@ "vite": "^7.3.0" }, "peerDependencies": { + "jitar": "^0.10.7", "vite": ">=4.0.0 || >=5.0.0 || >=6.0.0 || >=7.0.0" }, "peerDependenciesMeta": { @@ -5838,9 +5940,9 @@ } }, "packages/plugin-vite/node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index fae26979..82af200e 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", + "@types/node": "^25.6.2", "@typescript-eslint/eslint-plugin": "^8.57.0", "@vitest/coverage-v8": "^4.1.0", "auto-changelog": "^2.5.0", diff --git a/packages/analysis/README.md b/packages/analysis/README.md index 447c26a1..2fc7d39c 100644 --- a/packages/analysis/README.md +++ b/packages/analysis/README.md @@ -60,13 +60,7 @@ const { a, b: c, ...others } = myObject; ``` ```ts -// Nested destructuring is not supported (will be supported) -const [ a, [ b = 42, c, d ] ] = myArray; -const { a: { c, d = true }, b = 42 } = myObject; -``` - -```ts -// Dynamic property destructuring is not supported (won't be supported) +// Dynamic property destructuring is not supported (will be supported) const [ [a]: b ] = myArray; const { [a]: b } = myObject; ``` diff --git a/packages/analysis/src/dynamic/ClassMerger.ts b/packages/analysis/src/dynamic/ClassMerger.ts index 2efc9e37..83aba10e 100644 --- a/packages/analysis/src/dynamic/ClassMerger.ts +++ b/packages/analysis/src/dynamic/ClassMerger.ts @@ -1,37 +1,28 @@ -import { ESClass, ESDeclaration, ESFunction, ESScope } from '../models'; +import { ESClass, ESClassMember } from '../model'; export default class ClassMerger { merge(model: ESClass, parent: ESClass): ESClass { - const declarations = this.#mergeDeclarations(model.declarations, parent.declarations); - const functions = this.#mergeFunctions(model.functions, parent.functions); - const getters = this.#mergeFunctions(model.getters, parent.getters); - const setters = this.#mergeFunctions(model.setters, parent.setters); + const constructors = model.construct !== undefined ? [model.construct] : []; + const fields = this.#mergeMembers(model.fields, parent.fields); + const methods = this.#mergeMembers(model.methods, parent.methods); + const getters = this.#mergeMembers(model.getters, parent.getters); + const setters = this.#mergeMembers(model.setters, parent.setters); - const members = [...declarations.values(), ...functions.values(), ...getters.values(), ...setters.values()]; + const members = [...fields, ...constructors, ...methods, ...getters, ...setters]; - return new ESClass(model.name, parent.name, new ESScope(members)); + return new ESClass(model.identifier, parent.identifier, members); } - #mergeDeclarations(model: ESDeclaration[], parent: ESDeclaration[]): ESDeclaration[] + #mergeMembers(model: T[], parent: T[]): T[] { - const declarations = new Map(); + const members = new Map(); - parent.forEach(declaration => declarations.set(declaration.name, declaration)); - model.forEach(declaration => declarations.set(declaration.name, declaration)); + parent.forEach(member => members.set(member.identifier, member)); + model.forEach(member => members.set(member.identifier, member)); - return [...declarations.values()]; - } - - #mergeFunctions(model: ESFunction[], parent: ESFunction[]): ESFunction[] - { - const functions = new Map(); - - parent.forEach(funktion => functions.set(funktion.name, funktion)); - model.forEach(funktion => functions.set(funktion.name, funktion)); - - return [...functions.values()]; + return [...members.values()]; } } diff --git a/packages/analysis/src/dynamic/Reflector.ts b/packages/analysis/src/dynamic/Reflector.ts index 787dbb51..d4eacb18 100644 --- a/packages/analysis/src/dynamic/Reflector.ts +++ b/packages/analysis/src/dynamic/Reflector.ts @@ -1,5 +1,5 @@ -import { ESClass, ESDeclaration, ESExpression, ESFunction, ESGetter, ESMember, ESModule, ESScope, ESSetter, ESValue } from '../models'; +import { ESClass, ESClassMember, ESVariable, ESExpression, ESFunction, ESField, ESMethod, ESGetter, ESModule, ESSetter, ESConstructor, ESIdentifierBinding } from '../model'; import { Parser } from '../static'; import ClassMerger from './ClassMerger'; @@ -33,20 +33,21 @@ export default class Reflector } else { + const binding = new ESIdentifierBinding(key, false); const expression = new ESExpression(code); - members.push(new ESDeclaration(key, expression)); + members.push(new ESVariable('var', binding, expression)); } } - return new ESModule(new ESScope(members)); + return new ESModule(members); } fromClass(clazz: Function, inherit = false): ESClass { const model = this.isClass(clazz) - ? this.#reflectStatic(clazz) - : this.#reflectDynamic(clazz); + ? this.#reflectClassStatic(clazz) + : this.#reflectClassDynamic(clazz); if (inherit === false) { @@ -116,42 +117,41 @@ export default class Reflector || clazz.toString().startsWith('async function'); } - #reflectStatic(clazz: Function): ESClass + #reflectClassStatic(clazz: Function): ESClass { const code = clazz.toString(); return this.#parser.parseClass(code); } - #reflectDynamic(clazz: Function): ESClass + #reflectClassDynamic(clazz: Function): ESClass { - const object = this.createInstance(clazz); - const members = this.#getMembers(clazz, object); - const scope = new ESScope(members); + const instance = this.createInstance(clazz); + const members = this.#getClassMembers(clazz, instance); - return new ESClass(clazz.name, undefined, scope); + return new ESClass(clazz.name, undefined, members); } - #getMembers(clazz: Function, object: object): ESMember[] + #getClassMembers(clazz: Function, instance: object): ESClassMember[] { - const declarations = this.#getDeclarations(object); - const functions = this.#getFunctions(clazz); + const fields = this.#getClassFields(instance); + const methods = this.#getClassMethods(clazz); - return [...declarations, ...functions]; + return [...fields, ...methods]; } - #getDeclarations(object: object): ESDeclaration[] + #getClassFields(instance: object): ESField[] { - const fieldNames = Object.getOwnPropertyNames(object); - const values = object as Record; + const fieldNames = Object.getOwnPropertyNames(instance); + const values = instance as Record; - const models: ESDeclaration[] = []; + const models = []; for (const fieldName of fieldNames) { const content = values[fieldName]; - const value = content !== undefined ? new ESValue(String(content)) : undefined; - const model = new ESDeclaration(fieldName, value); + const initializer = content !== undefined ? new ESExpression(String(content)) : undefined; + const model = new ESField(fieldName, 'public', 'instance', initializer); models.push(model); } @@ -159,35 +159,39 @@ export default class Reflector return models; } - #getFunctions(clazz: Function): ESFunction[] + #getClassMethods(clazz: Function): (ESConstructor | ESGetter | ESSetter | ESMethod )[] { const functionDescriptions = Object.getOwnPropertyDescriptors(clazz.prototype); - const models: ESFunction[] = []; + const models = []; for (const functionName in functionDescriptions) { const description = functionDescriptions[functionName]; - const funktion = description.value; + const method = description.value; - if (funktion instanceof Function === false) + if (method instanceof Function === false) { continue; } - const model = this.fromFunction(funktion); + const model = this.fromFunction(method); - if (description.get !== undefined) + if (model.identifier === 'constructor') { - models.push(new ESGetter(model.name, model.parameters, model.body, model.isStatic, model.isAsync, model.isPrivate)); + models.push(new ESConstructor(model.parameters, model.body)); + } + else if (description.get !== undefined) + { + models.push(new ESGetter(model.identifier!, 'public', 'instance', model.body)); } else if (description.set !== undefined) { - models.push(new ESSetter(model.name, model.parameters, model.body, model.isStatic, model.isAsync, model.isPrivate)); + models.push(new ESSetter(model.identifier!, 'public', 'instance', model.parameters[0], model.body)); } else { - models.push(model); + models.push(new ESMethod(model.identifier!, 'public', 'instance', model.parameters, model.body, model.isAsync)); } } diff --git a/packages/analysis/src/index.ts b/packages/analysis/src/index.ts index b3819fc7..938eb2f1 100644 --- a/packages/analysis/src/index.ts +++ b/packages/analysis/src/index.ts @@ -1,9 +1,11 @@ export { - ESAlias, ESArray, ESClass, ESDeclaration, ESDestructuredArray, ESDestructuredObject, ESDestructuredValue, - ESExport, ESExpression, ESField, ESFunction, ESGenerator, ESGetter, ESImport, ESMember, ESModule, - ESObject, ESParameter, ESSetter -} from './models'; + ESBinding, ESIdentifierBinding, ESArrayBinding, ESObjectBinding, ESBindingElement, + ESDeclaration, ESBlock, ESExpression, ESVariable, + ESFunction, ESArrowFunction, ESGeneratorFunction, ESParameter, + ESClass, ESClassMember, ESField, ESMethod, ESGeneratorMethod, ESConstructor, ESGetter, ESSetter, + ESModule, ESModuleMember, ESExport, ESImport, ESStatement +} from './model'; export { Reflector } from './dynamic'; export { Parser } from './static'; diff --git a/packages/analysis/src/model/ESArrayBinding.ts b/packages/analysis/src/model/ESArrayBinding.ts new file mode 100644 index 00000000..ee1a0d39 --- /dev/null +++ b/packages/analysis/src/model/ESArrayBinding.ts @@ -0,0 +1,29 @@ + +import type ESBindingElement from './ESBindingElement'; +import ESBinding from './ESBinding'; + +export default class ESArrayBinding extends ESBinding +{ + elements: ESBindingElement[]; + + constructor(elements: ESBindingElement[]) + { + super(); + + this.elements = elements; + } + + clone(): ESArrayBinding + { + const elements = this.elements.map(element => element.clone()); + + return new ESArrayBinding(elements); + } + + toString(): string + { + const elements = this.elements.map(element => element.toString()); + + return `[${elements.join(',')}]`; + } +} diff --git a/packages/analysis/src/model/ESArrowFunction.ts b/packages/analysis/src/model/ESArrowFunction.ts new file mode 100644 index 00000000..5c1b820a --- /dev/null +++ b/packages/analysis/src/model/ESArrowFunction.ts @@ -0,0 +1,29 @@ + +import type ESBlock from './ESBlock'; +import type ESParameter from './ESParameter'; +import ESFunction from './ESFunction'; + +export default class ESArrowFunction extends ESFunction +{ + constructor(parameters: ESParameter[], body: ESBlock, isAsync = false) + { + super(undefined, parameters, body, isAsync); + } + + clone(): ESFunction + { + const parameters = this.parameters.map(parameter => parameter.clone()); + const body = this.body.clone(); + + return new ESArrowFunction(parameters, body, this.isAsync); + } + + toString(): string + { + const prefix = this.isAsync ? 'async ' : ''; + const parameters = this.parameters.map((parameter) => parameter.toString()); + const body = this.body.toString(); + + return `${prefix}(${parameters.join(', ')})=>${body}`; + } +} diff --git a/packages/analysis/src/model/ESBinding.ts b/packages/analysis/src/model/ESBinding.ts new file mode 100644 index 00000000..5e63cffb --- /dev/null +++ b/packages/analysis/src/model/ESBinding.ts @@ -0,0 +1,7 @@ + +export default abstract class ESBinding +{ + abstract clone(): ESBinding; + + abstract toString(): string; +} diff --git a/packages/analysis/src/model/ESBindingElement.ts b/packages/analysis/src/model/ESBindingElement.ts new file mode 100644 index 00000000..fc041e19 --- /dev/null +++ b/packages/analysis/src/model/ESBindingElement.ts @@ -0,0 +1,31 @@ + +import type ESBinding from './ESBinding'; +import type ESStatement from './ESStatement'; + +export default class ESBindingElement +{ + binding: ESBinding; + initializer: ESStatement | undefined; + + constructor(binding: ESBinding, initializer: ESStatement | undefined) + { + this.binding = binding; + this.initializer = initializer; + } + + clone(): ESBindingElement + { + const binding = this.binding.clone(); + const initializer = this.initializer?.clone(); + + return new ESBindingElement(binding, initializer); + } + + toString(): string + { + const binding = this.binding.toString(); + const initializer = this.initializer !== undefined ? `=${this.initializer.toString(false)}` : ''; + + return `${binding}${initializer}`; + } +} diff --git a/packages/analysis/src/model/ESBlock.ts b/packages/analysis/src/model/ESBlock.ts new file mode 100644 index 00000000..87d9542e --- /dev/null +++ b/packages/analysis/src/model/ESBlock.ts @@ -0,0 +1,24 @@ + +import ESStatement from './ESStatement'; + +export default class ESBlock extends ESStatement +{ + code: string; + + constructor(code: string) + { + super(); + + this.code = code; + } + + clone(): ESBlock + { + return new ESBlock(this.code); + } + + toString(): string + { + return this.code; + } +} diff --git a/packages/analysis/src/model/ESClass.ts b/packages/analysis/src/model/ESClass.ts new file mode 100644 index 00000000..3aebd452 --- /dev/null +++ b/packages/analysis/src/model/ESClass.ts @@ -0,0 +1,129 @@ + +import type ESClassMember from './ESClassMember'; +import ESConstructor from './ESConstructor'; +import ESGetter from './ESGetter'; +import ESSetter from './ESSetter'; +import ESMethod from './ESMethod'; +import ESField from './ESField'; +import ESDeclaration from './ESDeclaration'; + +export type ESReadableClassMember = ESField | ESGetter; +export type ESWritableClassMember = ESField | ESSetter; + +export default class ESClass extends ESDeclaration +{ + parent: string | undefined; + members: ESClassMember[]; + + constructor(identifier: string | undefined, parent: string | undefined, members: ESClassMember[]) + { + super(identifier); + + this.parent = parent; + this.members = members; + } + + get construct() { return this.members.find(member => member instanceof ESConstructor); } + + get fields() { return this.members.filter(member => member instanceof ESField); } + + get getters() { return this.members.filter(member => member instanceof ESGetter); } + + get setters() { return this.members.filter(member => member instanceof ESSetter); } + + get methods() { return this.members.filter(member => member instanceof ESMethod); } + + get publicFields() { return this.fields.filter(member => member.visibility === 'public' ); } + + get publicGetters() { return this.getters.filter(member => member.visibility === 'public' ); } + + get publicSetters() { return this.setters.filter(member => member.visibility === 'public' ); } + + get publicMethods() { return this.methods.filter(member => member.visibility === 'public' ); } + + get readable(): ESReadableClassMember[] + { + return [...this.publicFields, ...this.publicGetters]; + } + + get writable(): ESWritableClassMember[] + { + return [...this.publicFields, ...this.publicSetters]; + } + + getMember(identifier: string): ESClassMember | undefined + { + return this.members.find(member => member.is(identifier)); + } + + hasMember(identifier: string): boolean + { + return this.members.some(member => member.is(identifier)); + } + + getField(identifier: string): ESField | undefined + { + return this.fields.find(member => member.is(identifier)); + } + + hasField(identifier: string): boolean + { + return this.fields.some(member => member.is(identifier)); + } + + getGetter(identifier: string): ESGetter | undefined + { + return this.getters.find(member => member.is(identifier)); + } + + hasGetter(identifier: string): boolean + { + return this.getters.some(member => member.is(identifier)); + } + + getSetter(identifier: string): ESSetter | undefined + { + return this.setters.find(member => member.is(identifier)); + } + + hasSetter(identifier: string): boolean + { + return this.setters.some(member => member.is(identifier)); + } + + getMethod(identifier: string): ESMethod | undefined + { + return this.methods.find(member => member.is(identifier)); + } + + hasMethod(identifier: string): boolean + { + return this.methods.some(member => member.is(identifier)); + } + + canRead(identifier: string): boolean + { + return this.readable.some(member => member.is(identifier)); + } + + canWrite(identifier: string): boolean + { + return this.writable.some(member => member.is(identifier)); + } + + clone(): ESClass + { + const members = this.members.map(member => member.clone()); + + return new ESClass(this.identifier, this.parent, members); + } + + toString(): string + { + const identifier = this.identifier ?? ''; + const infix = this.parent !== undefined ? ` extends ${this.parent}` : ''; + const members = this.members.map(member => member.toString()); + + return `class ${identifier}${infix}{${members.join('')}}`; + } +} diff --git a/packages/analysis/src/model/ESClassMember.ts b/packages/analysis/src/model/ESClassMember.ts new file mode 100644 index 00000000..dabfb684 --- /dev/null +++ b/packages/analysis/src/model/ESClassMember.ts @@ -0,0 +1,26 @@ + +export type Visibility = 'public' | 'private'; +export type Location = 'instance' | 'static'; + +export default abstract class ESClassMember +{ + identifier: string; + visibility: Visibility; + location: Location; + + constructor(identifier: string, visibility: Visibility, location: Location) + { + this.identifier = identifier; + this.visibility = visibility; + this.location = location; + } + + is(identifier: string): boolean + { + return this.identifier === identifier; + } + + abstract clone(): ESClassMember; + + abstract toString(): string; +} diff --git a/packages/analysis/src/model/ESConstructor.ts b/packages/analysis/src/model/ESConstructor.ts new file mode 100644 index 00000000..84de698c --- /dev/null +++ b/packages/analysis/src/model/ESConstructor.ts @@ -0,0 +1,34 @@ + +import type ESBlock from './ESBlock'; +import type ESParameter from './ESParameter'; +import ESClassMember from './ESClassMember'; + +export default class ESConstructor extends ESClassMember +{ + parameters: ESParameter[]; + body: ESBlock; + + constructor(parameters: ESParameter[], body: ESBlock) + { + super('constructor', 'public', 'instance'); + + this.parameters = parameters; + this.body = body; + } + + clone(): ESConstructor + { + const parameters = this.parameters.map(parameter => parameter.clone()); + const body = this.body.clone(); + + return new ESConstructor(parameters, body); + } + + toString(): string + { + const parameters = this.parameters.map((parameter) => parameter.toString()); + const body = this.body.toString(); + + return `${this.identifier}(${parameters.join(',')}) ${body}`; + } +} diff --git a/packages/analysis/src/model/ESDeclaration.ts b/packages/analysis/src/model/ESDeclaration.ts new file mode 100644 index 00000000..144d112d --- /dev/null +++ b/packages/analysis/src/model/ESDeclaration.ts @@ -0,0 +1,19 @@ + +import ESStatement from './ESStatement'; + +export default abstract class ESDeclaration extends ESStatement +{ + identifier: string | undefined; + + constructor(identifier: string | undefined) + { + super(); + + this.identifier = identifier; + } + + is(identifier: string): boolean + { + return this.identifier === identifier; + } +} diff --git a/packages/analysis/src/model/ESExport.ts b/packages/analysis/src/model/ESExport.ts new file mode 100644 index 00000000..0465fdc6 --- /dev/null +++ b/packages/analysis/src/model/ESExport.ts @@ -0,0 +1,42 @@ + +import type ESModuleMember from './ESModuleMember.js'; +import ESStatement from './ESStatement.js'; + +export default class ESExport extends ESStatement +{ + members: ESModuleMember[]; + from: string | undefined; + + constructor(members: ESModuleMember[], from: string | undefined) + { + super(); + + this.members = members; + this.from = from; + } + + hasMember(identifier: string): boolean + { + return this.members.some(member => member.is(identifier)); + } + + getMember(identifier: string): ESModuleMember | undefined + { + return this.members.find(member => member.is(identifier)); + } + + clone(): ESExport + { + const members = this.members.map(member => member.clone()); + + return new ESExport(members, this.from); + } + + toString(): string + { + const members = this.members.map(member => member.toString()); + const postfix = this.from ? ` from '${this.from}'` : ''; + + return `export {${members.join(',')}}${postfix};`; + } +} diff --git a/packages/analysis/src/model/ESExpression.ts b/packages/analysis/src/model/ESExpression.ts new file mode 100644 index 00000000..63c50d9b --- /dev/null +++ b/packages/analysis/src/model/ESExpression.ts @@ -0,0 +1,26 @@ + +import ESStatement from './ESStatement'; + +export default class ESExpression extends ESStatement +{ + code: string; + + constructor(code: string) + { + super(); + + this.code = code; + } + + clone(): ESExpression + { + return new ESExpression(this.code); + } + + toString(terminate = true): string + { + const terminator = terminate === false || this.code.endsWith('}') ? '' : ';'; + + return `${this.code}${terminator}`; + } +} diff --git a/packages/analysis/src/model/ESField.ts b/packages/analysis/src/model/ESField.ts new file mode 100644 index 00000000..3ee6ef72 --- /dev/null +++ b/packages/analysis/src/model/ESField.ts @@ -0,0 +1,32 @@ + +import type ESStatement from './ESStatement'; +import type { Visibility, Location } from './ESClassMember'; +import ESClassMember from './ESClassMember'; + +export default class ESField extends ESClassMember +{ + initializer: ESStatement | undefined; + + constructor(identifier: string, visibility: Visibility, location: Location, initializer: ESStatement | undefined) + { + super(identifier, visibility, location); + + this.initializer = initializer; + } + + clone(): ESField + { + const initializer = this.initializer?.clone(); + + return new ESField(this.identifier, this.visibility, this.location, initializer); + } + + toString(): string + { + const location = this.location === 'static' ? 'static ' : ''; + const visibility = this.visibility === 'private' ? '#' : ''; + const initializer = this.initializer !== undefined ? `=${this.initializer.toString(true)}` : ';'; + + return `${location}${visibility}${this.identifier}${initializer}`; + } +} diff --git a/packages/analysis/src/model/ESFunction.ts b/packages/analysis/src/model/ESFunction.ts new file mode 100644 index 00000000..0df73b30 --- /dev/null +++ b/packages/analysis/src/model/ESFunction.ts @@ -0,0 +1,38 @@ + +import type ESBlock from './ESBlock'; +import type ESParameter from './ESParameter'; +import ESDeclaration from './ESDeclaration'; + +export default class ESFunction extends ESDeclaration +{ + parameters: ESParameter[]; + body: ESBlock; + isAsync: boolean; + + constructor(identifier: string | undefined, parameters: ESParameter[], body: ESBlock, isAsync = false) + { + super(identifier); + + this.parameters = parameters; + this.body = body; + this.isAsync = isAsync; + } + + clone(): ESFunction + { + const parameters = this.parameters.map(parameter => parameter.clone()); + const body = this.body.clone(); + + return new ESFunction(this.identifier, parameters, body, this.isAsync); + } + + toString(): string + { + const prefix = this.isAsync ? 'async ' : ''; + const identifier = this.identifier ? ` ${this.identifier}` : ''; + const parameters = this.parameters.map((parameter) => parameter.toString()); + const body = this.body.toString(); + + return `${prefix}function${identifier}(${parameters.join(',')})${body}`; + } +} diff --git a/packages/analysis/src/model/ESGeneratorFunction.ts b/packages/analysis/src/model/ESGeneratorFunction.ts new file mode 100644 index 00000000..ea489eac --- /dev/null +++ b/packages/analysis/src/model/ESGeneratorFunction.ts @@ -0,0 +1,23 @@ + +import ESFunction from './ESFunction'; + +export default class ESGeneratorFunction extends ESFunction +{ + clone(): ESGeneratorFunction + { + const parameters = this.parameters.map(parameter => parameter.clone()); + const body = this.body.clone(); + + return new ESGeneratorFunction(this.identifier, parameters, body, this.isAsync); + } + + toString(): string + { + const prefix = this.isAsync ? 'async ' : ''; + const identifier = this.identifier ?? ''; + const parameters = this.parameters.map((parameter) => parameter.toString()); + const body = this.body.toString(); + + return `${prefix}function* ${identifier}(${parameters.join(',')})${body}`; + } +} diff --git a/packages/analysis/src/model/ESGeneratorMethod.ts b/packages/analysis/src/model/ESGeneratorMethod.ts new file mode 100644 index 00000000..cc979a25 --- /dev/null +++ b/packages/analysis/src/model/ESGeneratorMethod.ts @@ -0,0 +1,23 @@ + +import ESMethod from './ESMethod'; + +export default class ESGeneratorMethod extends ESMethod +{ + clone(): ESGeneratorMethod + { + const parameters = this.parameters.map(parameter => parameter.clone()); + const body = this.body.clone(); + + return new ESGeneratorMethod(this.identifier, this.visibility, this.location, parameters, body, this.isAsync); + } + + toString(): string + { + const prefix = this.isAsync ? 'async ' : ''; + const identifier = this.identifier; + const parameters = this.parameters.map((parameter) => parameter.toString()); + const body = this.body.toString(); + + return `${prefix}*${identifier}(${parameters.join(',')})${body}`; + } +} diff --git a/packages/analysis/src/model/ESGetter.ts b/packages/analysis/src/model/ESGetter.ts new file mode 100644 index 00000000..b034618a --- /dev/null +++ b/packages/analysis/src/model/ESGetter.ts @@ -0,0 +1,32 @@ + +import type ESBlock from './ESBlock'; +import type { Visibility, Location } from './ESClassMember'; +import ESClassMember from './ESClassMember'; + +export default class ESGetter extends ESClassMember +{ + body: ESBlock; + + constructor(identifier: string, visibility: Visibility, location: Location, body: ESBlock) + { + super(identifier, visibility, location); + + this.body = body; + } + + clone(): ESGetter + { + const body = this.body.clone(); + + return new ESGetter(this.identifier, this.visibility, this.location, body); + } + + toString(): string + { + const location = this.location === 'static' ? 'static ' : ''; + const visibility = this.visibility === 'private' ? '#' : ''; + const body = this.body.toString(); + + return `${location}get ${visibility}${this.identifier}()${body}`; + } +} diff --git a/packages/analysis/src/model/ESIdentifierBinding.ts b/packages/analysis/src/model/ESIdentifierBinding.ts new file mode 100644 index 00000000..b5e87df7 --- /dev/null +++ b/packages/analysis/src/model/ESIdentifierBinding.ts @@ -0,0 +1,28 @@ + +import ESBinding from './ESBinding'; + +export default class ESIdentifierBinding extends ESBinding +{ + identifier: string; + isRest: boolean; + + constructor(identifier: string, isRest = false) + { + super(); + + this.identifier = identifier; + this.isRest = isRest; + } + + clone(): ESIdentifierBinding + { + return new ESIdentifierBinding(this.identifier, this.isRest); + } + + toString(): string + { + const prefix = this.isRest ? '...' : ''; + + return `${prefix}${this.identifier}`; + } +} diff --git a/packages/analysis/src/model/ESImport.ts b/packages/analysis/src/model/ESImport.ts new file mode 100644 index 00000000..97231d41 --- /dev/null +++ b/packages/analysis/src/model/ESImport.ts @@ -0,0 +1,46 @@ + +import type ESModuleMember from './ESModuleMember.js'; +import ESStatement from './ESStatement.js'; + +export default class ESImport extends ESStatement +{ + members: ESModuleMember[]; + from: string; + + constructor(members: ESModuleMember[], from: string) + { + super(); + + this.members = members; + this.from = from; + } + + hasMember(identifier: string): boolean + { + return this.members.some(member => member.is(identifier)); + } + + getMember(identifier: string): ESModuleMember | undefined + { + return this.members.find(member => member.is(identifier)); + } + + clone(): ESImport + { + const members = this.members.map(member => member.clone()); + + return new ESImport(members, this.from); + } + + toString(): string + { + if (this.members.length === 0) + { + return `import '${this.from}';`; + } + + const members = this.members.map(member => member.toString()); + + return `import {${members.join(',')}} from '${this.from}';`; + } +} diff --git a/packages/analysis/src/model/ESMethod.ts b/packages/analysis/src/model/ESMethod.ts new file mode 100644 index 00000000..24022438 --- /dev/null +++ b/packages/analysis/src/model/ESMethod.ts @@ -0,0 +1,40 @@ + +import type ESBlock from './ESBlock'; +import type ESParameter from './ESParameter'; +import type { Visibility, Location } from './ESClassMember'; +import ESClassMember from './ESClassMember'; + +export default class ESMethod extends ESClassMember +{ + parameters: ESParameter[]; + body: ESBlock; + isAsync: boolean; + + constructor(identifier: string, visibility: Visibility, location: Location, parameters: ESParameter[], body: ESBlock, isAsync = false) + { + super(identifier, visibility, location); + + this.parameters = parameters; + this.body = body; + this.isAsync = isAsync; + } + + clone(): ESMethod + { + const parameters = this.parameters.map(parameter => parameter.clone()); + const body = this.body.clone(); + + return new ESMethod(this.identifier, this.visibility, this.location, parameters, body, this.isAsync); + } + + toString(): string + { + const location = this.location === 'static' ? 'static ' : ''; + const visibility = this.visibility === 'private' ? '#' : ''; + const prefix = this.isAsync ? 'async ' : ''; + const parameters = this.parameters.map((parameter) => parameter.toString()); + const body = this.body.toString(); + + return `${location}${prefix}${visibility}${this.identifier}(${parameters.join(',')})${body}`; + } +} diff --git a/packages/analysis/src/model/ESModule.ts b/packages/analysis/src/model/ESModule.ts new file mode 100644 index 00000000..855ba47f --- /dev/null +++ b/packages/analysis/src/model/ESModule.ts @@ -0,0 +1,125 @@ + +import ESExport from './ESExport'; +import ESImport from './ESImport'; +import ESExpression from './ESExpression'; +import ESDeclaration from './ESDeclaration'; +import ESClass from './ESClass'; +import ESFunction from './ESFunction'; +import ESVariable from './ESVariable'; +import type ESStatement from './ESStatement'; + +export default class ESModule +{ + statements: ESStatement[]; + + constructor(statements: ESStatement[]) + { + this.statements = statements; + } + + get exports() { return this.statements.filter(statement => statement instanceof ESExport); } + + get imports() { return this.statements.filter(statement => statement instanceof ESImport); } + + get expressions() { return this.statements.filter(statement => statement instanceof ESExpression); } + + get declarations() { return this.statements.filter(statement => statement instanceof ESDeclaration); } + + get variables() { return this.statements.filter(statement => statement instanceof ESVariable); } + + get functions() { return this.statements.filter(statement => statement instanceof ESFunction); } + + get classes() { return this.statements.filter(statement => statement instanceof ESClass); } + + get exported(): ESDeclaration[] + { + const declarations: ESDeclaration[] = []; + + for (const entry of this.exports) + { + for (const member of entry.members) + { + const declaration = this.getDeclaration(member.identifier); + + if (declaration === undefined) + { + continue; + } + + declarations.push(declaration); + } + } + + return declarations; + } + + get exportedVariables() { return this.exported.filter(declaration => declaration instanceof ESVariable); } + + get exportedFunctions() { return this.exported.filter(declaration => declaration instanceof ESFunction); } + + get exportedClasses() { return this.exported.filter(declaration => declaration instanceof ESClass); } + + getExport(name: string): ESExport | undefined + { + return this.exports.find(entry => entry.hasMember(name)); + } + + getImport(name: string): ESImport | undefined + { + return this.imports.find(entry => entry.hasMember(name)); + } + + hasDeclaration(identifier: string): boolean + { + return this.declarations.some(entry => entry.is(identifier)); + } + + getDeclaration(identifier: string): ESDeclaration | undefined + { + return this.declarations.find(entry => entry.is(identifier)); + } + + hasVariable(identifier: string): boolean + { + return this.variables.some(entry => entry.is(identifier)); + } + + getVariable(identifier: string): ESVariable | undefined + { + return this.variables.find(entry => entry.is(identifier)); + } + + hasFunction(identifier: string): boolean + { + return this.functions.some(entry => entry.is(identifier)); + } + + getFunction(identifier: string): ESFunction | undefined + { + return this.functions.find(entry => entry.is(identifier)); + } + + hasClass(identifier: string): boolean + { + return this.classes.some(entry => entry.is(identifier)); + } + + getClass(identifier: string): ESClass | undefined + { + return this.classes.find(entry => entry.is(identifier)); + } + + clone(): ESModule + { + const statements = this.statements.map(statement => statement.clone()); + + return new ESModule(statements); + } + + toString(): string + { + return this.statements + .map(statement => statement.toString(true)) + .join('\n'); + } +} diff --git a/packages/analysis/src/model/ESModuleMember.ts b/packages/analysis/src/model/ESModuleMember.ts new file mode 100644 index 00000000..d65a5f80 --- /dev/null +++ b/packages/analysis/src/model/ESModuleMember.ts @@ -0,0 +1,33 @@ + +export default class ESModuleMember +{ + identifier: string; + alias?: string; + + constructor(identifier: string, alias?: string) + { + this.identifier = identifier; + this.alias = alias; + } + + is(identifier: string): boolean + { + return this.alias === identifier + || (this.alias === undefined && this.identifier === identifier); + } + + clone(): ESModuleMember + { + return new ESModuleMember(this.identifier, this.alias); + } + + toString(): string + { + if (this.alias === undefined) + { + return this.identifier; + } + + return `${this.identifier} as ${this.alias}`; + } +} diff --git a/packages/analysis/src/model/ESObjectBinding.ts b/packages/analysis/src/model/ESObjectBinding.ts new file mode 100644 index 00000000..707bc21f --- /dev/null +++ b/packages/analysis/src/model/ESObjectBinding.ts @@ -0,0 +1,29 @@ + +import type ESBindingElement from './ESBindingElement'; +import ESBinding from './ESBinding'; + +export default class ESObjectBinding extends ESBinding +{ + elements: ESBindingElement[]; + + constructor(elements: ESBindingElement[]) + { + super(); + + this.elements = elements; + } + + clone(): ESObjectBinding + { + const elements = this.elements.map(element => element.clone()); + + return new ESObjectBinding(elements); + } + + toString(): string + { + const elements = this.elements.map(element => element.toString()); + + return `{${elements.join(',')}}`; + } +} diff --git a/packages/analysis/src/model/ESParameter.ts b/packages/analysis/src/model/ESParameter.ts new file mode 100644 index 00000000..daafc249 --- /dev/null +++ b/packages/analysis/src/model/ESParameter.ts @@ -0,0 +1,10 @@ + +import ESBindingElement from './ESBindingElement'; + +export default class ESParameter extends ESBindingElement +{ + clone(): ESParameter + { + return super.clone(); + } +} diff --git a/packages/analysis/src/model/ESSetter.ts b/packages/analysis/src/model/ESSetter.ts new file mode 100644 index 00000000..2c330191 --- /dev/null +++ b/packages/analysis/src/model/ESSetter.ts @@ -0,0 +1,37 @@ + +import type ESBlock from './ESBlock'; +import type ESParameter from './ESParameter'; +import type { Visibility, Location } from './ESClassMember'; +import ESClassMember from './ESClassMember'; + +export default class ESSetter extends ESClassMember +{ + parameter: ESParameter; + body: ESBlock; + + constructor(identifier: string, visibility: Visibility, location: Location, parameter: ESParameter, body: ESBlock) + { + super(identifier, visibility, location); + + this.parameter = parameter; + this.body = body; + } + + clone(): ESSetter + { + const parameter = this.parameter.clone(); + const body = this.body.clone(); + + return new ESSetter(this.identifier, this.visibility, this.location, parameter, body); + } + + toString(): string + { + const location = this.location === 'static' ? 'static ' : ''; + const visibility = this.visibility === 'private' ? '#' : ''; + const parameter = this.parameter.toString(); + const body = this.body.toString(); + + return `${location}set ${visibility}${this.identifier}(${parameter})${body}`; + } +} diff --git a/packages/analysis/src/model/ESStatement.ts b/packages/analysis/src/model/ESStatement.ts new file mode 100644 index 00000000..9ee52339 --- /dev/null +++ b/packages/analysis/src/model/ESStatement.ts @@ -0,0 +1,7 @@ + +export default abstract class ESStatement +{ + abstract clone(): ESStatement; + + abstract toString(terminate: boolean): string; +} diff --git a/packages/analysis/src/model/ESVariable.ts b/packages/analysis/src/model/ESVariable.ts new file mode 100644 index 00000000..34191a4e --- /dev/null +++ b/packages/analysis/src/model/ESVariable.ts @@ -0,0 +1,39 @@ + +import type ESBinding from './ESBinding'; +import type ESStatement from './ESStatement'; +import ESDeclaration from './ESDeclaration'; + +export type Type = 'const' | 'let' | 'var'; +export type Initializer = ESStatement | undefined; + +export default class ESVariable extends ESDeclaration +{ + type: Type; + binding: ESBinding; + initializer: Initializer; + + constructor(type: Type, binding: ESBinding, initializer: Initializer) + { + super(binding.toString()); + + this.type = type; + this.binding = binding; + this.initializer = initializer; + } + + clone(): ESVariable + { + const binding = this.binding.clone(); + const initializer = this.initializer?.clone(); + + return new ESVariable(this.type, binding, initializer); + } + + toString(): string + { + const initializer = this.initializer !== undefined ? `=${this.initializer.toString(true)}` : ''; + const terminator = initializer.endsWith(';') ? '' : ';'; + + return `${this.type} ${this.identifier}${initializer}${terminator}`; + } +} diff --git a/packages/analysis/src/model/index.ts b/packages/analysis/src/model/index.ts new file mode 100644 index 00000000..d6ed6688 --- /dev/null +++ b/packages/analysis/src/model/index.ts @@ -0,0 +1,30 @@ + +export type { Visibility as ClassVisibility, Location as ClassLocation } from './ESClassMember'; +export type { Type as VariableType } from './ESVariable'; + +export { default as ESArrayBinding } from './ESArrayBinding'; +export { default as ESArrowFunction } from './ESArrowFunction'; +export { default as ESBinding } from './ESBinding'; +export { default as ESBindingElement } from './ESBindingElement'; +export { default as ESBlock } from './ESBlock'; +export { default as ESClass } from './ESClass'; +export { default as ESClassMember } from './ESClassMember'; +export { default as ESConstructor } from './ESConstructor'; +export { default as ESDeclaration } from './ESDeclaration'; +export { default as ESExport } from './ESExport'; +export { default as ESExpression } from './ESExpression'; +export { default as ESField } from './ESField'; +export { default as ESFunction } from './ESFunction'; +export { default as ESGeneratorFunction } from './ESGeneratorFunction'; +export { default as ESGeneratorMethod } from './ESGeneratorMethod'; +export { default as ESGetter } from './ESGetter'; +export { default as ESIdentifierBinding } from './ESIdentifierBinding'; +export { default as ESImport } from './ESImport'; +export { default as ESMethod } from './ESMethod'; +export { default as ESModule } from './ESModule'; +export { default as ESModuleMember } from './ESModuleMember'; +export { default as ESObjectBinding } from './ESObjectBinding'; +export { default as ESParameter } from './ESParameter'; +export { default as ESSetter } from './ESSetter'; +export { default as ESStatement } from './ESStatement'; +export { default as ESVariable } from './ESVariable'; diff --git a/packages/analysis/src/models/ESAlias.ts b/packages/analysis/src/models/ESAlias.ts deleted file mode 100644 index 916ed2cc..00000000 --- a/packages/analysis/src/models/ESAlias.ts +++ /dev/null @@ -1,21 +0,0 @@ - -export default class ESAlias -{ - readonly #name: string; - readonly #as: string; - - constructor(name: string, as: string) - { - this.#name = name; - this.#as = as; - } - - get name(): string { return this.#name; } - - get as(): string { return this.#as; } - - toString(): string - { - return `${this.#name} as ${this.#as}`; - } -} diff --git a/packages/analysis/src/models/ESArray.ts b/packages/analysis/src/models/ESArray.ts deleted file mode 100644 index 83b8345e..00000000 --- a/packages/analysis/src/models/ESArray.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import ESValue from './ESValue.js'; - -export default class ESArray extends ESValue -{ - -} diff --git a/packages/analysis/src/models/ESClass.ts b/packages/analysis/src/models/ESClass.ts deleted file mode 100644 index 2eaba977..00000000 --- a/packages/analysis/src/models/ESClass.ts +++ /dev/null @@ -1,151 +0,0 @@ - -import ESDeclaration from './ESDeclaration.js'; -import ESFunction from './ESFunction.js'; -import ESGenerator from './ESGenerator.js'; -import ESGetter from './ESGetter.js'; -import ESMember from './ESMember.js'; -import ESScope from './ESScope.js'; -import ESSetter from './ESSetter.js'; - -export default class ESClass extends ESMember -{ - readonly #parentName: string | undefined; - readonly #scope: ESScope; - - constructor(name: string, parentName: string | undefined, scope: ESScope) - { - super(name); - - this.#parentName = parentName; - this.#scope = scope; - } - - get parentName(): string | undefined { return this.#parentName; } - - get scope(): ESScope { return this.#scope; } - - get members(): ESMember[] { return this.#scope.members; } - - get declarations(): ESDeclaration[] { return this.#scope.declarations; } - - get functions(): ESFunction[] { return this.#scope.functions; } - - get getters(): ESGetter[] { return this.#scope.getters; } - - get setters(): ESSetter[] { return this.#scope.setters; } - - get generators(): ESGenerator[] { return this.#scope.generators; } - - get readable(): (ESDeclaration | ESGetter)[] - { - const members = new Map(); - - this.getters.forEach(getter => { members.set(getter.name, getter); }); - this.declarations.forEach(declaration => { if (declaration.isPublic) members.set(declaration.name, declaration); }); - - return [...members.values()]; - } - - get writable(): (ESDeclaration | ESSetter)[] - { - const members = new Map(); - - this.setters.forEach(setter => { members.set(setter.name, setter); }); - this.declarations.forEach(declaration => { if (declaration.isPublic) members.set(declaration.name, declaration); }); - - return [...members.values()]; - } - - get callable(): ESFunction[] - { - return this.functions.filter(funktion => funktion.isPublic); - } - - getMember(name: string): ESMember | undefined - { - return this.#scope.getMember(name); - } - - getDeclaration(name: string): ESDeclaration | undefined - { - return this.#scope.getDeclaration(name); - } - - getFunction(name: string): ESFunction | undefined - { - return this.#scope.getFunction(name); - } - - getGetter(name: string): ESGetter | undefined - { - return this.#scope.getGetter(name); - } - - getSetter(name: string): ESSetter | undefined - { - return this.#scope.getSetter(name); - } - - getGenerator(name: string): ESGenerator | undefined - { - return this.#scope.getGenerator(name); - } - - hasMember(name: string): boolean - { - return this.#scope.hasMember(name); - } - - hasDeclaration(name: string): boolean - { - return this.#scope.hasDeclaration(name); - } - - hasFunction(name: string): boolean - { - return this.#scope.hasFunction(name); - } - - hasGetter(name: string): boolean - { - return this.#scope.hasGetter(name); - } - - hasSetter(name: string): boolean - { - return this.#scope.hasSetter(name); - } - - hasGenerator(name: string): boolean - { - return this.#scope.hasGenerator(name); - } - - canRead(name: string): boolean - { - const declaration = this.getDeclaration(name); - - return declaration?.isPublic || this.hasGetter(name); - } - - canWrite(name: string): boolean - { - const declaration = this.getDeclaration(name); - - return declaration?.isPublic || this.hasSetter(name); - } - - canCall(name: string): boolean - { - const funktion = this.getFunction(name); - - return funktion?.isPublic ?? false; - } - - toString(): string - { - const infix = this.#parentName !== undefined ? ` extends ${this.#parentName}` : ''; - - return `class ${this.name}${infix} { ${this.#scope.toString()} }`; - } -} diff --git a/packages/analysis/src/models/ESDeclaration.ts b/packages/analysis/src/models/ESDeclaration.ts deleted file mode 100644 index df638342..00000000 --- a/packages/analysis/src/models/ESDeclaration.ts +++ /dev/null @@ -1,27 +0,0 @@ - -import ESIdentifier from './ESIdentifier.js'; -import ESMember from './ESMember.js'; -import ESValue from './ESValue.js'; - -export default class ESDeclaration extends ESMember -{ - readonly #identifier: ESIdentifier; - readonly #value: ESValue | undefined; - - constructor(identifier: ESIdentifier, value: ESValue | undefined, isStatic = false, isPrivate = false) - { - super(identifier.toString(), isStatic, isPrivate); - - this.#identifier = identifier; - this.#value = value; - } - - get identifier() { return this.#identifier; } - - get value() { return this.#value; } - - toString(): string - { - return `${this.name}${this.value ? ' = ' + this.value.toString() : ''}`; - } -} diff --git a/packages/analysis/src/models/ESDestructuredArray.ts b/packages/analysis/src/models/ESDestructuredArray.ts deleted file mode 100644 index 439fc2e6..00000000 --- a/packages/analysis/src/models/ESDestructuredArray.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import ESDestructuredValue from './ESDestructuredValue.js'; - -export default class ESDestructuredArray extends ESDestructuredValue -{ - toString(): string - { - return `[ ${super.toString()} ]`; - } -} diff --git a/packages/analysis/src/models/ESDestructuredObject.ts b/packages/analysis/src/models/ESDestructuredObject.ts deleted file mode 100644 index 0301119d..00000000 --- a/packages/analysis/src/models/ESDestructuredObject.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import ESDestructuredValue from './ESDestructuredValue.js'; - -export default class ESDestructuredObject extends ESDestructuredValue -{ - toString(): string - { - return `{ ${super.toString()} }`; - } -} diff --git a/packages/analysis/src/models/ESDestructuredValue.ts b/packages/analysis/src/models/ESDestructuredValue.ts deleted file mode 100644 index 8f1b3783..00000000 --- a/packages/analysis/src/models/ESDestructuredValue.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import ESParameter from './ESParameter.js'; - -export default class ESDestructuredValue -{ - readonly #members: ESParameter[]; - - constructor(members: ESParameter[]) - { - this.#members = members; - } - - get members() { return this.#members; } - - toString(): string - { - return this.#members.map(member => member.toString()).join(' , '); - } -} diff --git a/packages/analysis/src/models/ESExport.ts b/packages/analysis/src/models/ESExport.ts deleted file mode 100644 index 98f3a4ef..00000000 --- a/packages/analysis/src/models/ESExport.ts +++ /dev/null @@ -1,38 +0,0 @@ - -import ESAlias from './ESAlias.js'; -import ESMember from './ESMember.js'; - -export default class ESExport extends ESMember -{ - readonly #members: ESAlias[]; - readonly #from: string | undefined; - - constructor(members: ESAlias[], from: string | undefined) - { - super(''); - - this.#members = members; - this.#from = from; - } - - get members() { return this.#members; } - - get from() { return this.#from; } - - hasMember(name: string): boolean - { - return this.#members.some(member => member.as === name); - } - - getMember(name: string): ESAlias | undefined - { - return this.#members.find(member => member.as === name); - } - - toString(): string - { - const postfix = this.#from ? ` from '${this.#from}'` : ''; - - return `export { ${this.#members.join(', ')} }${postfix}`; - } -} diff --git a/packages/analysis/src/models/ESExpression.ts b/packages/analysis/src/models/ESExpression.ts deleted file mode 100644 index b19e39cd..00000000 --- a/packages/analysis/src/models/ESExpression.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import ESValue from './ESValue.js'; - -export default class ESExpression extends ESValue -{ - -} diff --git a/packages/analysis/src/models/ESField.ts b/packages/analysis/src/models/ESField.ts deleted file mode 100644 index 6ddeba47..00000000 --- a/packages/analysis/src/models/ESField.ts +++ /dev/null @@ -1,23 +0,0 @@ - -import ESValue from './ESValue.js'; - -export default class ESField -{ - readonly #name: string; - readonly #value: ESValue | undefined; - - constructor(name: string, value: ESValue | undefined) - { - this.#name = name; - this.#value = value; - } - - get name() { return this.#name; } - - get value() { return this.#value; } - - toString(): string - { - return `${this.name}${this.value ? ' = ' + this.value.toString() : ''}`; - } -} diff --git a/packages/analysis/src/models/ESFunction.ts b/packages/analysis/src/models/ESFunction.ts deleted file mode 100644 index 289268c9..00000000 --- a/packages/analysis/src/models/ESFunction.ts +++ /dev/null @@ -1,32 +0,0 @@ - -import ESMember from './ESMember.js'; -import ESParameter from './ESParameter.js'; - -export default class ESFunction extends ESMember -{ - readonly #parameters: ESParameter[]; - readonly #body: string; - readonly #isAsync: boolean; - - constructor(name: string, parameters: ESParameter[], body: string, isStatic = false, isAsync = false, isPrivate = false) - { - super(name, isStatic, isPrivate); - - this.#parameters = parameters; - this.#body = body; - this.#isAsync = isAsync; - } - - get parameters() { return this.#parameters; } - - get body() { return this.#body; } - - get isAsync() { return this.#isAsync; } - - toString(): string - { - const parameters = this.parameters.map((parameter) => parameter.toString()); - - return `${this.isAsync ? 'async ' : ''}${this.name}(${parameters.join(', ')}) { ${this.body} }`; - } -} diff --git a/packages/analysis/src/models/ESGenerator.ts b/packages/analysis/src/models/ESGenerator.ts deleted file mode 100644 index eb7c6a6e..00000000 --- a/packages/analysis/src/models/ESGenerator.ts +++ /dev/null @@ -1,12 +0,0 @@ - -import ESFunction from './ESFunction.js'; - -export default class ESGenerator extends ESFunction -{ - toString(): string - { - const parameters = this.parameters.map((parameter) => parameter.toString()); - - return `${this.isAsync ? 'async ' : ''}${this.name}*(${parameters.join(', ')}) { ${this.body} }`; - } -} diff --git a/packages/analysis/src/models/ESGetter.ts b/packages/analysis/src/models/ESGetter.ts deleted file mode 100644 index 4bc4e68b..00000000 --- a/packages/analysis/src/models/ESGetter.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import ESFunction from './ESFunction.js'; - -export default class ESGetter extends ESFunction -{ - toString(): string - { - return `get ${super.toString()}`; - } -} diff --git a/packages/analysis/src/models/ESIdentifier.ts b/packages/analysis/src/models/ESIdentifier.ts deleted file mode 100644 index 67eaae59..00000000 --- a/packages/analysis/src/models/ESIdentifier.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import ESDestructuredArray from './ESDestructuredArray.js'; -import ESDestructuredObject from './ESDestructuredObject.js'; - -type ESIdentifier = string | ESDestructuredArray | ESDestructuredObject - -export default ESIdentifier; diff --git a/packages/analysis/src/models/ESImport.ts b/packages/analysis/src/models/ESImport.ts deleted file mode 100644 index 4259e36e..00000000 --- a/packages/analysis/src/models/ESImport.ts +++ /dev/null @@ -1,36 +0,0 @@ - -import ESAlias from './ESAlias.js'; -import ESMember from './ESMember.js'; - -export default class ESImport extends ESMember -{ - readonly #members: ESAlias[]; - readonly #from: string; - - constructor(members: ESAlias[], from: string) - { - super(''); - - this.#members = members; - this.#from = from; - } - - get members() { return this.#members; } - - get from() { return this.#from; } - - hasMember(name: string): boolean - { - return this.#members.some(member => member.as === name); - } - - getMember(name: string): ESAlias | undefined - { - return this.#members.find(member => member.as === name); - } - - toString(): string - { - return `import { ${this.#members.map(member => member.toString()).join(', ')} } from '${this.#from}';`; - } -} diff --git a/packages/analysis/src/models/ESMember.ts b/packages/analysis/src/models/ESMember.ts deleted file mode 100644 index 1a002217..00000000 --- a/packages/analysis/src/models/ESMember.ts +++ /dev/null @@ -1,22 +0,0 @@ - -export default class ESMember -{ - readonly #name: string; - readonly #isStatic: boolean; - readonly #isPrivate: boolean; - - constructor(name: string, isStatic = false, isPrivate = false) - { - this.#name = name; - this.#isStatic = isStatic; - this.#isPrivate = isPrivate; - } - - get name() { return this.#name; } - - get isStatic() { return this.#isStatic; } - - get isPrivate() { return this.#isPrivate; } - - get isPublic() { return this.#isPrivate === false; } -} diff --git a/packages/analysis/src/models/ESModule.ts b/packages/analysis/src/models/ESModule.ts deleted file mode 100644 index e3f98536..00000000 --- a/packages/analysis/src/models/ESModule.ts +++ /dev/null @@ -1,178 +0,0 @@ - -import ESClass from './ESClass.js'; -import ESDeclaration from './ESDeclaration.js'; -import ESExport from './ESExport.js'; -import ESFunction from './ESFunction.js'; -import ESGenerator from './ESGenerator.js'; -import ESImport from './ESImport.js'; -import ESMember from './ESMember.js'; -import ESScope from './ESScope.js'; - -export default class ESModule -{ - readonly #scope: ESScope; - - constructor(scope: ESScope) - { - this.#scope = scope; - } - - get scope(): ESScope { return this.#scope; } - - get members(): ESMember[] { return this.#scope.members; } - - get exportedMembers(): ESMember[] { return this.#filterExported(this.#scope.members); } - - get imports(): ESImport[] { return this.#scope.imports; } - - get exports(): ESExport[] { return this.#scope.exports; } - - get declarations(): ESDeclaration[] { return this.#scope.declarations; } - - get exportedDeclarations(): ESDeclaration[] { return this.#filterExported(this.#scope.declarations) as ESDeclaration[]; } - - get functions(): ESFunction[] { return this.#scope.functions; } - - get exportedFunctions(): ESFunction[] { return this.#filterExported(this.#scope.functions) as ESFunction[]; } - - get generators(): ESFunction[] { return this.#scope.generators; } - - get exportedGenerators(): ESGenerator[] { return this.#filterExported(this.#scope.generators) as ESGenerator[]; } - - get classes(): ESClass[] { return this.#scope.classes; } - - get exportedClasses(): ESClass[] { return this.#filterExported(this.#scope.classes) as ESClass[]; } - - get exported(): Map - { - const exported = new Map(); - - for (const exportItem of this.exports) - { - for (const alias of exportItem.members) - { - const member = this.getMember(alias.name); - - if (member !== undefined) - { - exported.set(alias.as, member); - } - } - } - - return exported; - } - - getMember(name: string): ESMember | undefined - { - return this.#scope.getMember(name); - } - - getDeclaration(name: string): ESDeclaration | undefined - { - return this.#scope.getDeclaration(name); - } - - getFunction(name: string): ESFunction | undefined - { - return this.#scope.getFunction(name); - } - - getGenerator(name: string): ESFunction | undefined - { - return this.#scope.getGenerator(name); - } - - getClass(name: string): ESClass | undefined - { - return this.#scope.getClass(name); - } - - hasMember(name: string): boolean - { - return this.#scope.hasMember(name); - } - - hasDeclaration(name: string): boolean - { - return this.#scope.hasDeclaration(name); - } - - hasFunction(name: string): boolean - { - return this.#scope.hasFunction(name); - } - - hasGenerator(name: string): boolean - { - return this.#scope.hasGenerator(name); - } - - hasClass(name: string): boolean - { - return this.#scope.hasClass(name); - } - - getImport(name: string): ESImport | undefined - { - return this.imports.find(importItem => importItem.hasMember(name)); - } - - getImported(name: string): ESMember | undefined - { - for (const importItem of this.imports) - { - for (const alias of importItem.members) - { - if (alias.as === name) - { - return this.getMember(alias.name); - } - } - } - - return undefined; - } - - isExported(member: ESMember): boolean - { - for (const exportItem of this.exports) - { - for (const alias of exportItem.members) - { - if (alias.name === member.name) - { - return true; - } - } - } - - return false; - } - - getExport(name: string): ESExport | undefined - { - return this.exports.find(exportItem => exportItem.hasMember(name)); - } - - getExported(name: string): ESMember | undefined - { - for (const exportItem of this.exports) - { - for (const alias of exportItem.members) - { - if (alias.as === name) - { - return this.getMember(alias.name); - } - } - } - - return undefined; - } - - #filterExported(members: ESMember[]): ESMember[] - { - return members.filter(member => this.isExported(member)); - } -} diff --git a/packages/analysis/src/models/ESObject.ts b/packages/analysis/src/models/ESObject.ts deleted file mode 100644 index e83a2320..00000000 --- a/packages/analysis/src/models/ESObject.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import ESValue from './ESValue.js'; - -export default class ESObject extends ESValue -{ - -} diff --git a/packages/analysis/src/models/ESParameter.ts b/packages/analysis/src/models/ESParameter.ts deleted file mode 100644 index f51d62d8..00000000 --- a/packages/analysis/src/models/ESParameter.ts +++ /dev/null @@ -1,8 +0,0 @@ - -import ESDestructuredArray from './ESDestructuredArray.js'; -import ESField from './ESField.js'; -import ESDestructuredObject from './ESDestructuredObject.js'; - -type ESParameter = ESField | ESDestructuredObject | ESDestructuredArray; - -export default ESParameter; diff --git a/packages/analysis/src/models/ESScope.ts b/packages/analysis/src/models/ESScope.ts deleted file mode 100644 index d517a5d7..00000000 --- a/packages/analysis/src/models/ESScope.ts +++ /dev/null @@ -1,126 +0,0 @@ - -import ESMember from './ESMember.js'; -import ESGetter from './ESGetter.js'; -import ESSetter from './ESSetter.js'; -import ESFunction from './ESFunction.js'; -import ESClass from './ESClass.js'; -import ESImport from './ESImport.js'; -import ESExport from './ESExport.js'; -import ESGenerator from './ESGenerator.js'; -import ESDeclaration from './ESDeclaration.js'; - -// Required to work after minification. -const IMPORT_NAME = ESImport.name; -const EXPORT_NAME = ESExport.name; -const DECLARATION_NAME = ESDeclaration.name; -const FUNCTION_NAME = ESFunction.name; -const GETTER_NAME = ESGetter.name; -const SETTER_NAME = ESSetter.name; -const GENERATOR_NAME = ESGenerator.name; -const CLASS_NAME = ESClass.name; - -export default class ESScope -{ - readonly #members: ESMember[]; - - constructor(members: ESMember[]) - { - this.#members = members; - } - - // The constructor name is used to determine the type of the member. - // This makes sure that the member is of the exact type and not a subclass. - - get members(): ESMember[] { return this.#members; } - - get imports(): ESImport[] { return this.#members.filter(member => member.constructor.name === IMPORT_NAME) as ESImport[]; } - - get exports(): ESExport[] { return this.#members.filter(member => member.constructor.name === EXPORT_NAME) as ESExport[]; } - - get declarations(): ESDeclaration[] { return this.#members.filter(member => member.constructor.name === DECLARATION_NAME) as ESDeclaration[]; } - - get functions(): ESFunction[] { return this.#members.filter(member => member.constructor.name === FUNCTION_NAME) as ESFunction[]; } - - get getters(): ESGetter[] { return this.#members.filter(member => member.constructor.name === GETTER_NAME) as ESGetter[]; } - - get setters(): ESSetter[] { return this.#members.filter(member => member.constructor.name === SETTER_NAME) as ESSetter[]; } - - get generators(): ESGenerator[] { return this.#members.filter(member => member.constructor.name === GENERATOR_NAME) as ESGenerator[]; } - - get classes(): ESClass[] { return this.#members.filter(member => member.constructor.name === CLASS_NAME) as ESClass[]; } - - getMember(name: string): ESMember | undefined - { - return this.#members.find(member => member.name === name); - } - - getDeclaration(name: string): ESDeclaration | undefined - { - return this.declarations.find(member => member.name === name); - } - - getFunction(name: string): ESFunction | undefined - { - return this.functions.find(member => member.name === name); - } - - getGetter(name: string): ESGetter | undefined - { - return this.getters.find(member => member.name === name); - } - - getSetter(name: string): ESSetter | undefined - { - return this.setters.find(member => member.name === name); - } - - getGenerator(name: string): ESGenerator | undefined - { - return this.generators.find(member => member.name === name); - } - - getClass(name: string): ESClass | undefined - { - return this.classes.find(member => member.name === name); - } - - hasMember(name: string): boolean - { - return this.getMember(name) !== undefined; - } - - hasDeclaration(name: string): boolean - { - return this.getDeclaration(name) !== undefined; - } - - hasFunction(name: string): boolean - { - return this.getFunction(name) !== undefined; - } - - hasGetter(name: string): boolean - { - return this.getGetter(name) !== undefined; - } - - hasSetter(name: string): boolean - { - return this.getSetter(name) !== undefined; - } - - hasGenerator(name: string): boolean - { - return this.getGenerator(name) !== undefined; - } - - hasClass(name: string): boolean - { - return this.getClass(name) !== undefined; - } - - toString(): string - { - return this.#members.map(member => member.toString()).join('\n'); - } -} diff --git a/packages/analysis/src/models/ESSetter.ts b/packages/analysis/src/models/ESSetter.ts deleted file mode 100644 index 4450e3ae..00000000 --- a/packages/analysis/src/models/ESSetter.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import ESFunction from './ESFunction.js'; - -export default class ESSetter extends ESFunction -{ - toString(): string - { - return `set ${super.toString()}`; - } -} diff --git a/packages/analysis/src/models/ESValue.ts b/packages/analysis/src/models/ESValue.ts deleted file mode 100644 index 4cd3f4cb..00000000 --- a/packages/analysis/src/models/ESValue.ts +++ /dev/null @@ -1,17 +0,0 @@ - -export default class ESValue -{ - readonly #definition: string; - - constructor(definition: string) - { - this.#definition = definition; - } - - get definition() { return this.#definition; } - - toString(): string - { - return this.#definition; - } -} diff --git a/packages/analysis/src/models/index.ts b/packages/analysis/src/models/index.ts deleted file mode 100644 index 267b1b9e..00000000 --- a/packages/analysis/src/models/index.ts +++ /dev/null @@ -1,23 +0,0 @@ - -export { default as ESAlias } from './ESAlias'; -export { default as ESArray } from './ESArray'; -export { default as ESClass } from './ESClass'; -export { default as ESDeclaration } from './ESDeclaration'; -export { default as ESDestructuredArray } from './ESDestructuredArray'; -export { default as ESDestructuredObject } from './ESDestructuredObject'; -export { default as ESDestructuredValue } from './ESDestructuredValue'; -export { default as ESExport } from './ESExport'; -export { default as ESExpression } from './ESExpression'; -export { default as ESField } from './ESField'; -export { default as ESFunction } from './ESFunction'; -export { default as ESGenerator } from './ESGenerator'; -export { default as ESGetter } from './ESGetter'; -export { default as ESIdentifier } from './ESIdentifier'; -export { default as ESImport } from './ESImport'; -export { default as ESMember } from './ESMember'; -export { default as ESModule } from './ESModule'; -export { default as ESObject } from './ESObject'; -export { default as ESParameter } from './ESParameter'; -export { default as ESScope } from './ESScope'; -export { default as ESSetter } from './ESSetter'; -export { default as ESValue } from './ESValue'; diff --git a/packages/analysis/src/static/Coder.ts b/packages/analysis/src/static/Coder.ts new file mode 100644 index 00000000..80b41b57 --- /dev/null +++ b/packages/analysis/src/static/Coder.ts @@ -0,0 +1,68 @@ + +import Token from './models/Token'; +import { TokenType } from './definitions/TokenType'; +import { isDeclaration, Keyword } from './definitions/Keyword'; + +export default class Coder +{ + readonly #tokens: Token[]; + + constructor(tokens: Token[] = []) + { + this.#tokens = tokens; + } + + get tokens() { return this.#tokens; } + + append(...tokens: Token[]): void + { + this.#tokens.push(...tokens); + } + + merge(coder: Coder): void + { + this.append(...coder.tokens); + } + + generate(): string + { + let code = ''; + let previous: Token = new Token(TokenType.NOTHING, '', 0, 0); + + for (const current of this.#tokens) + { + const glue = this.#needsSpacing(previous, current) ? ' ' : ''; + + code += `${glue}${current.value}`; + + previous = current; + } + + return code; + } + + #needsSpacing(previous: Token, current: Token): boolean + { + if (previous.isType(TokenType.KEYWORD) && this.#isSpacedKeyword(previous)) + { + return true; + } + if (previous.isType(TokenType.OPERATOR) && current.isType(TokenType.OPERATOR)) + { + return true; + } + + return false; + } + + #isSpacedKeyword(token: Token): boolean + { + return isDeclaration(token.value) + || token.hasValue(Keyword.RETURN) + || token.hasValue(Keyword.ASYNC) + || token.hasValue(Keyword.AWAIT) + || token.hasValue(Keyword.YIELD) + || token.hasValue(Keyword.NEW) + || token.hasValue(Keyword.THROW); + } +} diff --git a/packages/analysis/src/static/Lexer.ts b/packages/analysis/src/static/Lexer.ts index 5e03be44..3174e17b 100644 --- a/packages/analysis/src/static/Lexer.ts +++ b/packages/analysis/src/static/Lexer.ts @@ -3,14 +3,17 @@ import CharList from './models/CharList'; import Token from './models/Token'; import TokenList from './models/TokenList'; +import { isBoolean } from './definitions/Boolean'; import { Comment, isComment } from './definitions/Comment'; import { isDivider } from './definitions/Divider'; -import { isEmpty } from './definitions/Empty'; +import { isEmpty, isNothing } from './definitions/Empty'; import { Group, isGroup } from './definitions/Group'; import { isKeyword } from './definitions/Keyword'; import { List, isList } from './definitions/List'; import { isLiteral } from './definitions/Literal'; +import { isNumber, isHexadecimal, isBinary } from './definitions/Number'; import { isOperator, Operator } from './definitions/Operator'; +import { isIndicator } from './definitions/Indicator'; import { Punctuation } from './definitions/Punctuation'; import { isScope } from './definitions/Scope'; import { TokenType } from './definitions/TokenType'; @@ -88,6 +91,13 @@ export default class Lexer return new Token(TokenType.REGEX, value, start, end); } + else if (this.#startsNumber(charList, lastToken)) + { + const value = this.#readNumber(charList); + const end = charList.position; + + return new Token(TokenType.NUMBER, value, start, end); + } else if (isLiteral(char)) { const value = this.#readLiteral(charList); @@ -102,6 +112,13 @@ export default class Lexer return new Token(TokenType.OPERATOR, value, start, end); } + else if (isIndicator(char)) + { + const value = this.#readOperation(charList); + const end = charList.position; + + return new Token(TokenType.INDICATOR, value, start, end); + } else if (isDivider(char)) { const end = charList.position; @@ -132,10 +149,22 @@ export default class Lexer } const value = this.#readIdentifier(charList); - const type = isKeyword(value) ? TokenType.KEYWORD : TokenType.IDENTIFIER; const end = charList.position; - return new Token(type, value, start, end); + if (isKeyword(value)) + { + return new Token(TokenType.KEYWORD, value, start, end); + } + else if (isBoolean(value)) + { + return new Token(TokenType.BOOLEAN, value, start, end); + } + else if (isNothing(value)) + { + return new Token(TokenType.NOTHING, value, start, end); + } + + return new Token(TokenType.IDENTIFIER, value, start, end); } #readComment(charList: CharList): string @@ -236,6 +265,89 @@ export default class Lexer return value; } + #startsNumber(charList: CharList, lastToken: Token | undefined): boolean + { + const current = charList.current; + const next = charList.next; + + if (isNumber(current)) + { + return true; + } + + if (current !== Operator.SUBTRACT) + { + return false; + } + + if (lastToken?.isType(TokenType.NUMBER)) + { + return false; + } + + return isNumber(next); + } + + #endsNumber(char: string, hexadecimal: boolean, binary: boolean): boolean + { + if (hexadecimal) + { + return isHexadecimal(char) === false + && char !== Punctuation.UNDERSCORE; + } + + if (binary) + { + return isBinary(char) === false + && char !== Punctuation.UNDERSCORE; + } + + return isNumber(char) === false + && char !== Punctuation.DOT + && char !== Punctuation.UNDERSCORE + && char !== 'e'; + } + + #readNumber(charList: CharList): string + { + let value = charList.current; + + if (value === Operator.SUBTRACT) + { + value += charList.step(); + } + + charList.step(); + + const hexadecimal = charList.current === 'x'; + const binary = charList.current === 'b'; + + if (hexadecimal || binary) + { + value += charList.current; + + charList.step(); + } + + while (charList.notAtEnd()) + { + const char = charList.current; + + if (this.#endsNumber(char, hexadecimal, binary)) + { + charList.stepBack(); + + break; + } + + value += char; + + charList.step(); + } + + return value; + } + #readLiteral(charList: CharList): string { // Literals are parsed including the start and end characters. diff --git a/packages/analysis/src/static/Parser.ts b/packages/analysis/src/static/Parser.ts index bf98ad47..5cc570d3 100644 --- a/packages/analysis/src/static/Parser.ts +++ b/packages/analysis/src/static/Parser.ts @@ -1,15 +1,19 @@ +import type { ClassVisibility, ClassLocation, VariableType } from '../model'; import { - ESModule, ESMember, ESExport, ESClass, ESFunction, ESField, ESGetter, ESSetter, ESImport, - ESGenerator, ESExpression, ESArray, ESObject, ESAlias, ESScope, ESValue, ESParameter, - ESDestructuredArray, ESDestructuredObject, ESDeclaration, ESIdentifier -} from '../models'; + ESBinding, ESIdentifierBinding, ESArrayBinding, ESObjectBinding, ESBindingElement, + ESBlock, ESExpression, ESVariable, + ESFunction, ESArrowFunction, ESGeneratorFunction, ESParameter, + ESClass, ESClassMember, ESField, ESMethod, ESGeneratorMethod, ESConstructor, ESGetter, ESSetter, + ESModule, ESModuleMember, ESExport, ESImport, ESStatement +} from '../model'; import { Divider, isDivider } from './definitions/Divider'; import { Group } from './definitions/Group'; -import { Keyword, isDeclaration, isKeyword, isNotReserved } from './definitions/Keyword'; +import { Keyword, isDeclaration, isRootKeyword, isContextualKeyword, isNotRootKeyword } from './definitions/Keyword'; import { List } from './definitions/List'; import { Operator } from './definitions/Operator'; +import { Indicator } from './definitions/Indicator'; import { Scope } from './definitions/Scope'; import { TokenType } from './definitions/TokenType'; @@ -23,11 +27,9 @@ import Token from './models/Token'; import TokenList from './models/TokenList'; import Lexer from './Lexer'; +import Coder from './Coder'; -const ANONYMOUS_IDENTIFIER = ''; const DEFAULT_IDENTIFIER = 'default'; -const PRIVATE_INDICATOR = '#'; -const DEFINITION_SEPARATOR = ' '; export default class Parser { @@ -41,117 +43,113 @@ export default class Parser parse(code: string): ESModule { const tokenList = this.#lexer.tokenize(code); - const scope = this.#parseScope(tokenList); + const statements = this.#parseAll(tokenList); - return new ESModule(scope); + return new ESModule(statements); } - parseFirst(code: string): ESMember | ESValue | undefined + parseStatement(code: string): ESStatement { const tokenList = this.#lexer.tokenize(code); + const statement = this.#parseNext(tokenList); - return this.#parseNext(tokenList); - } - - parseValue(code: string): ESValue - { - const model = this.parseFirst(code); - - if ((model instanceof ESValue) === false) + if (statement === undefined) { - throw new UnexpectedParseResult('a value definition'); + throw new UnexpectedParseResult('a statement'); } - return model; + return statement; } parseImport(code: string): ESImport { - const model = this.parseFirst(code); + const tokenList = this.#lexer.tokenize(code); + const declaration = this.#parseKeyword(tokenList); - if ((model instanceof ESImport) === false) + if ((declaration instanceof ESImport) === false) { throw new UnexpectedParseResult('an import definition'); } - return model; + return declaration; } parseExport(code: string): ESExport { - const model = this.parseFirst(code); + const tokenList = this.#lexer.tokenize(code); + const declaration = this.#parseKeyword(tokenList); - if ((model instanceof ESExport) === false) + if ((declaration instanceof ESExport) === false) { throw new UnexpectedParseResult('an export definition'); } - return model; + return declaration; } - parseDeclaration(code: string): ESDeclaration + parseVariable(code: string): ESVariable { - const model = this.parseFirst(code); + const tokenList = this.#lexer.tokenize(code); + const declaration = this.#parseKeyword(tokenList); - if ((model instanceof ESDeclaration) === false) + if ((declaration instanceof ESVariable) === false) { - throw new UnexpectedParseResult('a declaration definition'); + throw new UnexpectedParseResult('a variable definition'); } - return model; + return declaration; } parseFunction(code: string): ESFunction { const tokenList = this.#lexer.tokenize(code); - const model = this.#parseMember(tokenList); + const declaration = this.#parseKeyword(tokenList); - if ((model instanceof ESFunction) === false) + if ((declaration instanceof ESFunction) === false) { throw new UnexpectedParseResult('a function definition'); } - return model; + return declaration; } parseClass(code: string): ESClass { const tokenList = this.#lexer.tokenize(code); - const model = this.#parseMember(tokenList); + const declaration = this.#parseKeyword(tokenList); - if ((model instanceof ESClass) === false) + if ((declaration instanceof ESClass) === false) { throw new UnexpectedParseResult('a class definition'); } - return model; + return declaration; } - #parseScope(tokenList: TokenList): ESScope + #parseAll(tokenList: TokenList): ESStatement[] { - const members: ESMember[] = []; + const statements: ESStatement[] = []; while (tokenList.notAtEnd()) { - const member = this.#parseNext(tokenList); + const statement = this.#parseNext(tokenList); - if (member instanceof ESMember) + if (statement === undefined) { - // Only ES members are of interest - // because they can be exported - - members.push(member); + continue; } + + statements.push(statement); } - return new ESScope(members); + return statements; } - #parseNext(tokenList: TokenList, isAsync = false): ESMember | ESValue | undefined + #parseNext(tokenList: TokenList, isAsync = false): ESStatement | undefined { const token = tokenList.current; - if (token.isType(TokenType.LITERAL)) + if (this.#isValue(token)) { return this.#parseExpression(tokenList); } @@ -168,7 +166,7 @@ export default class Parser } else if (token.isType(TokenType.KEYWORD)) { - if (isNotReserved(token.value)) + if (isContextualKeyword(token.value)) { const next = tokenList.next; const nextIsFunction = next !== undefined && (next.hasValue(Keyword.FUNCTION) || next.hasValue(Group.OPEN)); @@ -179,7 +177,7 @@ export default class Parser return this.#parseNext(tokenList, true); } - else if (next === undefined || this.#atEndOfStatement(next)) + else if (next === undefined || this.#atEndOfExpression(next)) { return this.#parseExpression(tokenList); } @@ -190,7 +188,12 @@ export default class Parser return this.#parseExpression(tokenList); } - return this.#parseMember(tokenList, isAsync); + if (isNotRootKeyword(token.value)) + { + return this.#parseExpression(tokenList); + } + + return this.#parseKeyword(tokenList, isAsync); } else if (token.isType(TokenType.REGEX)) { @@ -198,7 +201,7 @@ export default class Parser } else if (token.hasValue(Group.OPEN)) { - const next = this.#peekAfterBlock(tokenList, Group.OPEN, Group.CLOSE); + const next = this.#peekAfterCollection(tokenList, Group.OPEN, Group.CLOSE); if (next?.hasValue(Operator.ARROW)) { @@ -209,11 +212,13 @@ export default class Parser } else if (token.hasValue(Scope.OPEN)) { - return this.#parseObject(tokenList); + // Object value + return this.#parseExpression(tokenList); } else if (token.hasValue(List.OPEN)) { - return this.#parseArray(tokenList); + // Array value + return this.#parseExpression(tokenList); } else if (token.hasValue(Operator.NOT) || token.hasValue(Operator.SUBTRACT)) { @@ -235,7 +240,16 @@ export default class Parser throw new UnexpectedToken(token.value, token.start); } - #parseMember(tokenList: TokenList, isAsync = false): ESMember + #isValue(token: Token): boolean + { + return token.isType(TokenType.LITERAL) + || token.isType(TokenType.NUMBER) + || token.isType(TokenType.BOOLEAN) + || token.isType(TokenType.NOTHING) + || token.hasValue(Keyword.NEW); + } + + #parseKeyword(tokenList: TokenList, isAsync = false): ESStatement | undefined { const token = tokenList.current; @@ -256,12 +270,16 @@ export default class Parser return this.#parseFunction(tokenList, isAsync); case Keyword.VAR: + return this.#parseVariable(tokenList, 'var'); + case Keyword.LET: + return this.#parseVariable(tokenList, 'let'); + case Keyword.CONST: - return this.#parseDeclaration(tokenList, false, true); + return this.#parseVariable(tokenList, 'const'); case Keyword.ASYNC: - return this.#parseMember(tokenList, true); + return this.#parseNext(tokenList, true); default: throw new UnexpectedKeyword(token.value, token.start); @@ -270,19 +288,21 @@ export default class Parser #parseImport(tokenList: TokenList): ESImport { - const members: ESAlias[] = []; + const members: ESModuleMember[] = []; let token = tokenList.current; if (token.isType(TokenType.LITERAL)) { - return new ESImport(members, token.value); + const from = this.#parseFrom(token.value); + + return new ESImport(members, from); } else if (token.hasValue(Group.OPEN)) { token = tokenList.step(); // Read away the open group - const from = token.value; + const from = this.#parseFrom(token.value); tokenList.step(2); // Read away the from value and scope close @@ -292,21 +312,21 @@ export default class Parser if (token.hasValue(Scope.OPEN) === false) { // Keep the * indicator, otherwise use the default identifier - const name = token.hasValue(Operator.MULTIPLY) ? Operator.MULTIPLY : DEFAULT_IDENTIFIER; + const identifier = token.hasValue(Operator.MULTIPLY) ? Operator.MULTIPLY : DEFAULT_IDENTIFIER; - let as = token.value; + let alias = token.value; - token = tokenList.step(); // Read away the name + token = tokenList.step(); // Read away the identifier if (token.hasValue(Keyword.AS)) { token = tokenList.step(); // Read away the AS keyword - as = token.value; + alias = token.value; - token = tokenList.step(); // Read away the alias name + token = tokenList.step(); // Read away the alias identifier } - members.push(new ESAlias(name, as)); + members.push(new ESModuleMember(identifier, alias)); } if (token.hasValue(Divider.SEPARATOR)) @@ -316,9 +336,9 @@ export default class Parser if (token.hasValue(Scope.OPEN)) { - const aliases = this.#parseAliasList(tokenList); + const parsedMembers = this.#parseModuleMembers(tokenList); - members.push(...aliases); + members.push(...parsedMembers); token = tokenList.current; } @@ -329,7 +349,8 @@ export default class Parser } token = tokenList.step(); // Read away the FROM keyword - const from = token.value; + + const from = this.#parseFrom(token.value); tokenList.step(); // Read away the source @@ -359,6 +380,21 @@ export default class Parser let token = tokenList.current; let stepSize = 0; + if (isDefault && this.#doesExportValue(tokenList)) + { + // Default exports of values need to move to their own declaration + + const identifier = `$_EXPORT_${token.start}_${token.end}`; + + tokenList.insert( + new Token(TokenType.KEYWORD, Keyword.CONST, 0, 0), + new Token(TokenType.IDENTIFIER, identifier, 0, 0), + new Token(TokenType.OPERATOR, Operator.ASSIGN, 0, 0) + ); + + token = tokenList.current; + } + if (token.hasValue(Keyword.ASYNC)) { token = tokenList.step(); // Read away the async keyword @@ -371,8 +407,9 @@ export default class Parser stepSize++; } - const name = this.#isIdentifier(token) ? token.value : ANONYMOUS_IDENTIFIER; - const as = isDefault ? DEFAULT_IDENTIFIER : name; + const identifier = this.#isIdentifier(token) ? token.value : ''; + const alias = isDefault ? DEFAULT_IDENTIFIER : undefined; + let from: string | undefined = undefined; token = tokenList.step(); // Read away the name @@ -380,8 +417,7 @@ export default class Parser if (token?.hasValue(Keyword.FROM)) { token = tokenList.step(); // Read away the FROM keyword - - from = token.value; + from = this.#parseFrom(token.value); } if (stepSize > 0) @@ -391,14 +427,28 @@ export default class Parser tokenList.stepBack(stepSize); // Step back to the original position } - const alias = new ESAlias(name, as); + const member = new ESModuleMember(identifier, alias); - return new ESExport([alias], from); + return new ESExport([member], from); + } + + #doesExportValue(tokenList: TokenList): boolean + { + const current = tokenList.current; + + if (this.#isValue(current)) + { + return true; + } + + const next = tokenList.next; + + return this.#isIdentifier(current) && next?.hasValue(Group.OPEN); // function call } #parseMultiExport(tokenList: TokenList): ESExport { - const members = this.#parseAliasList(tokenList); + const members = this.#parseModuleMembers(tokenList); let from: string | undefined = undefined; let token = tokenList.current; @@ -406,7 +456,7 @@ export default class Parser if (token?.hasValue(Keyword.FROM)) { token = tokenList.step(); // Read away the FROM keyword - from = token.value; + from = this.#parseFrom(token.value); } tokenList.step(); // Read away the source @@ -414,9 +464,14 @@ export default class Parser return new ESExport(members, from); } - #parseAliasList(tokenList: TokenList): ESAlias[] + #parseFrom(from: string): string + { + return from.slice(1, -1); + } + + #parseModuleMembers(tokenList: TokenList): ESModuleMember[] { - const aliases = []; + const members = []; let token = tokenList.step(); // Read away the scope open @@ -436,109 +491,151 @@ export default class Parser continue; } - const alias = this.#parseAlias(tokenList); + const member = this.#parseModuleMember(tokenList); - aliases.push(alias); + members.push(member); token = tokenList.step(); } - return aliases; + return members; } - #parseAlias(tokenList: TokenList): ESAlias + #parseModuleMember(tokenList: TokenList): ESModuleMember { let token = tokenList.current; - const name = token.value; - let as = name; + const identifier = token.value; + let alias: string | undefined = undefined; if (tokenList.next.hasValue(Keyword.AS)) { token = tokenList.step(2); // Read away the AS keyword - as = token.value; + alias = token.value; } - return new ESAlias(name, as); + return new ESModuleMember(identifier, alias); } - #parseDeclaration(tokenList: TokenList, isStatic: boolean, parseMultiple = false): ESMember + #parseVariable(tokenList: TokenList, type: VariableType): ESVariable { - let token = tokenList.current; - let identifier: ESIdentifier; - let isPrivate = false; + const binding = this.#parseBinding(tokenList); + const initializer = this.#parseInitializer(tokenList); + + return new ESVariable(type, binding, initializer); + } + #parseBinding(tokenList: TokenList): ESBinding + { + const token = tokenList.current; + if (token.hasValue(List.OPEN)) { - identifier = this.#parseDestructuredArray(tokenList); - token = tokenList.current; + return this.#parseArrayBinding(tokenList); } else if (token.hasValue(Scope.OPEN)) { - identifier = this.#parseDestructuredObject(tokenList); - token = tokenList.current; - } - else - { - isPrivate = token.value.startsWith(PRIVATE_INDICATOR); - identifier = isPrivate ? token.value.substring(1) : token.value; - token = tokenList.step(); // Read away the identifier + return this.#parseObjectBinding(tokenList); } + + return this.#parseIdentifierBinding(tokenList); + } - let value = undefined; + #parseArrayBinding(tokenList: TokenList): ESArrayBinding + { + const elements = this.#parseBindingElements(tokenList, List.CLOSE); - if (token.hasValue(Operator.ASSIGN)) - { - tokenList.step(); // Read away the assignment operator + return new ESArrayBinding(elements); + } - value = this.#parseNext(tokenList, false); - token = tokenList.current; - } + #parseObjectBinding(tokenList: TokenList): ESObjectBinding + { + const elements = this.#parseBindingElements(tokenList, Scope.CLOSE); + + return new ESObjectBinding(elements); + } - if (token !== undefined) + #parseBindingElements(tokenList: TokenList, closeId: string): ESBindingElement[] + { + const elements = []; + + tokenList.step(); // Read away the group open + + while (tokenList.notAtEnd()) { - if (token.hasValue(Divider.TERMINATOR)) + const token = tokenList.current; + + if (token.hasValue(closeId)) { - tokenList.step(); // Read away the terminator + // End of the element list + + tokenList.step(); // Read away the group close + + break; } - else if (parseMultiple === true && token.hasValue(Divider.SEPARATOR)) + else if (token.hasValue(Divider.SEPARATOR)) { - // Parse away the next declaration without saving it. - // Note that this is a known limitation as described in the readme. + // End of element tokenList.step(); // Read away the separator - this.#parseDeclaration(tokenList, isStatic, true); + continue; } - } - // Now we have the value we need to check if we need to recreate it - // with the correct name, static and private properties. + const binding = this.#parseBinding(tokenList); + const initializer = this.#parseInitializer(tokenList); - if (value instanceof ESGenerator) - { - return new ESGenerator(identifier.toString(), value.parameters, value.body, isStatic, value.isAsync, isPrivate); + const element = new ESBindingElement(binding, initializer); + elements.push(element); } - else if (value instanceof ESFunction) + + return elements; + } + + #parseIdentifierBinding(tokenList: TokenList): ESIdentifierBinding + { + let token = tokenList.current; + let isRest = false; + + if (token.hasValue(Operator.SPREAD)) { - return new ESFunction(identifier.toString(), value.parameters, value.body, isStatic, value.isAsync, isPrivate); + isRest = true; + token = tokenList.step(); } - else if (value instanceof ESClass) + + const identifier = token.value; + + tokenList.step(); // Read away the identifier + + return new ESIdentifierBinding(identifier, isRest); + } + + #parseInitializer(tokenList: TokenList): ESStatement | undefined + { + const token = tokenList.current; + + if (token.hasValue(Operator.ASSIGN) === false) { - return new ESClass(identifier.toString(), value.parentName, value.scope); + if (token.hasValue(Divider.TERMINATOR)) + { + tokenList.step(); // Read away the terminator + } + + return undefined; } - return new ESDeclaration(identifier, value as ESValue, isStatic, isPrivate); + tokenList.step(); // Read away the assignment operator + + return this.#parseNext(tokenList, false); } - #parseFunction(tokenList: TokenList, isAsync: boolean, isStatic = false, isGetter = false, isSetter = false): ESFunction + #parseFunction(tokenList: TokenList, isAsync: boolean): ESFunction { let token = tokenList.current; - let name = ANONYMOUS_IDENTIFIER; + let identifier: string | undefined = undefined; let isGenerator = false; - let isPrivate = false; - if (token.hasValue(Operator.MULTIPLY)) + if (token.hasValue(Indicator.GENERATOR)) { isGenerator = true; @@ -547,13 +644,12 @@ export default class Parser if (this.#isIdentifier(token)) { - isPrivate = token.value.startsWith(PRIVATE_INDICATOR); - name = isPrivate ? token.value.substring(1) : token.value; + identifier = token.value; - tokenList.step(); // Read away the function name + tokenList.step(); // Read away the function identifier } - const parameters = this.#parseParameters(tokenList, Group.CLOSE); + const parameters = this.#parseBindingElements(tokenList, Group.CLOSE); token = tokenList.current; @@ -562,40 +658,35 @@ export default class Parser throw new ExpectedToken(Scope.OPEN, token.start); } - const body = this.#parseBlock(tokenList, Scope.OPEN, Scope.CLOSE); + const body = this.#parseBlock(tokenList); if (isGenerator) { - return new ESGenerator(name, parameters, body, isStatic, isAsync, isPrivate); - } - else if (isGetter) - { - return new ESGetter(name, parameters, body, isStatic, isAsync, isPrivate); + return new ESGeneratorFunction(identifier, parameters, body, isAsync); } - else if (isSetter) - { - return new ESSetter(name, parameters, body, isStatic, isAsync, isPrivate); - } - - return new ESFunction(name, parameters, body, isStatic, isAsync, isPrivate); + + return new ESFunction(identifier, parameters, body, isAsync); } - #parseArrowFunction(tokenList: TokenList, isAsync: boolean): ESFunction + #parseArrowFunction(tokenList: TokenList, isAsync: boolean): ESArrowFunction { let token = tokenList.current; let parameters: ESParameter[]; if (token.hasValue(Group.OPEN)) { - parameters = this.#parseParameters(tokenList, Group.CLOSE); - token = tokenList.current; + parameters = this.#parseBindingElements(tokenList, Group.CLOSE); } else { - parameters = [new ESField(token.value, undefined)]; - token = tokenList.step(); + const binding = this.#parseIdentifierBinding(tokenList); + const parameter = new ESBindingElement(binding, undefined); + + parameters = [parameter]; } + token = tokenList.current; + if (token.hasValue(Operator.ARROW) === false) { throw new ExpectedToken(Operator.ARROW, token.start); @@ -603,77 +694,40 @@ export default class Parser token = tokenList.step(); // Read away the arrow - const body = token.hasValue(Scope.OPEN) - ? this.#parseBlock(tokenList, Scope.OPEN, Scope.CLOSE) - : this.#parseExpression(tokenList).definition; - - return new ESFunction(ANONYMOUS_IDENTIFIER, parameters, body, false, isAsync, false); - } - - #parseParameters(tokenList: TokenList, closeId: string): ESParameter[] - { - const parameters = []; - - tokenList.step(); // Read away the group open + let body: ESBlock; - while (tokenList.notAtEnd()) + if (token.hasValue(Scope.OPEN)) { - const token = tokenList.current; - - if (token.hasValue(closeId)) - { - // End of the parameter list - - tokenList.step(); // Read away the group close - - break; - } - else if (token.hasValue(Divider.SEPARATOR)) - { - // End of parameter - - tokenList.step(); // Read away the separator - - continue; - } - - let parameter; - - if (token.hasValue(Scope.OPEN)) - { - parameter = this.#parseDestructuredObject(tokenList); - } - else if (token.hasValue(List.OPEN)) - { - parameter = this.#parseDestructuredArray(tokenList); - } - else - { - parameter = this.#parseField(tokenList); - } + body = this.#parseBlock(tokenList); + } + else + { + const expression = this.#parseExpression(tokenList); - parameters.push(parameter); + body = new ESBlock(expression.code); } - return parameters; + return new ESArrowFunction(parameters, body, isAsync); } #parseClass(tokenList: TokenList): ESClass { let token = tokenList.current; - let name = ANONYMOUS_IDENTIFIER; + + let identifier: string | undefined = undefined; let parent: string | undefined = undefined; if (this.#isIdentifier(token)) { - name = token.value; + identifier = token.value; - token = tokenList.step(); // Read away the class name + token = tokenList.step(); // Read away the class identifier } if (token.hasValue(Keyword.EXTENDS)) { token = tokenList.step(); // Read away the extends keyword + parent = token.value; token = tokenList.step(); // Read away the extends name @@ -684,12 +738,12 @@ export default class Parser throw new ExpectedToken(Scope.OPEN, token.start); } - const scope = this.#parseClassScope(tokenList); + const members = this.#parseClassMembers(tokenList); - return new ESClass(name, parent, scope); + return new ESClass(identifier, parent, members); } - #parseClassScope(tokenList: TokenList): ESScope + #parseClassMembers(tokenList: TokenList): ESClassMember[] { let token = tokenList.step(); // Read away the scope open @@ -711,40 +765,48 @@ export default class Parser token = tokenList.current; } - return new ESScope(members); + return members; } - #parseClassMember(tokenList: TokenList): ESMember + #parseClassMember(tokenList: TokenList): ESClassMember { let token = tokenList.current; + let visibility: ClassVisibility = 'public'; + let location: ClassLocation = 'instance'; let isAsync = false; - let isStatic = false; - let isGetter = false; - let isSetter = false; while (tokenList.notAtEnd()) { - if (token.hasValue(Keyword.STATIC)) + if (token.hasValue(Indicator.PRIVATE)) + { + visibility = 'private'; + } + else if (token.hasValue(Keyword.STATIC)) { - isStatic = true; + location = 'static'; } else if (token.hasValue(Keyword.ASYNC)) { isAsync = true; } + else if (token.hasValue(Keyword.CONSTRUCTOR)) + { + return this.#parseConstructor(tokenList); + } else if (token.hasValue(Keyword.GET)) { - isGetter = true; + return this.#parseGetter(tokenList, location); } else if (token.hasValue(Keyword.SET)) { - isSetter = true; + return this.#parseSetter(tokenList, location); } - else if (token.hasValue(Operator.MULTIPLY)) + else if (token.hasValue(Indicator.GENERATOR)) { - // Generator function - return this.#parseFunction(tokenList, isAsync, isStatic, false, false); + tokenList.step(); // Read away the generator indicator + + return this.#parseMethod(tokenList, visibility, location, isAsync, true); } else { @@ -757,114 +819,225 @@ export default class Parser const nextToken = tokenList.next; return nextToken.hasValue(Group.OPEN) - ? this.#parseFunction(tokenList, isAsync, isStatic, isGetter, isSetter) - : this.#parseDeclaration(tokenList, isStatic); + ? this.#parseMethod(tokenList, visibility, location, isAsync, false) + : this.#parseField(tokenList, visibility, location); } - #parseArray(tokenList: TokenList): ESArray + #parseConstructor(tokenList: TokenList): ESConstructor { - const items = this.#parseBlock(tokenList, List.OPEN, List.CLOSE); + tokenList.step(); // Read away the constructor keyword + + const parameters = this.#parseBindingElements(tokenList, Group.CLOSE); - return new ESArray(items); - } + const token = tokenList.current; - #parseDestructuredArray(tokenList: TokenList): ESDestructuredArray - { - const fields = this.#parseParameters(tokenList, List.CLOSE); + if (token.hasValue(Scope.OPEN) === false) + { + throw new ExpectedToken(Scope.OPEN, token.start); + } + + const body = this.#parseBlock(tokenList); - return new ESDestructuredArray(fields); + return new ESConstructor(parameters, body); } - #parseObject(tokenList: TokenList): ESObject + #parseGetter(tokenList: TokenList, location: ClassLocation): ESGetter { - const fields = this.#parseBlock(tokenList, Scope.OPEN, Scope.CLOSE); + let visibility: ClassVisibility = 'public'; + + let token = tokenList.step(); // Read away the get keyword + + if (token.hasValue(Indicator.PRIVATE)) + { + visibility = 'private'; + + token = tokenList.step(); // Read away the private indicator + } - return new ESObject(fields); + const identifier = token.value; + + tokenList.step(); // Read away the identifier + + const parameters = this.#parseBindingElements(tokenList, Group.CLOSE); + + if (parameters.length !== 0) + { + throw new UnexpectedParseResult('an empty parameter list'); + } + + token = tokenList.current; + + if (token.hasValue(Scope.OPEN) === false) + { + throw new ExpectedToken(Scope.OPEN, token.start); + } + + const body = this.#parseBlock(tokenList); + + return new ESGetter(identifier, visibility, location, body); } - #parseDestructuredObject(tokenList: TokenList): ESDestructuredObject + #parseSetter(tokenList: TokenList, location: ClassLocation): ESSetter { - const fields = this.#parseParameters(tokenList, Scope.CLOSE); + let visibility: ClassVisibility = 'public'; + + let token = tokenList.step(); // Read away the set keyword + + if (token.hasValue(Indicator.PRIVATE)) + { + visibility = 'private'; + + token = tokenList.step(); // Read away the private indicator + } + + const identifier = token.value; + + tokenList.step(); // Read away the identifier + + const parameters = this.#parseBindingElements(tokenList, Group.CLOSE); + + token = tokenList.current; - return new ESDestructuredObject(fields); + if (parameters.length !== 1) + { + throw new UnexpectedParseResult('exactly one setter parameter'); + } + + if (token.hasValue(Scope.OPEN) === false) + { + throw new ExpectedToken(Scope.OPEN, token.start); + } + + const body = this.#parseBlock(tokenList); + + return new ESSetter(identifier, visibility, location, parameters[0], body); } - #parseField(tokenList: TokenList): ESField + #parseMethod(tokenList: TokenList, visibility: ClassVisibility, location: ClassLocation, isAsync: boolean, isGenerator: boolean): ESMethod { let token = tokenList.current; + + const identifier = token.value; - const name = token.value; + tokenList.step(); // Read away the identifier - token = tokenList.step(); // Read away the name + const parameters = this.#parseBindingElements(tokenList, Group.CLOSE); - let value = undefined; + token = tokenList.current; - if (token.hasValue(Operator.ASSIGN)) + if (token.hasValue(Scope.OPEN) === false) { - tokenList.step(); // Read away the assignment operator + throw new ExpectedToken(Scope.OPEN, token.start); + } + + const body = this.#parseBlock(tokenList); - value = this.#parseNext(tokenList, false) as ESValue; + if (isGenerator) + { + return new ESGeneratorMethod(identifier, visibility, location, parameters, body, isAsync); } - return new ESField(name, value); + return new ESMethod(identifier, visibility, location, parameters, body, isAsync); + } + + #parseField(tokenList: TokenList, visibility: ClassVisibility, location: ClassLocation): ESField + { + const token = tokenList.current; + + const identifier = token.value; + + tokenList.step(); // Read away the name + + const initializer = this.#parseInitializer(tokenList); + + return new ESField(identifier, visibility, location, initializer); + } + + #parseBlock(tokenList: TokenList): ESBlock + { + const coder = this.#parseCollectionToCode(tokenList, Scope.OPEN, Scope.CLOSE); + + const code = coder.generate(); + + return new ESBlock(code); } #parseExpression(tokenList: TokenList): ESExpression { + const coder = new Coder(); + let token = tokenList.current; - let code = ''; while (tokenList.notAtEnd()) { if (token.hasValue(List.OPEN)) { - const array = this.#parseBlock(tokenList, List.OPEN, List.CLOSE); + const array = this.#parseCollectionToCode(tokenList, List.OPEN, List.CLOSE); + + coder.merge(array); - code += array + DEFINITION_SEPARATOR; token = tokenList.current; } else if (token.hasValue(Group.OPEN)) { - const group = this.#parseBlock(tokenList, Group.OPEN, Group.CLOSE); + const group = this.#parseCollectionToCode(tokenList, Group.OPEN, Group.CLOSE); + + coder.merge(group); - code += group + DEFINITION_SEPARATOR; token = tokenList.current; } else if (token.hasValue(Scope.OPEN)) { - const scope = this.#parseBlock(tokenList, Scope.OPEN, Scope.CLOSE); + const scope = this.#parseCollectionToCode(tokenList, Scope.OPEN, Scope.CLOSE); + + coder.merge(scope); - code += scope + DEFINITION_SEPARATOR; token = tokenList.current; } else { - code += token.toString() + DEFINITION_SEPARATOR; + coder.append(token); + token = tokenList.step(); } - if (token === undefined || this.#atEndOfStatement(token)) + if (token === undefined) + { + break; + } + + if (this.#atEndOfExpression(token)) { - // End of the list + if (token.hasValue(Divider.TERMINATOR)) + { + tokenList.step(); // Read away the terminator + } break; } } - return new ESExpression(code.trim()); + const code = coder.generate(); + + return new ESExpression(code); } - #parseBlock(tokenList: TokenList, openId: string, closeId: string): string + #parseCollectionToCode(tokenList: TokenList, openId: string, closeId: string): Coder { - let token = tokenList.step(); // Read away the open + let token = tokenList.current; + + const coder = new Coder([token]); - let code = openId + DEFINITION_SEPARATOR; + token = tokenList.step(); // Read away the open while (tokenList.notAtEnd()) { if (token.hasValue(openId)) { - code += this.#parseBlock(tokenList, openId, closeId) + DEFINITION_SEPARATOR; + const collection = this.#parseCollectionToCode(tokenList, openId, closeId); + + coder.merge(collection); + token = tokenList.current; continue; @@ -873,23 +1046,24 @@ export default class Parser { tokenList.step(); // Read away the close - code += closeId; + coder.append(token); - return code; + return coder; } - code += token.toString() + DEFINITION_SEPARATOR; + coder.append(token); + token = tokenList.step(); } - return code; + return coder; } - #peekAfterBlock(tokenList: TokenList, openId: string, closeId: string): Token | undefined + #peekAfterCollection(tokenList: TokenList, openId: string, closeId: string): Token | undefined { const start = tokenList.position; - this.#parseBlock(tokenList, openId, closeId); + this.#parseCollectionToCode(tokenList, openId, closeId); const token = tokenList.current; const end = tokenList.position; @@ -899,16 +1073,16 @@ export default class Parser return token; } - #atEndOfStatement(token: Token): boolean + #atEndOfExpression(token: Token): boolean { return [Divider.TERMINATOR, Divider.SEPARATOR].includes(token.value) || [List.CLOSE, Group.CLOSE, Scope.CLOSE].includes(token.value) - || isKeyword(token.value); + || isRootKeyword(token.value); } #isIdentifier(token: Token): boolean { return token.isType(TokenType.IDENTIFIER) - || (token.isType(TokenType.KEYWORD) && isNotReserved(token.value)); + || (token.isType(TokenType.KEYWORD) && isContextualKeyword(token.value)); } } diff --git a/packages/analysis/src/static/definitions/Boolean.ts b/packages/analysis/src/static/definitions/Boolean.ts new file mode 100644 index 00000000..b947e89f --- /dev/null +++ b/packages/analysis/src/static/definitions/Boolean.ts @@ -0,0 +1,9 @@ + +const BOOLEAN = ['true', 'false']; + +function isBoolean(value: string) +{ + return BOOLEAN.includes(value); +} + +export { isBoolean }; diff --git a/packages/analysis/src/static/definitions/Empty.ts b/packages/analysis/src/static/definitions/Empty.ts index 723456f0..fbb7a278 100644 --- a/packages/analysis/src/static/definitions/Empty.ts +++ b/packages/analysis/src/static/definitions/Empty.ts @@ -6,6 +6,8 @@ const Empty = STRING: '' }; +const NOTHING = ['null', 'undefined']; + const Empties = Object.values(Empty) as unknown[]; function isEmpty(value: unknown): boolean @@ -13,4 +15,9 @@ function isEmpty(value: unknown): boolean return Empties.includes(value); } -export { Empty, isEmpty }; +function isNothing(value: string) +{ + return NOTHING.includes(value); +} + +export { Empty, isEmpty, isNothing }; diff --git a/packages/analysis/src/static/definitions/Indicator.ts b/packages/analysis/src/static/definitions/Indicator.ts new file mode 100644 index 00000000..52fdb4cc --- /dev/null +++ b/packages/analysis/src/static/definitions/Indicator.ts @@ -0,0 +1,15 @@ + +const Indicator = +{ + GENERATOR: '*', + PRIVATE: '#' +}; + +const Indicators = Object.values(Indicator); + +function isIndicator(value: string): boolean +{ + return Indicators.includes(value); +} + +export { Indicator, isIndicator }; diff --git a/packages/analysis/src/static/definitions/Keyword.ts b/packages/analysis/src/static/definitions/Keyword.ts index 1f02cbf2..d1109942 100644 --- a/packages/analysis/src/static/definitions/Keyword.ts +++ b/packages/analysis/src/static/definitions/Keyword.ts @@ -1,49 +1,103 @@ const Keyword = { + // Modules + IMPORT: 'import', EXPORT: 'export', + AS: 'as', + FROM: 'from', DEFAULT: 'default', - CLASS: 'class', - FUNCTION: 'function', + + // Declarations + ASYNC: 'async', + AWAIT: 'await', + USING: 'using', CONST: 'const', LET: 'let', VAR: 'var', - AS: 'as', - FROM: 'from', - IMPORT: 'import', + + // Functions + FUNCTION: 'function', + RETURN: 'return', + YIELD: 'yield', + + // Classes + CLASS: 'class', + CONSTRUCTOR: 'constructor', GET: 'get', SET: 'set', EXTENDS: 'extends', STATIC: 'static', - ASYNC: 'async', - RETURN: 'return', - // Other keywords are not needed in the parse - // process and must be treated as identifiers. + NEW: 'new', + THIS: 'this', + + // Iterations + DO: 'do', + WHILE: 'while', + FOR: 'for', + OF: 'of', + IN: 'in', + BREAK: 'break', + CONTINUE: 'continue', + + // Control flow + IF: 'if', + ELSE: 'else', + SWITCH: 'switch', + CASE: 'case', + + // Exceptions + THROW: 'throw', + TRY: 'try', + CATCH: 'catch', + FINALLY: 'finally' }; const Keywords = Object.values(Keyword); +const RootKeywords = // That we parse at module level +[ + Keyword.IMPORT, Keyword.EXPORT, + Keyword.VAR, Keyword.LET, Keyword.CONST, Keyword.FUNCTION, Keyword.CLASS, + Keyword.ASYNC +]; + +const DeclarationKeywords = // Things to import / export +[ + Keyword.VAR, Keyword.LET, Keyword.CONST, + Keyword.FUNCTION, Keyword.CLASS, + Keyword.USING +]; + +const ContextualKeywords = // Are also valid identifiers +[ + Keyword.AS, Keyword.ASYNC, Keyword.GET, Keyword.SET, + Keyword.OF, Keyword.YIELD, Keyword.FROM, Keyword.STATIC +]; + function isKeyword(value: string): boolean { return Keywords.includes(value); } +function isRootKeyword(value: string): boolean +{ + return RootKeywords.includes(value); +} + +function isNotRootKeyword(value: string): boolean +{ + return isRootKeyword(value) === false; +} + function isDeclaration(value: string): boolean { - return value === Keyword.CLASS - || value === Keyword.FUNCTION - || value === Keyword.CONST - || value === Keyword.LET - || value === Keyword.VAR; + return DeclarationKeywords.includes(value); } -function isNotReserved(value: string): boolean +function isContextualKeyword(value: string): boolean { - return value === Keyword.AS - || value === Keyword.ASYNC - || value === Keyword.FROM - || value === Keyword.GET - || value === Keyword.SET; + return ContextualKeywords.includes(value); } -export { Keyword, isKeyword, isDeclaration, isNotReserved }; +export { Keyword, isKeyword, isRootKeyword, isNotRootKeyword, isDeclaration, isContextualKeyword }; diff --git a/packages/analysis/src/static/definitions/Number.ts b/packages/analysis/src/static/definitions/Number.ts new file mode 100644 index 00000000..e896ee16 --- /dev/null +++ b/packages/analysis/src/static/definitions/Number.ts @@ -0,0 +1,21 @@ + +const NUMBERS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; +const HEXADECIMAL = [...NUMBERS, 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F']; +const BINARY = ['0', '1']; + +function isNumber(value: string): boolean +{ + return NUMBERS.includes(value); +} + +function isHexadecimal(value: string): boolean +{ + return HEXADECIMAL.includes(value); +} + +function isBinary(value: string): boolean +{ + return BINARY.includes(value); +} + +export { isNumber, isHexadecimal, isBinary }; diff --git a/packages/analysis/src/static/definitions/Operator.ts b/packages/analysis/src/static/definitions/Operator.ts index dbc008a2..5cc78056 100644 --- a/packages/analysis/src/static/definitions/Operator.ts +++ b/packages/analysis/src/static/definitions/Operator.ts @@ -3,25 +3,31 @@ const Operator = { ADD: '+', ARROW: '=>', + ASSIGN: '=', ASSIGN_ADD: '+=', ASSIGN_BITWISE_AND: '&=', ASSIGN_BITWISE_OR: '|=', + ASSIGN_BITWISE_XOR: '^=', ASSIGN_DIVIDE: '/=', + ASSIGN_EXPONENTIAL: '**=', ASSIGN_LEFT_SHIFT: '<<=', ASSIGN_LOGICAL_AND: '&&=', ASSIGN_LOGICAL_OR: '||=', - ASSIGN_MODULO: '%=', + ASSIGN_REMAINDER: '%=', ASSIGN_MULTIPLY: '*=', + ASSIGN_NULLISH: '??=', ASSIGN_RIGHT_SHIFT: '>>=', ASSIGN_SUBTRACT: '-=', - ASSIGN_XOR: '^=', BITWISE_AND: '&', BITWISE_OR: '|', + BITWISE_NOT: '~', + BITWISE_XOR: '^', DECREMENT: '--', DIVIDE: '/', EQUAL: '==', EQUAL_STRICT: '===', + EXPONENTIAL: '**', GREATER: '>', GREATER_EQUAL: '>=', INCREMENT: '++', @@ -30,15 +36,20 @@ const Operator = LESS_EQUAL: '<=', LOGICAL_AND: '&&', LOGICAL_OR: '||', - MODULO: '%', + REMAINDER: '%', MULTIPLY: '*', + NULLISH: '??', + CHAINING: '.', + OPTIONAL_CHAINING: '?.', + SPREAD: '...', NOT: '!', NOT_EQUAL: '!=', NOT_EQUAL_STRICT: '!==', RIGHT_SHIFT: '>>', SUBTRACT: '-', TERNARY: '?', - XOR: '^' + + $BRIDGE_CHAINING_SPREAD: '..' }; const Operators = Object.values(Operator); diff --git a/packages/analysis/src/static/definitions/Punctuation.ts b/packages/analysis/src/static/definitions/Punctuation.ts index ce98fb60..529b9172 100644 --- a/packages/analysis/src/static/definitions/Punctuation.ts +++ b/packages/analysis/src/static/definitions/Punctuation.ts @@ -15,7 +15,8 @@ const Punctuation = RIGHT_BRACE: '}', SINGLE_QUOTE: '\'', DOUBLE_QUOTE: '"', - BACKTICK: '`' + BACKTICK: '`', + UNDERSCORE: '_' }; const Punctuations = Object.values(Punctuation); diff --git a/packages/analysis/src/static/definitions/TokenType.ts b/packages/analysis/src/static/definitions/TokenType.ts index c3edd4a9..165cab04 100644 --- a/packages/analysis/src/static/definitions/TokenType.ts +++ b/packages/analysis/src/static/definitions/TokenType.ts @@ -1,6 +1,7 @@ const TokenType = { + BOOLEAN: 'boolean', COMMENT: 'comment', DIVIDER: 'divider', GROUP: 'group', @@ -8,7 +9,10 @@ const TokenType = KEYWORD: 'keyword', LIST: 'list', LITERAL: 'literal', + NOTHING: 'nothing', + NUMBER: 'number', OPERATOR: 'operator', + INDICATOR: 'indicator', REGEX: 'regex', SCOPE: 'scope', WHITESPACE: 'whitespace' diff --git a/packages/analysis/src/static/index.ts b/packages/analysis/src/static/index.ts index 957dc6a1..a03965ab 100644 --- a/packages/analysis/src/static/index.ts +++ b/packages/analysis/src/static/index.ts @@ -12,5 +12,5 @@ export * from './definitions/Scope'; export * from './definitions/TokenType'; export * from './definitions/Whitespace'; -export { default as Lexer } from './Lexer'; +export { default as Lexer} from './Lexer'; export { default as Parser } from './Parser'; diff --git a/packages/analysis/src/static/models/ItemList.ts b/packages/analysis/src/static/models/ItemList.ts index 1fada904..dada7a83 100644 --- a/packages/analysis/src/static/models/ItemList.ts +++ b/packages/analysis/src/static/models/ItemList.ts @@ -52,4 +52,9 @@ export default class ItemList { return this.#position + 1 < this.#items.length; } + + insert(...item: T[]): void + { + this.#items.splice(this.#position, 0, ...item); + } } diff --git a/packages/analysis/test/dynamic/Reflector.spec.ts b/packages/analysis/test/dynamic/Reflector.spec.ts index 8bead38a..eac72a8a 100644 --- a/packages/analysis/test/dynamic/Reflector.spec.ts +++ b/packages/analysis/test/dynamic/Reflector.spec.ts @@ -1,7 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { ESExpression, ESField } from '../../src/models'; import { Reflector } from '../../src/dynamic'; import { CLASSES, FUNCTIONS, OBJECTS, MODULES } from './fixtures'; @@ -16,20 +15,20 @@ describe('Reflector', () => { const reflectionModule = reflector.fromModule(MODULES.MIXED, false); - const members = reflectionModule.members; - expect(members.length).toBe(3); + const statements = reflectionModule.statements; + expect(statements).toHaveLength(3); - const declarations = reflectionModule.declarations; - expect(declarations.length).toBe(1); - expect(declarations[0].name).toBe('johnDoe'); + const variables = reflectionModule.variables; + expect(variables).toHaveLength(1); + expect(variables[0].identifier).toEqual('johnDoe'); const functions = reflectionModule.functions; - expect(functions.length).toBe(1); - expect(functions[0].name).toBe('requiredFunction'); + expect(functions).toHaveLength(1); + expect(functions[0].identifier).toEqual('requiredFunction'); const classes = reflectionModule.classes; - expect(classes.length).toBe(1); - expect(classes[0].name).toBe('Child'); + expect(classes).toHaveLength(1); + expect(classes[0].identifier).toEqual('Child'); }); }); @@ -38,105 +37,98 @@ describe('Reflector', () => it('should get class reflection model without parent members from a class', () => { const reflectionClass = reflector.fromClass(CLASSES.Child, false); - expect(reflectionClass.name).toBe('Child'); + expect(reflectionClass.identifier).toEqual('Child'); + expect(reflectionClass.members).toHaveLength(9); + expect(reflectionClass.construct).toBeDefined(); - const members = reflectionClass.members; - expect(members.length).toBe(9); - - const declarations = reflectionClass.declarations; - expect(declarations.length).toBe(4); - expect(declarations[0].name).toBe('firstName'); - expect(declarations[1].name).toBe('lastName'); - expect(declarations[2].name).toBe('age'); - expect(declarations[3].name).toBe('state'); + const fields = reflectionClass.fields; + expect(fields).toHaveLength(4); + expect(fields[0].identifier).toEqual('firstName'); + expect(fields[1].identifier).toEqual('lastName'); + expect(fields[2].identifier).toEqual('age'); + expect(fields[3].identifier).toEqual('state'); const getters = reflectionClass.getters; - expect(getters.length).toBe(2); - expect(getters[0].name).toBe('fullName'); - expect(getters[1].name).toBe('age'); + expect(getters).toHaveLength(2); + expect(getters[0].identifier).toEqual('fullName'); + expect(getters[1].identifier).toEqual('age'); const setters = reflectionClass.setters; - expect(setters.length).toBe(1); - expect(setters[0].name).toBe('state'); + expect(setters).toHaveLength(1); + expect(setters[0].identifier).toEqual('state'); - const functions = reflectionClass.functions; - expect(functions.length).toBe(2); - expect(functions[0].name).toBe('constructor'); - expect(functions[1].name).toBe('toString'); + const methods = reflectionClass.methods; + expect(methods).toHaveLength(1); + expect(methods[0].identifier).toEqual('toString'); }); it('should get class reflection model with parent members from a class', () => { const reflectionClass = reflector.fromClass(CLASSES.Child, true); - expect(reflectionClass.name).toBe('Child'); - - const members = reflectionClass.members; - expect(members.length).toBe(11); - - const declarations = reflectionClass.declarations; - expect(declarations.length).toBe(5); - expect(declarations[0].name).toBe('id'); - expect(declarations[1].name).toBe('firstName'); - expect(declarations[2].name).toBe('lastName'); - expect(declarations[3].name).toBe('age'); - expect(declarations[4].name).toBe('state'); + expect(reflectionClass.identifier).toEqual('Child'); + expect(reflectionClass.members).toHaveLength(11); + expect(reflectionClass.construct).toBeDefined(); + + const fields = reflectionClass.fields; + expect(fields).toHaveLength(5); + expect(fields[0].identifier).toEqual('id'); + expect(fields[1].identifier).toEqual('firstName'); + expect(fields[2].identifier).toEqual('lastName'); + expect(fields[3].identifier).toEqual('age'); + expect(fields[4].identifier).toEqual('state'); const getters = reflectionClass.getters; - expect(getters.length).toBe(2); - expect(getters[0].name).toBe('fullName'); - expect(getters[1].name).toBe('age'); + expect(getters).toHaveLength(2); + expect(getters[0].identifier).toEqual('fullName'); + expect(getters[1].identifier).toEqual('age'); const setters = reflectionClass.setters; - expect(setters.length).toBe(1); - expect(setters[0].name).toBe('state'); - - const functions = reflectionClass.functions; - expect(functions.length).toBe(3); - expect(functions[0].name).toBe('constructor'); - expect(functions[1].name).toBe('speak'); - expect(functions[2].name).toBe('toString'); + expect(setters).toHaveLength(1); + expect(setters[0].identifier).toEqual('state'); + + const methods = reflectionClass.methods; + expect(methods).toHaveLength(2); + expect(methods[0].identifier).toEqual('speak'); + expect(methods[1].identifier).toEqual('toString'); }); it('should get class reflection model from a function based class', () => { const reflectionClass = reflector.fromClass(Error, true); - expect(reflectionClass.name).toBe('Error'); - - const members = reflectionClass.members; - expect(members.length).toBe(3); - - const declarations = reflectionClass.declarations; - expect(declarations.length).toBe(1); - expect(declarations[0].name).toBe('stack'); - - const functions = reflectionClass.functions; - expect(functions.length).toBe(2); - expect(functions[0].name).toBe('Error'); - expect(functions[1].name).toBe('toString'); + expect(reflectionClass.identifier).toEqual('Error'); + expect(reflectionClass.members).toHaveLength(3); + expect(reflectionClass.construct).toBeUndefined(); + + const fields = reflectionClass.fields; + expect(fields).toHaveLength(1); + expect(fields[0].identifier).toEqual('stack'); + + const methods = reflectionClass.methods; + expect(methods).toHaveLength(2); + expect(methods[0].identifier).toEqual('Error'); + expect(methods[1].identifier).toEqual('toString'); }); it('should get class reflection model from a class with function based parent class', () => { const reflectionClass = reflector.fromClass(CLASSES.CustomError, true); - expect(reflectionClass.name).toBe('CustomError'); - - const members = reflectionClass.members; - expect(members.length).toBe(6); + expect(reflectionClass.identifier).toEqual('CustomError'); + expect(reflectionClass.members).toHaveLength(6); + expect(reflectionClass.construct).toBeDefined(); - const declarations = reflectionClass.declarations; - expect(declarations.length).toBe(2); - expect(declarations[0].name).toBe('stack'); - expect(declarations[1].name).toBe('additional'); + const fields = reflectionClass.fields; + expect(fields).toHaveLength(2); + expect(fields[0].identifier).toEqual('stack'); + expect(fields[1].identifier).toEqual('additional'); const getters = reflectionClass.getters; - expect(getters.length).toBe(1); - expect(getters[0].name).toBe('additional'); - - const functions = reflectionClass.functions; - expect(functions.length).toBe(3); - expect(functions[0].name).toBe('Error'); - expect(functions[1].name).toBe('toString'); - expect(functions[2].name).toBe('constructor'); + expect(getters).toHaveLength(1); + expect(getters[0].identifier).toEqual('additional'); + + const methods = reflectionClass.methods; + expect(methods).toHaveLength(2); + expect(methods[0].identifier).toEqual('Error'); + expect(methods[1].identifier).toEqual('toString'); }); }); @@ -145,37 +137,29 @@ describe('Reflector', () => it('should get class reflection model without parent members from an object', () => { const reflectionClass = reflector.fromObject(OBJECTS.CLASS, false); - expect(reflectionClass.name).toBe('Child'); - - const members = reflectionClass.members; - expect(members.length).toBe(9); + expect(reflectionClass.identifier).toEqual('Child'); + expect(reflectionClass.members).toHaveLength(9); }); it('should get class reflection model with parent members from an object', () => { const reflectionClass = reflector.fromObject(OBJECTS.CLASS, true); - expect(reflectionClass.name).toBe('Child'); - - const members = reflectionClass.members; - expect(members.length).toBe(11); + expect(reflectionClass.identifier).toEqual('Child'); + expect(reflectionClass.members).toHaveLength(11); }); it('should get class reflection model from a function based class object', () => { const reflectionClass = reflector.fromObject(OBJECTS.ERROR, true); - expect(reflectionClass.name).toBe('Error'); - - const members = reflectionClass.members; - expect(members.length).toBe(3); + expect(reflectionClass.identifier).toEqual('Error'); + expect(reflectionClass.members).toHaveLength(3); }); it('should get class reflection model from a a class object with function based parent class', () => { const reflectionClass = reflector.fromObject(OBJECTS.CUSTOM_ERROR, true); - expect(reflectionClass.name).toBe('CustomError'); - - const members = reflectionClass.members; - expect(members.length).toBe(6); + expect(reflectionClass.identifier).toEqual('CustomError'); + expect(reflectionClass.members).toHaveLength(6); }); }); @@ -184,25 +168,20 @@ describe('Reflector', () => it('should get function reflection model', () => { const functionFunction = reflector.fromFunction(FUNCTIONS.OPTIONAL_ARGS); - expect(functionFunction.name).toBe('optionalFunction'); - expect(functionFunction.body).toBe('{ return a + b + c ; }'); + expect(functionFunction.identifier).toEqual('optionalFunction'); + expect(functionFunction.body.toString()).toEqual('{return a+b+c;}'); const parameters = functionFunction.parameters; - expect(parameters.length).toBe(3); + expect(parameters).toHaveLength(3); - const first = parameters[0] as ESField; - expect(first.name).toBe('a'); - expect(first.value).toBe(undefined); + expect(parameters[0].binding.toString()).toEqual('a'); + expect(parameters[0].initializer).toBeUndefined(); - const second = parameters[1] as ESField; - expect(second.name).toBe('b'); - expect(second.value).toBeInstanceOf(ESExpression); - expect(second.value?.definition).toBe('new Child ( 1 , "Jane" , "Doe" , 42 )'); + expect(parameters[1].binding.toString()).toEqual('b'); + expect(parameters[1].initializer?.toString(false)).toEqual('new Child(1,"Jane","Doe",42)'); - const third = parameters[2] as ESField; - expect(third.name).toBe('c'); - expect(third.value).toBeInstanceOf(ESExpression); - expect(third.value?.definition).toBe('0'); + expect(parameters[2].binding.toString()).toEqual('c'); + expect(parameters[2].initializer?.toString(false)).toEqual('0'); }); }); @@ -213,9 +192,9 @@ describe('Reflector', () => // eslint-disable-next-line @typescript-eslint/no-explicit-any const peter = reflector.createInstance(CLASSES.Child, [1, 'Peter', 'van Vliet', 24]) as any; expect(peter).toBeInstanceOf(CLASSES.Child); - expect(peter.id).toBe(1); - expect(peter.fullName).toBe('Peter van Vliet'); - expect(peter.age).toBe(24); + expect(peter.id).toEqual(1); + expect(peter.fullName).toEqual('Peter van Vliet'); + expect(peter.age).toEqual(24); }); }); @@ -224,13 +203,13 @@ describe('Reflector', () => it('should detect a class object', () => { const result = reflector.isClassObject(OBJECTS.CLASS); - expect(result).toBe(true); + expect(result).toBeTruthy(); }); it('should detect a non class object', () => { const result = reflector.isClassObject(OBJECTS.PLAIN); - expect(result).toBe(false); + expect(result).toBeFalsy(); }); }); @@ -239,8 +218,7 @@ describe('Reflector', () => it('should get the class of an object', () => { const result = reflector.getClass(OBJECTS.CLASS); - - expect(result.name).toBe('Child'); + expect(result.name).toEqual('Child'); }); }); @@ -249,8 +227,7 @@ describe('Reflector', () => it('should get the parent of a class', () => { const result = reflector.getParentClass(CLASSES.Child); - - expect(result.name).toBe('Parent'); + expect(result.name).toEqual('Parent'); }); }); }); diff --git a/packages/analysis/test/model/ESClass.spec.ts b/packages/analysis/test/model/ESClass.spec.ts new file mode 100644 index 00000000..8c94a078 --- /dev/null +++ b/packages/analysis/test/model/ESClass.spec.ts @@ -0,0 +1,285 @@ + +import { describe, expect, it } from 'vitest'; + +import { ESField, ESConstructor, ESMethod, ESGeneratorMethod, ESGetter, ESSetter } from '../../src/model'; + +import { esClass } from './fixtures'; + +describe('model/ESClass', () => +{ + describe('.fields', () => + { + it('should filter field members', () => + { + const declarations = esClass.fields; + expect(declarations).toHaveLength(4); + + expect(declarations[0]).toBeInstanceOf(ESField); + expect(declarations[0].identifier).toEqual('name'); + + expect(declarations[1]).toBeInstanceOf(ESField); + expect(declarations[1].identifier).toEqual('age'); + + expect(declarations[2]).toBeInstanceOf(ESField); + expect(declarations[2].identifier).toEqual('length'); + + expect(declarations[3]).toBeInstanceOf(ESField); + expect(declarations[3].identifier).toEqual('secret'); + }); + }); + + describe('.construct', () => + { + it('should filter the constructor member', () => + { + const construct = esClass.construct; + expect(construct).toBeInstanceOf(ESConstructor); + expect(construct?.parameters).toHaveLength(1); + }); + }); + + describe('.getters', () => + { + it('should filter getter members', () => + { + const getters = esClass.getters; + expect(getters).toHaveLength(2); + + expect(getters[0]).toBeInstanceOf(ESGetter); + expect(getters[0].identifier).toEqual('name'); + + expect(getters[1]).toBeInstanceOf(ESGetter); + expect(getters[1].identifier).toEqual('age'); + }); + }); + + describe('.setters', () => + { + it('should filter setter members', () => + { + const setters = esClass.setters; + expect(setters).toHaveLength(1); + + expect(setters[0]).toBeInstanceOf(ESSetter); + expect(setters[0].identifier).toEqual('age'); + }); + }); + + describe('.methods', () => + { + it('should filter method members', () => + { + const methods = esClass.methods; + expect(methods).toHaveLength(3); + + expect(methods[0]).toBeInstanceOf(ESMethod); + expect(methods[0].identifier).toEqual('secretStuff'); + + expect(methods[1]).toBeInstanceOf(ESMethod); + expect(methods[1].identifier).toEqual('toString'); + + expect(methods[2]).toBeInstanceOf(ESGeneratorMethod); + expect(methods[2].identifier).toEqual('createJohn'); + }); + }); + + describe('.readable', () => + { + it('should return all readable members', () => + { + const readable = esClass.readable; + expect(readable).toHaveLength(3); + + expect(readable[0].identifier).toEqual('length'); + expect(readable[0]).toBeInstanceOf(ESField); + + expect(readable[1].identifier).toEqual('name'); + expect(readable[1]).toBeInstanceOf(ESGetter); + + expect(readable[2].identifier).toEqual('age'); + expect(readable[2]).toBeInstanceOf(ESGetter); + }); + }); + + describe('.writable', () => + { + it('should return all writable members', () => + { + const writable = esClass.writable; + expect(writable).toHaveLength(2); + + expect(writable[0].identifier).toEqual('length'); + expect(writable[0]).toBeInstanceOf(ESField); + + expect(writable[1].identifier).toEqual('age'); + expect(writable[1]).toBeInstanceOf(ESSetter); + }); + }); + + describe('.getMember(identifier)', () => + { + it('should get a member by its identifier', () => + { + const member = esClass.getMember('toString'); + expect(member).toBeInstanceOf(ESMethod); + expect(member?.identifier).toEqual('toString'); + }); + }); + + describe('.getField(identifier)', () => + { + it('should get a field by its identifier', () => + { + const member = esClass.getField('name'); + expect(member).toBeInstanceOf(ESField); + expect(member?.identifier).toEqual('name'); + }); + }); + + describe('.getGetter(identifier)', () => + { + it('should get a getter by its identifier', () => + { + const member = esClass.getGetter('name'); + expect(member).toBeInstanceOf(ESGetter); + expect(member?.identifier).toEqual('name'); + }); + }); + + describe('.getSetter(identifier)', () => + { + it('should get a setter by its identifier', () => + { + const member = esClass.getSetter('age'); + expect(member).toBeInstanceOf(ESSetter); + expect(member?.identifier).toEqual('age'); + }); + }); + + describe('.getMethod(identifier)', () => + { + it('should get a method by its identifier', () => + { + const member = esClass.getMethod('secretStuff'); + expect(member).toBeInstanceOf(ESMethod); + expect(member?.identifier).toEqual('secretStuff'); + }); + }); + + describe('.hasMember(identifier)', () => + { + it('should have an existing member', () => + { + const result = esClass.hasMember('age'); + expect(result).toBeTruthy(); + }); + + it('should not have a non-existing member', () => + { + const result = esClass.hasMember('nonExisting'); + expect(result).toBeFalsy(); + }); + }); + + describe('.hasField(identifier)', () => + { + it('should have an existing field', () => + { + const result = esClass.hasField('name'); + expect(result).toBeTruthy(); + }); + + it('should not have a non-existing field', () => + { + const result = esClass.hasField('createJohn'); + expect(result).toBeFalsy(); + }); + }); + + describe('.hasGetter(identifier)', () => + { + it('should have an existing getter', () => + { + const result = esClass.hasGetter('name'); + expect(result).toBeTruthy(); + }); + + it('should not have a non-existing getter', () => + { + const result = esClass.hasGetter('createJohn'); + expect(result).toBeFalsy(); + }); + }); + + describe('.hasSetter(identifier)', () => + { + it('should have an existing setter', () => + { + const result = esClass.hasSetter('age'); + expect(result).toBeTruthy(); + }); + + it('should not have a non-existing setter', () => + { + const result = esClass.hasSetter('name'); + expect(result).toBeFalsy(); + }); + }); + + describe('.hasMethod(identifier)', () => + { + it('should have an existing function', () => + { + const result = esClass.hasMethod('secretStuff'); + expect(result).toBeTruthy(); + }); + + it('should not have a non-existing function', () => + { + const result = esClass.hasMethod('name'); + expect(result).toBeFalsy(); + }); + }); + + describe('.canRead(identifier)', () => + { + it('should read a public field', () => + { + const canRead = esClass.canRead('length'); + expect(canRead).toBeTruthy(); + }); + + it('should read a private field with getter', () => + { + const canRead = esClass.canRead('name'); + expect(canRead).toBeTruthy(); + }); + + it('should not read a private field without a getter', () => + { + const canRead = esClass.canRead('secret'); + expect(canRead).toBeFalsy(); + }); + }); + + describe('.canWrite(identifier)', () => + { + it('should write a public field', () => + { + const canWrite = esClass.canWrite('length'); + expect(canWrite).toBeTruthy(); + }); + + it('should write a private field with setter', () => + { + const canWrite = esClass.canWrite('age'); + expect(canWrite).toBeTruthy(); + }); + + it('should not write a private field without a setter', () => + { + const canWrite = esClass.canWrite('secret'); + expect(canWrite).toBeFalsy(); + }); + }); +}); diff --git a/packages/analysis/test/model/ESModule.spec.ts b/packages/analysis/test/model/ESModule.spec.ts new file mode 100644 index 00000000..618e9570 --- /dev/null +++ b/packages/analysis/test/model/ESModule.spec.ts @@ -0,0 +1,227 @@ + +import { describe, expect, it } from 'vitest'; + +import { ESImport, ESExport, ESClass, ESFunction, ESGeneratorFunction, ESVariable } from '../../src/model'; + +import { esModule } from './fixtures'; + +describe('model/ESModule', () => +{ + describe('.imports', () => + { + it('should filter import members', () => + { + const imports = esModule.imports; + expect(imports).toHaveLength(1); + + expect(imports[0]).toBeInstanceOf(ESImport); + expect(imports[0].members.length).toBe(1); + }); + }); + + describe('.exports', () => + { + it('should filter export members', () => + { + const exports = esModule.exports; + expect(exports).toHaveLength(1); + + expect(exports[0]).toBeInstanceOf(ESExport); + expect(exports[0].members.length).toBe(4); + }); + }); + + describe('.variables', () => + { + it('should filter variable members', () => + { + const declarations = esModule.variables; + expect(declarations).toHaveLength(2); + + expect(declarations[0]).toBeInstanceOf(ESVariable); + expect(declarations[0].identifier).toEqual('peter'); + + expect(declarations[1]).toBeInstanceOf(ESVariable); + expect(declarations[1].identifier).toEqual('bas'); + }); + }); + + describe('.functions', () => + { + it('should filter function members', () => + { + const functions = esModule.functions; + expect(functions).toHaveLength(3); + + expect(functions[0]).toBeInstanceOf(ESFunction); + expect(functions[0].identifier).toEqual('createJohn'); + + expect(functions[1]).toBeInstanceOf(ESFunction); + expect(functions[1].identifier).toEqual('sum'); + + expect(functions[2]).toBeInstanceOf(ESGeneratorFunction); + expect(functions[2].identifier).toEqual('generateNumbers'); + }); + }); + + describe('.classes', () => + { + it('should filter class members', () => + { + const classes = esModule.classes; + expect(classes).toHaveLength(2); + + expect(classes[0]).toBeInstanceOf(ESClass); + expect(classes[0].identifier).toEqual('Customer'); + + expect(classes[1]).toBeInstanceOf(ESClass); + expect(classes[1].identifier).toEqual('Order'); + }); + }); + + describe('.getDeclaration(identifier)', () => + { + it('should get a declaration by its identifier', () => + { + const member = esModule.getDeclaration('sum'); + expect(member).toBeInstanceOf(ESFunction); + expect(member?.identifier).toEqual('sum'); + }); + }); + + describe('.getVariable(identifier)', () => + { + it('should get a variable by its identifier', () => + { + const member = esModule.getVariable('bas'); + expect(member).toBeInstanceOf(ESVariable); + expect(member?.identifier).toEqual('bas'); + }); + }); + + describe('.getFunction(identifier)', () => + { + it('should get a function by its identifier', () => + { + const member = esModule.getFunction('createJohn'); + expect(member).toBeInstanceOf(ESFunction); + expect(member?.identifier).toEqual('createJohn'); + }); + + it('should get a generator function by its identifier', () => + { + const member = esModule.getFunction('generateNumbers'); + expect(member).toBeInstanceOf(ESGeneratorFunction); + expect(member?.identifier).toEqual('generateNumbers'); + }); + }); + + describe('.getClass(identifier)', () => + { + it('should get a class by its identifier', () => + { + const member = esModule.getClass('Customer'); + expect(member).toBeInstanceOf(ESClass); + expect(member?.identifier).toEqual('Customer'); + }); + }); + + describe('.hasDeclaration(identifier)', () => + { + it('should have an existing member', () => + { + const result = esModule.hasDeclaration('sum'); + expect(result).toBeTruthy(); + }); + + it('should not have a non-existing member', () => + { + const result = esModule.hasDeclaration('nonExisting'); + expect(result).toBeFalsy(); + }); + }); + + describe('.hasVariable(identifier)', () => + { + it('should have an existing variable', () => + { + const result = esModule.hasVariable('peter'); + expect(result).toBeTruthy(); + }); + + it('should not have a non-existing variable', () => + { + const result = esModule.hasVariable('Customer'); + expect(result).toBeFalsy(); + }); + }); + + describe('.hasFunction(identifier)', () => + { + it('should have an existing function', () => + { + const result = esModule.hasFunction('createJohn'); + expect(result).toBeTruthy(); + }); + + it('should not have a non-existing function', () => + { + const result = esModule.hasFunction('bas'); + expect(result).toBeFalsy(); + }); + }); + + describe('.hasClass(identifier)', () => + { + it('should have an existing class', () => + { + const result = esModule.hasClass('Customer'); + expect(result).toBe(true); + }); + + it('should not have a non-existing class', () => + { + const result = esModule.hasClass('peter'); + expect(result).toBe(false); + }); + }); + + describe('.exportedVariables', () => + { + it('should filter exported variables', () => + { + const declarations = esModule.exportedVariables; + expect(declarations).toHaveLength(1); + + expect(declarations[0]).toBeInstanceOf(ESVariable); + expect(declarations[0].identifier).toEqual('peter'); + }); + }); + + describe('.exportedFunctions', () => + { + it('should filter exported functions', () => + { + const functions = esModule.exportedFunctions; + expect(functions).toHaveLength(2); + + expect(functions[0]).toBeInstanceOf(ESFunction); + expect(functions[0].identifier).toEqual('sum'); + + expect(functions[1]).toBeInstanceOf(ESGeneratorFunction); + expect(functions[1].identifier).toEqual('generateNumbers'); + }); + }); + + describe('.exportedClasses', () => + { + it('should filter exported classes', () => + { + const classes = esModule.exportedClasses; + expect(classes).toHaveLength(1); + + expect(classes[0]).toBeInstanceOf(ESClass); + expect(classes[0].identifier).toEqual('Customer'); + }); + }); +}); diff --git a/packages/analysis/test/model/fixtures/esClass.fixture.ts b/packages/analysis/test/model/fixtures/esClass.fixture.ts new file mode 100644 index 00000000..5aae0ef5 --- /dev/null +++ b/packages/analysis/test/model/fixtures/esClass.fixture.ts @@ -0,0 +1,19 @@ + +import { ESClass, ESConstructor, ESBlock, ESParameter, ESIdentifierBinding, ESExpression, ESField, ESMethod, ESGeneratorMethod, ESGetter, ESSetter } from '../../../src/model'; + +const members = +[ + new ESField('name', 'private', 'instance', new ESExpression('"Peter"')), + new ESField('age', 'private', 'instance', undefined), + new ESField('length', 'public', 'instance', undefined), + new ESField('secret', 'private', 'instance', undefined), + new ESConstructor([new ESParameter(new ESIdentifierBinding('age'), undefined)], new ESBlock('{ this.#age = age; }')), + new ESGetter('name', 'public', 'instance', new ESBlock('{ return this.#name; }')), + new ESGetter('age', 'public', 'instance', new ESBlock('{ return this.#age; }')), + new ESSetter('age', 'public', 'instance', new ESParameter(new ESIdentifierBinding('age'), undefined), new ESBlock('{ this.#age = age; }')), + new ESMethod('secretStuff', 'private', 'instance', [], new ESBlock('{ }')), + new ESMethod('toString', 'public', 'instance', [], new ESBlock('{ return `${this.#name} (${this.#age})` }')), + new ESGeneratorMethod('createJohn', 'public', 'instance', [], new ESBlock('{ }')) +]; + +export const esClass = new ESClass('Person', undefined, members); diff --git a/packages/analysis/test/model/fixtures/esModule.fixture.ts b/packages/analysis/test/model/fixtures/esModule.fixture.ts new file mode 100644 index 00000000..af95fe29 --- /dev/null +++ b/packages/analysis/test/model/fixtures/esModule.fixture.ts @@ -0,0 +1,22 @@ + +import { ESImport, ESModuleMember, ESVariable, ESIdentifierBinding, ESExpression, ESFunction, ESGeneratorFunction, ESParameter, ESBlock, ESClass, ESExport, ESModule } from '../../../src/model'; + +const members = +[ + new ESImport([new ESModuleMember('default', 'Person')], './Person.js'), + new ESVariable('const', new ESIdentifierBinding('peter'), new ESExpression('new Person("Peter")')), + new ESVariable('const', new ESIdentifierBinding('bas'), new ESExpression('new Person("Bas")')), + new ESFunction('createJohn', [], new ESBlock('{ return new Person("John"); }')), + new ESFunction('sum', [new ESParameter(new ESIdentifierBinding('a'), undefined), new ESParameter(new ESIdentifierBinding('b'), undefined)], new ESBlock('{ return a + b; }')), + new ESGeneratorFunction('generateNumbers', [new ESParameter(new ESIdentifierBinding('count'), undefined)], new ESBlock('{ for (let i = 0; i < count; i++) yield i; }')), + new ESClass('Customer', 'Person', []), + new ESClass('Order', undefined, []), + new ESExport([ + new ESModuleMember('sum', 'default'), + new ESModuleMember('peter'), + new ESModuleMember('Customer'), + new ESModuleMember('generateNumbers') + ], undefined) +]; + +export const esModule = new ESModule(members); diff --git a/packages/analysis/test/models/fixtures/index.ts b/packages/analysis/test/model/fixtures/index.ts similarity index 67% rename from packages/analysis/test/models/fixtures/index.ts rename to packages/analysis/test/model/fixtures/index.ts index c87b52d8..2b149c31 100644 --- a/packages/analysis/test/models/fixtures/index.ts +++ b/packages/analysis/test/model/fixtures/index.ts @@ -1,4 +1,3 @@ export * from './esClass.fixture'; export * from './esModule.fixture'; -export * from './esScope.fixture'; diff --git a/packages/analysis/test/models/ESClass.spec.ts b/packages/analysis/test/models/ESClass.spec.ts deleted file mode 100644 index 7fd0244b..00000000 --- a/packages/analysis/test/models/ESClass.spec.ts +++ /dev/null @@ -1,124 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import { ESDeclaration, ESFunction, ESGetter, ESSetter } from '../../src/models'; - -import { esClass } from './fixtures'; - -describe('models/ESClass', () => -{ - // Scope tests are omitted - - describe('.readable', () => - { - it('should return all readable members', () => - { - const readable = esClass.readable; - expect(readable.length).toBe(3); - - expect(readable[0].name).toBe('name'); - expect(readable[0]).toBeInstanceOf(ESGetter); - - expect(readable[1].name).toBe('age'); - expect(readable[1]).toBeInstanceOf(ESGetter); - - expect(readable[2].name).toBe('length'); - expect(readable[2]).toBeInstanceOf(ESDeclaration); - }); - }); - - describe('.writable', () => - { - it('should return all writable members', () => - { - const writable = esClass.writable; - expect(writable.length).toBe(2); - - expect(writable[0].name).toBe('age'); - expect(writable[0]).toBeInstanceOf(ESSetter); - - expect(writable[1].name).toBe('length'); - expect(writable[1]).toBeInstanceOf(ESDeclaration); - }); - }); - - describe('.callable', () => - { - it('should return all callable members', () => - { - const callable = esClass.callable; - expect(callable.length).toBe(2); - - expect(callable[0].name).toBe('constructor'); - expect(callable[0]).toBeInstanceOf(ESFunction); - - expect(callable[1].name).toBe('toString'); - expect(callable[1]).toBeInstanceOf(ESFunction); - }); - }); - - describe('.canRead(name)', () => - { - it('should read a public field', () => - { - const canRead = esClass.canRead('length'); - - expect(canRead).toBe(true); - }); - - it('should read a private field with getter', () => - { - const canRead = esClass.canRead('name'); - - expect(canRead).toBe(true); - }); - - it('should not read a private field without a getter', () => - { - const canRead = esClass.canRead('secret'); - - expect(canRead).toBe(false); - }); - }); - - describe('.canWrite(name)', () => - { - it('should write a public field', () => - { - const canWrite = esClass.canWrite('length'); - - expect(canWrite).toBe(true); - }); - - it('should write a private field with setter', () => - { - const canWrite = esClass.canWrite('age'); - - expect(canWrite).toBe(true); - }); - - it('should not wite a private field without a setter', () => - { - const canWrite = esClass.canWrite('secret'); - - expect(canWrite).toBe(false); - }); - }); - - describe('.canCall(name)', () => - { - it('should call a public function', () => - { - const canCall = esClass.canCall('toString'); - - expect(canCall).toBe(true); - }); - - it('should not call a private function', () => - { - const canCall = esClass.canCall('secretStuff'); - - expect(canCall).toBe(false); - }); - }); -}); diff --git a/packages/analysis/test/models/ESModule.spec.ts b/packages/analysis/test/models/ESModule.spec.ts deleted file mode 100644 index 7d82ba18..00000000 --- a/packages/analysis/test/models/ESModule.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import { ESClass, ESDeclaration, ESFunction, ESGenerator, ESMember } from '../../src/models'; - -import { esModule } from './fixtures'; - -describe('models/ESModule', () => -{ - // Scope tests are omitted - - describe('.exportedDeclarations', () => - { - it('should filter exported declarations', () => - { - const declarations = esModule.exportedDeclarations; - expect(declarations.length).toBe(1); - - expect(declarations[0]).toBeInstanceOf(ESDeclaration); - expect(declarations[0].name).toBe('peter'); - }); - }); - - describe('.exportedFunctions', () => - { - it('should filter exported functions', () => - { - const functions = esModule.exportedFunctions; - expect(functions.length).toBe(1); - - expect(functions[0]).toBeInstanceOf(ESFunction); - expect(functions[0].name).toBe('sum'); - }); - }); - - describe('.exportedGenerators', () => - { - it('should filter exported generators', () => - { - const generators = esModule.exportedGenerators; - expect(generators.length).toBe(1); - - expect(generators[0]).toBeInstanceOf(ESGenerator); - expect(generators[0].name).toBe('generateNumbers'); - }); - }); - - describe('.exportedClasses', () => - { - it('should filter exported classes', () => - { - const classes = esModule.exportedClasses; - expect(classes.length).toBe(1); - - expect(classes[0]).toBeInstanceOf(ESClass); - expect(classes[0].name).toBe('Customer'); - }); - }); - - describe('.exported', () => - { - it('should return a map with exported members only', () => - { - const exported = esModule.exported; - expect(exported.size).toBe(4); - - const first = exported.get('default') as ESFunction; - expect(first).toBeInstanceOf(ESFunction); - expect(first.name).toBe('sum'); - - const second = exported.get('peter') as ESDeclaration; - expect(second).toBeInstanceOf(ESDeclaration); - expect(second.name).toBe('peter'); - - const third = exported.get('Customer') as ESClass; - expect(third).toBeInstanceOf(ESClass); - expect(third.name).toBe('Customer'); - - const fourth = exported.get('generateNumbers') as ESGenerator; - expect(fourth).toBeInstanceOf(ESGenerator); - expect(fourth.name).toBe('generateNumbers'); - }); - }); - - describe('.isExported(member)', () => - { - it('should indicate that a member is exported', () => - { - const member = esModule.getMember('sum') as ESMember; - const result = esModule.isExported(member); - expect(result).toBe(true); - }); - - it('should indicate that a member is not exported', () => - { - const member = esModule.getMember('createJohn') as ESMember; - const result = esModule.isExported(member); - expect(result).toBe(false); - }); - }); - - describe('.getExported(name)', () => - { - it('should get an exported member', () => - { - const member = esModule.getExported('default') as ESFunction; - expect(member).toBeInstanceOf(ESFunction); - expect(member.name).toBe('sum'); - }); - - it('should not get a non-exported member', () => - { - const member = esModule.getExported('createJohn'); - expect(member).toBe(undefined); - }); - }); -}); diff --git a/packages/analysis/test/models/ESScope.spec.ts b/packages/analysis/test/models/ESScope.spec.ts deleted file mode 100644 index 72b4a745..00000000 --- a/packages/analysis/test/models/ESScope.spec.ts +++ /dev/null @@ -1,289 +0,0 @@ - -import { describe, expect, it } from 'vitest'; - -import { ESImport, ESDeclaration, ESFunction, ESGenerator, ESClass, ESExport, ESGetter, ESSetter } from '../../src/models'; - -import { esScope } from './fixtures'; - -describe('models/ESScope', () => -{ - describe('.imports', () => - { - it('should filter import members', () => - { - const imports = esScope.imports; - expect(imports.length).toBe(1); - - expect(imports[0]).toBeInstanceOf(ESImport); - expect(imports[0].members.length).toBe(1); - }); - }); - - describe('.exports', () => - { - it('should filter export members', () => - { - const exports = esScope.exports; - expect(exports.length).toBe(1); - - expect(exports[0]).toBeInstanceOf(ESExport); - expect(exports[0].members.length).toBe(2); - }); - }); - - describe('.fields', () => - { - it('should filter field members', () => - { - const declarations = esScope.declarations; - expect(declarations.length).toBe(2); - - expect(declarations[0]).toBeInstanceOf(ESDeclaration); - expect(declarations[0].name).toBe('name'); - - expect(declarations[1]).toBeInstanceOf(ESDeclaration); - expect(declarations[1].name).toBe('age'); - }); - }); - - describe('.functions', () => - { - it('should filter function members', () => - { - const functions = esScope.functions; - expect(functions.length).toBe(2); - - expect(functions[0]).toBeInstanceOf(ESFunction); - expect(functions[0].name).toBe('createJohn'); - - expect(functions[1]).toBeInstanceOf(ESFunction); - expect(functions[1].name).toBe('sum'); - }); - }); - - describe('.getters', () => - { - it('should filter getter members', () => - { - const getters = esScope.getters; - expect(getters.length).toBe(2); - - expect(getters[0]).toBeInstanceOf(ESGetter); - expect(getters[0].name).toBe('name'); - - expect(getters[1]).toBeInstanceOf(ESGetter); - expect(getters[1].name).toBe('age'); - }); - }); - - describe('.setters', () => - { - it('should filter setter members', () => - { - const setters = esScope.setters; - expect(setters.length).toBe(1); - - expect(setters[0]).toBeInstanceOf(ESSetter); - expect(setters[0].name).toBe('age'); - }); - }); - - describe('.generators', () => - { - it('should filter generator members', () => - { - const generators = esScope.generators; - expect(generators.length).toBe(1); - - expect(generators[0]).toBeInstanceOf(ESGenerator); - expect(generators[0].name).toBe('createJohn'); - }); - }); - - describe('.classes', () => - { - it('should filter class members', () => - { - const classes = esScope.classes; - expect(classes.length).toBe(1); - - expect(classes[0]).toBeInstanceOf(ESClass); - expect(classes[0].name).toBe('Customer'); - }); - }); - - describe('.getMember(name)', () => - { - it('should get a member by its name', () => - { - const member = esScope.getMember('sum'); - expect(member).toBeInstanceOf(ESFunction); - expect(member?.name).toBe('sum'); - }); - }); - - describe('.getDeclaration(name)', () => - { - it('should get a field by its name', () => - { - const member = esScope.getDeclaration('name'); - expect(member).toBeInstanceOf(ESDeclaration); - expect(member?.name).toBe('name'); - }); - }); - - describe('.getFunction(name)', () => - { - it('should get a function by its name', () => - { - const member = esScope.getFunction('createJohn'); - expect(member).toBeInstanceOf(ESFunction); - expect(member?.name).toBe('createJohn'); - }); - }); - - describe('.getGetter(name)', () => - { - it('should get a getter by its name', () => - { - const member = esScope.getGetter('name'); - expect(member).toBeInstanceOf(ESGetter); - expect(member?.name).toBe('name'); - }); - }); - - describe('.getSetter(name)', () => - { - it('should get a setter by its name', () => - { - const member = esScope.getSetter('age'); - expect(member).toBeInstanceOf(ESSetter); - expect(member?.name).toBe('age'); - }); - }); - - describe('.getGenerator(name)', () => - { - it('should get a generator by its name', () => - { - const member = esScope.getGenerator('createJohn'); - expect(member).toBeInstanceOf(ESGenerator); - expect(member?.name).toBe('createJohn'); - }); - }); - - describe('.getClass(name)', () => - { - it('should get a class by its name', () => - { - const member = esScope.getClass('Customer'); - expect(member).toBeInstanceOf(ESClass); - expect(member?.name).toBe('Customer'); - }); - }); - - describe('.hasMember(name)', () => - { - it('should have an existing member', () => - { - const result = esScope.hasMember('sum'); - expect(result).toBe(true); - }); - - it('should not have a non-existing member', () => - { - const result = esScope.hasMember('nonExisting'); - expect(result).toBe(false); - }); - }); - - describe('.hasDeclaration(name)', () => - { - it('should have an existing field', () => - { - const result = esScope.hasDeclaration('age'); - expect(result).toBe(true); - }); - - it('should not have a non-existing field', () => - { - const result = esScope.hasDeclaration('Customer'); - expect(result).toBe(false); - }); - }); - - describe('.hasFunction(name)', () => - { - it('should have an existing function', () => - { - const result = esScope.hasFunction('createJohn'); - expect(result).toBe(true); - }); - - it('should not have a non-existing function', () => - { - const result = esScope.hasFunction('name'); - expect(result).toBe(false); - }); - }); - - describe('.hasGetter(name)', () => - { - it('should have an existing getter', () => - { - const result = esScope.hasGetter('name'); - expect(result).toBe(true); - }); - - it('should not have a non-existing getter', () => - { - const result = esScope.hasGetter('createJohn'); - expect(result).toBe(false); - }); - }); - - describe('.hasSetter(name)', () => - { - it('should have an existing setter', () => - { - const result = esScope.hasSetter('age'); - expect(result).toBe(true); - }); - - it('should not have a non-existing setter', () => - { - const result = esScope.hasSetter('name'); - expect(result).toBe(false); - }); - }); - - describe('.hasGenerator(name)', () => - { - it('should have an existing generator', () => - { - const result = esScope.hasGenerator('createJohn'); - expect(result).toBe(true); - }); - - it('should not have a non-existing generator', () => - { - const result = esScope.hasGenerator('sum'); - expect(result).toBe(false); - }); - }); - - describe('.hasClass(name)', () => - { - it('should have an existing class', () => - { - const result = esScope.hasClass('Customer'); - expect(result).toBe(true); - }); - - it('should not have a non-existing class', () => - { - const result = esScope.hasClass('name'); - expect(result).toBe(false); - }); - }); -}); diff --git a/packages/analysis/test/models/fixtures/esClass.fixture.ts b/packages/analysis/test/models/fixtures/esClass.fixture.ts deleted file mode 100644 index cc3e7fdc..00000000 --- a/packages/analysis/test/models/fixtures/esClass.fixture.ts +++ /dev/null @@ -1,20 +0,0 @@ - -import { ESClass, ESDeclaration, ESExpression, ESField, ESFunction, ESGetter, ESScope, ESSetter } from '../../../src/models'; - -const members = -[ - new ESDeclaration('name', new ESExpression('"Peter"'), false, true), - new ESDeclaration('age', undefined, false, true), - new ESDeclaration('length', undefined, false, false), - new ESDeclaration('secret', undefined, false, true), - new ESFunction('constructor', [new ESField('age', undefined)], 'this.#age = age;'), - new ESGetter('name', [], 'return this.#name;'), - new ESGetter('age', [], 'return this.#age;'), - new ESSetter('age', [new ESField('age', undefined)], 'this.#age = age;'), - new ESFunction('secretStuff', [], '', false, false, true), - new ESFunction('toString', [], 'return `${this.#name} (${this.#age})`'), -]; - -const scope = new ESScope(members); - -export const esClass = new ESClass('Person', undefined, scope); diff --git a/packages/analysis/test/models/fixtures/esModule.fixture.ts b/packages/analysis/test/models/fixtures/esModule.fixture.ts deleted file mode 100644 index e0376cfa..00000000 --- a/packages/analysis/test/models/fixtures/esModule.fixture.ts +++ /dev/null @@ -1,25 +0,0 @@ - -import { ESImport, ESAlias, ESDeclaration, ESExpression, ESFunction, ESField, ESGenerator, ESClass, ESScope, ESExport, ESModule } from '../../../src/models'; - -const members = -[ - new ESImport([new ESAlias('default', 'Person')], './Person.js'), - new ESDeclaration('peter', new ESExpression('new Person("Peter")')), - new ESDeclaration('bas', new ESExpression('new Person("Bas")')), - new ESFunction('createJohn', [], 'return new Person("John")'), - new ESFunction('sum', [new ESField('a', undefined), new ESField('b', undefined)], 'return a + b'), - new ESGenerator('generateNumbers', [new ESField('count', undefined)], 'for (let i = 0; i < count; i++) yield i;'), - new ESGenerator('createJohn', [], 'yield new Person("John")'), - new ESClass('Customer', 'Person', new ESScope([])), - new ESClass('Order', undefined, new ESScope([])), - new ESExport([ - new ESAlias('sum', 'default'), - new ESAlias('peter', 'peter'), - new ESAlias('Customer', 'Customer'), - new ESAlias('generateNumbers', 'generateNumbers') - ], undefined) -]; - -const scope = new ESScope(members); - -export const esModule = new ESModule(scope); diff --git a/packages/analysis/test/models/fixtures/esScope.fixture.ts b/packages/analysis/test/models/fixtures/esScope.fixture.ts deleted file mode 100644 index ecb9bdec..00000000 --- a/packages/analysis/test/models/fixtures/esScope.fixture.ts +++ /dev/null @@ -1,19 +0,0 @@ - -import { ESImport, ESAlias, ESDeclaration, ESExpression, ESFunction, ESField, ESGenerator, ESClass, ESScope, ESExport, ESGetter, ESSetter } from '../../../src/models'; - -const members = -[ - new ESImport([new ESAlias('default', 'Person')], './Person.js'), - new ESDeclaration('name', new ESExpression('"john"'), false, true), - new ESDeclaration('age', new ESExpression('42'), false, true), - new ESFunction('createJohn', [], '{ return new Person("John") }'), - new ESFunction('sum', [new ESField('a', undefined), new ESField('b', undefined)], 'return a + b'), - new ESGetter('name', [], 'return this.#name;'), - new ESGetter('age', [], 'return this.#age;'), - new ESSetter('age', [new ESField('age', undefined)], 'this.#age = age;'), - new ESClass('Customer', 'Person', new ESScope([])), - new ESGenerator('createJohn', [], 'yield new Person("John")'), - new ESExport([new ESAlias('Customer', 'Customer'), new ESAlias('sum', 'default')], undefined) -]; - -export const esScope = new ESScope(members); diff --git a/packages/analysis/test/static/Lexer.spec.ts b/packages/analysis/test/static/Lexer.spec.ts index 35e0df71..96654dec 100644 --- a/packages/analysis/test/static/Lexer.spec.ts +++ b/packages/analysis/test/static/Lexer.spec.ts @@ -7,251 +7,305 @@ import { CODE } from './fixtures'; const lexer = new Lexer(); -describe('parser/Lexer', () => +describe('Lexer', () => { - describe('.tokenize(code, ignoreComments)', () => + describe('Tokenize', () => { + it('should distinguish value types', () => + { + const tokens = lexer.tokenize(CODE.VALUES); + expect(tokens.size).toEqual(7); + + expect(tokens.get(0).type).toEqual(TokenType.NUMBER); + expect(tokens.get(0).value).toEqual('42'); + + expect(tokens.get(1).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(1).value).toEqual('hello'); + + expect(tokens.get(2).type).toEqual(TokenType.LITERAL); + expect(tokens.get(2).value).toEqual('"world"'); + + expect(tokens.get(3).type).toEqual(TokenType.BOOLEAN); + expect(tokens.get(3).value).toEqual('true'); + + expect(tokens.get(4).type).toEqual(TokenType.BOOLEAN); + expect(tokens.get(4).value).toEqual('false'); + + expect(tokens.get(5).type).toEqual(TokenType.NOTHING); + expect(tokens.get(5).value).toEqual('undefined'); + + expect(tokens.get(6).type).toEqual(TokenType.NOTHING); + expect(tokens.get(6).value).toEqual('null'); + }); + it('should separate operators', () => { const tokens = lexer.tokenize(CODE.OPERATORS); - expect(tokens.size).toBe(6); + expect(tokens.size).toEqual(6); - expect(tokens.get(0).type).toBe(TokenType.OPERATOR); - expect(tokens.get(0).value).toBe(Operator.EQUAL_STRICT); + expect(tokens.get(0).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(0).value).toEqual(Operator.EQUAL_STRICT); - expect(tokens.get(1).type).toBe(TokenType.OPERATOR); - expect(tokens.get(1).value).toBe(Operator.EQUAL); + expect(tokens.get(1).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(1).value).toEqual(Operator.EQUAL); - expect(tokens.get(2).type).toBe(TokenType.OPERATOR); - expect(tokens.get(2).value).toBe(Operator.NOT_EQUAL); + expect(tokens.get(2).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(2).value).toEqual(Operator.NOT_EQUAL); - expect(tokens.get(3).type).toBe(TokenType.OPERATOR); - expect(tokens.get(3).value).toBe(Operator.ASSIGN_ADD); + expect(tokens.get(3).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(3).value).toEqual(Operator.ASSIGN_ADD); - expect(tokens.get(4).type).toBe(TokenType.OPERATOR); - expect(tokens.get(4).value).toBe(Operator.MULTIPLY); + expect(tokens.get(4).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(4).value).toEqual(Operator.MULTIPLY); - expect(tokens.get(5).type).toBe(TokenType.OPERATOR); - expect(tokens.get(5).value).toBe(Operator.NOT_EQUAL); + expect(tokens.get(5).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(5).value).toEqual(Operator.NOT_EQUAL); + }); + + it('should separate numbers from operations', () => + { + const tokens = lexer.tokenize(CODE.NUMBERS); + expect(tokens.size).toEqual(6); + + expect(tokens.get(0).type).toEqual(TokenType.NUMBER); + expect(tokens.get(0).value).toEqual('-12'); + + expect(tokens.get(1).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(1).value).toEqual(Operator.SUBTRACT); + + expect(tokens.get(2).type).toEqual(TokenType.NUMBER); + expect(tokens.get(2).value).toEqual('10'); + + expect(tokens.get(3).type).toEqual(TokenType.NUMBER); + expect(tokens.get(3).value).toEqual('12_345_678.90'); + + expect(tokens.get(4).type).toEqual(TokenType.NUMBER); + expect(tokens.get(4).value).toEqual('0x124_a4Bc'); + + expect(tokens.get(5).type).toEqual(TokenType.NUMBER); + expect(tokens.get(5).value).toEqual('0b11_0110'); }); it('should separate keywords from literals', () => { const tokens = lexer.tokenize(CODE.LITERALS); - expect(tokens.size).toBe(3); + expect(tokens.size).toEqual(3); - expect(tokens.get(0).type).toBe(TokenType.LITERAL); - expect(tokens.get(0).value).toBe('`foo\\`ter`'); + expect(tokens.get(0).type).toEqual(TokenType.LITERAL); + expect(tokens.get(0).value).toEqual('`foo\\`ter`'); - expect(tokens.get(1).type).toBe(TokenType.LITERAL); - expect(tokens.get(1).value).toBe('"bar\\"becue"'); + expect(tokens.get(1).type).toEqual(TokenType.LITERAL); + expect(tokens.get(1).value).toEqual('"bar\\"becue"'); - expect(tokens.get(2).type).toBe(TokenType.LITERAL); - expect(tokens.get(2).value).toBe("'baz'"); + expect(tokens.get(2).type).toEqual(TokenType.LITERAL); + expect(tokens.get(2).value).toEqual("'baz'"); }); it('should separate keywords from identifiers', () => { const tokens = lexer.tokenize(CODE.KEYWORDS_IDENTIFIERS); - expect(tokens.size).toBe(4); + expect(tokens.size).toEqual(4); - expect(tokens.get(0).type).toBe(TokenType.KEYWORD); - expect(tokens.get(0).value).toBe('class'); + expect(tokens.get(0).type).toEqual(TokenType.KEYWORD); + expect(tokens.get(0).value).toEqual('class'); - expect(tokens.get(1).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(1).value).toBe('Foo'); + expect(tokens.get(1).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(1).value).toEqual('Foo'); - expect(tokens.get(2).type).toBe(TokenType.KEYWORD); - expect(tokens.get(2).value).toBe('function'); + expect(tokens.get(2).type).toEqual(TokenType.KEYWORD); + expect(tokens.get(2).value).toEqual('function'); - expect(tokens.get(3).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(3).value).toBe('bar'); + expect(tokens.get(3).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(3).value).toEqual('bar'); }); it('should tokenize a code statement', () => { const tokens = lexer.tokenize(CODE.STATEMENT); - expect(tokens.size).toBe(17); + expect(tokens.size).toEqual(17); - expect(tokens.get(0).type).toBe(TokenType.KEYWORD); - expect(tokens.get(0).value).toBe('const'); + expect(tokens.get(0).type).toEqual(TokenType.KEYWORD); + expect(tokens.get(0).value).toEqual('const'); - expect(tokens.get(1).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(1).value).toBe('identifier'); + expect(tokens.get(1).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(1).value).toEqual('identifier'); - expect(tokens.get(2).type).toBe(TokenType.OPERATOR); - expect(tokens.get(2).value).toBe(Operator.ASSIGN); + expect(tokens.get(2).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(2).value).toEqual(Operator.ASSIGN); - expect(tokens.get(3).type).toBe(TokenType.GROUP); - expect(tokens.get(3).value).toBe(Group.OPEN); + expect(tokens.get(3).type).toEqual(TokenType.GROUP); + expect(tokens.get(3).value).toEqual(Group.OPEN); - expect(tokens.get(4).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(4).value).toBe('12'); + expect(tokens.get(4).type).toEqual(TokenType.NUMBER); + expect(tokens.get(4).value).toEqual('12'); - expect(tokens.get(5).type).toBe(TokenType.OPERATOR); - expect(tokens.get(5).value).toBe(Operator.GREATER_EQUAL); + expect(tokens.get(5).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(5).value).toEqual(Operator.GREATER_EQUAL); - expect(tokens.get(6).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(6).value).toBe('3'); + expect(tokens.get(6).type).toEqual(TokenType.NUMBER); + expect(tokens.get(6).value).toEqual('3'); - expect(tokens.get(7).type).toBe(TokenType.GROUP); - expect(tokens.get(7).value).toBe(Group.CLOSE); + expect(tokens.get(7).type).toEqual(TokenType.GROUP); + expect(tokens.get(7).value).toEqual(Group.CLOSE); - expect(tokens.get(8).type).toBe(TokenType.OPERATOR); - expect(tokens.get(8).value).toBe(Operator.TERNARY); + expect(tokens.get(8).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(8).value).toEqual(Operator.TERNARY); - expect(tokens.get(9).type).toBe(TokenType.SCOPE); - expect(tokens.get(9).value).toBe(Scope.OPEN); + expect(tokens.get(9).type).toEqual(TokenType.SCOPE); + expect(tokens.get(9).value).toEqual(Scope.OPEN); - expect(tokens.get(10).type).toBe(TokenType.LITERAL); - expect(tokens.get(10).value).toBe("'foo'"); + expect(tokens.get(10).type).toEqual(TokenType.LITERAL); + expect(tokens.get(10).value).toEqual("'foo'"); - expect(tokens.get(11).type).toBe(TokenType.SCOPE); - expect(tokens.get(11).value).toBe(Scope.CLOSE); + expect(tokens.get(11).type).toEqual(TokenType.SCOPE); + expect(tokens.get(11).value).toEqual(Scope.CLOSE); - expect(tokens.get(12).type).toBe(TokenType.DIVIDER); - expect(tokens.get(12).value).toBe(Divider.SCOPE); + expect(tokens.get(12).type).toEqual(TokenType.DIVIDER); + expect(tokens.get(12).value).toEqual(Divider.SCOPE); - expect(tokens.get(13).type).toBe(TokenType.LIST); - expect(tokens.get(13).value).toBe(List.OPEN); + expect(tokens.get(13).type).toEqual(TokenType.LIST); + expect(tokens.get(13).value).toEqual(List.OPEN); - expect(tokens.get(14).type).toBe(TokenType.LITERAL); - expect(tokens.get(14).value).toBe('"bar"'); + expect(tokens.get(14).type).toEqual(TokenType.LITERAL); + expect(tokens.get(14).value).toEqual('"bar"'); - expect(tokens.get(15).type).toBe(TokenType.LIST); - expect(tokens.get(15).value).toBe(List.CLOSE); + expect(tokens.get(15).type).toEqual(TokenType.LIST); + expect(tokens.get(15).value).toEqual(List.CLOSE); - expect(tokens.get(16).type).toBe(TokenType.DIVIDER); - expect(tokens.get(16).value).toBe(Divider.TERMINATOR); + expect(tokens.get(16).type).toEqual(TokenType.DIVIDER); + expect(tokens.get(16).value).toEqual(Divider.TERMINATOR); }); it('should tokenize a regex statement', () => { const tokens = lexer.tokenize(CODE.REGEX_STATEMENT); - expect(tokens.size).toBe(8); + expect(tokens.size).toEqual(9); + + expect(tokens.get(0).type).toEqual(TokenType.KEYWORD); + expect(tokens.get(0).value).toEqual(Keyword.CONST); - expect(tokens.get(0).type).toBe(TokenType.KEYWORD); - expect(tokens.get(0).value).toBe(Keyword.CONST); + expect(tokens.get(1).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(1).value).toEqual('regex'); - expect(tokens.get(1).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(1).value).toBe('regex'); + expect(tokens.get(2).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(2).value).toEqual(Operator.ASSIGN); - expect(tokens.get(2).type).toBe(TokenType.OPERATOR); - expect(tokens.get(2).value).toBe(Operator.ASSIGN); + expect(tokens.get(3).type).toEqual(TokenType.REGEX); + expect(tokens.get(3).value).toEqual(`/[\\"]['"]/g`); - expect(tokens.get(3).type).toBe(TokenType.REGEX); - expect(tokens.get(3).value).toBe(`/[\\"]['"]/g`); + expect(tokens.get(4).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(4).value).toEqual(Operator.CHAINING); - expect(tokens.get(4).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(4).value).toBe('.test'); + expect(tokens.get(5).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(5).value).toEqual('test'); - expect(tokens.get(5).type).toBe(TokenType.GROUP); - expect(tokens.get(5).value).toBe(Group.OPEN); + expect(tokens.get(6).type).toEqual(TokenType.GROUP); + expect(tokens.get(6).value).toEqual(Group.OPEN); - expect(tokens.get(6).type).toBe(TokenType.LITERAL); - expect(tokens.get(6).value).toBe("'foo'"); + expect(tokens.get(7).type).toEqual(TokenType.LITERAL); + expect(tokens.get(7).value).toEqual("'foo'"); - expect(tokens.get(7).type).toBe(TokenType.GROUP); - expect(tokens.get(7).value).toBe(Group.CLOSE); + expect(tokens.get(8).type).toEqual(TokenType.GROUP); + expect(tokens.get(8).value).toEqual(Group.CLOSE); }); it('should tokenize a regex array', () => { const tokens = lexer.tokenize(CODE.REGEX_ARRAY); - expect(tokens.size).toBe(5); + expect(tokens.size).toEqual(5); - expect(tokens.get(0).type).toBe(TokenType.LIST); - expect(tokens.get(0).value).toBe(List.OPEN); + expect(tokens.get(0).type).toEqual(TokenType.LIST); + expect(tokens.get(0).value).toEqual(List.OPEN); - expect(tokens.get(1).type).toBe(TokenType.REGEX); - expect(tokens.get(1).value).toBe(`/[\\"]['"]/g`); + expect(tokens.get(1).type).toEqual(TokenType.REGEX); + expect(tokens.get(1).value).toEqual(`/[\\"]['"]/g`); - expect(tokens.get(2).type).toBe(TokenType.DIVIDER); - expect(tokens.get(2).value).toBe(Divider.SEPARATOR); + expect(tokens.get(2).type).toEqual(TokenType.DIVIDER); + expect(tokens.get(2).value).toEqual(Divider.SEPARATOR); - expect(tokens.get(3).type).toBe(TokenType.REGEX); - expect(tokens.get(3).value).toBe(`/Windows (?:NT|Phone) ([0-9.]+)/`); + expect(tokens.get(3).type).toEqual(TokenType.REGEX); + expect(tokens.get(3).value).toEqual(`/Windows (?:NT|Phone) ([0-9.]+)/`); - expect(tokens.get(4).type).toBe(TokenType.LIST); - expect(tokens.get(4).value).toBe(List.CLOSE); + expect(tokens.get(4).type).toEqual(TokenType.LIST); + expect(tokens.get(4).value).toEqual(List.CLOSE); }); it('should tokenize a regex object', () => { const tokens = lexer.tokenize(CODE.REGEX_OBJECT); - expect(tokens.size).toBe(5); + expect(tokens.size).toEqual(5); - expect(tokens.get(0).type).toBe(TokenType.SCOPE); - expect(tokens.get(0).value).toBe(Scope.OPEN); + expect(tokens.get(0).type).toEqual(TokenType.SCOPE); + expect(tokens.get(0).value).toEqual(Scope.OPEN); - expect(tokens.get(1).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(1).value).toBe('test'); + expect(tokens.get(1).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(1).value).toEqual('test'); - expect(tokens.get(2).type).toBe(TokenType.DIVIDER); - expect(tokens.get(2).value).toBe(Divider.SCOPE); + expect(tokens.get(2).type).toEqual(TokenType.DIVIDER); + expect(tokens.get(2).value).toEqual(Divider.SCOPE); - expect(tokens.get(3).type).toBe(TokenType.REGEX); - expect(tokens.get(3).value).toBe(`/[\\"]['"]/g`); + expect(tokens.get(3).type).toEqual(TokenType.REGEX); + expect(tokens.get(3).value).toEqual(`/[\\"]['"]/g`); - expect(tokens.get(4).type).toBe(TokenType.SCOPE); - expect(tokens.get(4).value).toBe(Scope.CLOSE); + expect(tokens.get(4).type).toEqual(TokenType.SCOPE); + expect(tokens.get(4).value).toEqual(Scope.CLOSE); }); it('should tokenize a regex result', () => { const tokens = lexer.tokenize(CODE.REGEX_RESULT); - expect(tokens.size).toBe(3); + expect(tokens.size).toEqual(3); - expect(tokens.get(0).type).toBe(TokenType.KEYWORD); - expect(tokens.get(0).value).toBe(Keyword.RETURN); + expect(tokens.get(0).type).toEqual(TokenType.KEYWORD); + expect(tokens.get(0).value).toEqual(Keyword.RETURN); - expect(tokens.get(1).type).toBe(TokenType.REGEX); - expect(tokens.get(1).value).toBe(`/[\\"]['"]/g`); + expect(tokens.get(1).type).toEqual(TokenType.REGEX); + expect(tokens.get(1).value).toEqual(`/[\\"]['"]/g`); - expect(tokens.get(2).type).toBe(TokenType.DIVIDER); - expect(tokens.get(2).value).toBe(Divider.TERMINATOR); + expect(tokens.get(2).type).toEqual(TokenType.DIVIDER); + expect(tokens.get(2).value).toEqual(Divider.TERMINATOR); }); it('should tokenize a regex argument', () => { const tokens = lexer.tokenize(CODE.REGEX_ARGUMENT); - expect(tokens.size).toBe(4); + expect(tokens.size).toEqual(4); - expect(tokens.get(0).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(0).value).toBe('doSomething'); + expect(tokens.get(0).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(0).value).toEqual('doSomething'); - expect(tokens.get(1).type).toBe(TokenType.GROUP); - expect(tokens.get(1).value).toBe(Group.OPEN); + expect(tokens.get(1).type).toEqual(TokenType.GROUP); + expect(tokens.get(1).value).toEqual(Group.OPEN); - expect(tokens.get(2).type).toBe(TokenType.REGEX); - expect(tokens.get(2).value).toBe(`/[\\"]['"]/g`); + expect(tokens.get(2).type).toEqual(TokenType.REGEX); + expect(tokens.get(2).value).toEqual(`/[\\"]['"]/g`); - expect(tokens.get(3).type).toBe(TokenType.GROUP); - expect(tokens.get(3).value).toBe(Group.CLOSE); + expect(tokens.get(3).type).toEqual(TokenType.GROUP); + expect(tokens.get(3).value).toEqual(Group.CLOSE); }); it('should tokenize minified code', () => { const tokens = lexer.tokenize(CODE.MINIFIED); - expect(tokens.size).toBe(6); + expect(tokens.size).toEqual(6); - expect(tokens.get(0).type).toBe(TokenType.KEYWORD); - expect(tokens.get(0).value).toBe(Keyword.RETURN); + expect(tokens.get(0).type).toEqual(TokenType.KEYWORD); + expect(tokens.get(0).value).toEqual(Keyword.RETURN); - expect(tokens.get(1).type).toBe(TokenType.LITERAL); - expect(tokens.get(1).value).toBe('`foo`'); + expect(tokens.get(1).type).toEqual(TokenType.LITERAL); + expect(tokens.get(1).value).toEqual('`foo`'); - expect(tokens.get(2).type).toBe(TokenType.DIVIDER); - expect(tokens.get(2).value).toBe(Divider.TERMINATOR); + expect(tokens.get(2).type).toEqual(TokenType.DIVIDER); + expect(tokens.get(2).value).toEqual(Divider.TERMINATOR); - expect(tokens.get(3).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(3).value).toBe('identifier1'); + expect(tokens.get(3).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(3).value).toEqual('identifier1'); - expect(tokens.get(4).type).toBe(TokenType.OPERATOR); - expect(tokens.get(4).value).toBe(Operator.ASSIGN); + expect(tokens.get(4).type).toEqual(TokenType.OPERATOR); + expect(tokens.get(4).value).toEqual(Operator.ASSIGN); - expect(tokens.get(5).type).toBe(TokenType.IDENTIFIER); - expect(tokens.get(5).value).toBe('identifier2'); + expect(tokens.get(5).type).toEqual(TokenType.IDENTIFIER); + expect(tokens.get(5).value).toEqual('identifier2'); }); }); }); diff --git a/packages/analysis/test/static/Parser.spec.ts b/packages/analysis/test/static/Parser.spec.ts index 24330a08..f443af14 100644 --- a/packages/analysis/test/static/Parser.spec.ts +++ b/packages/analysis/test/static/Parser.spec.ts @@ -1,985 +1,1192 @@ import { describe, expect, it } from 'vitest'; -import { ESArray, ESObject, ESExpression, ESField, ESGenerator, ESDestructuredArray, ESDestructuredObject } from '../../src/models'; +import { + ESIdentifierBinding, ESArrayBinding, ESObjectBinding, ESBindingElement, + ESExpression, + ESFunction, ESArrowFunction, ESGeneratorFunction, + ESClass, ESGeneratorMethod, ESConstructor, + ESExport, + ESVariable +} from '../../src/model'; + import { Parser } from '../../src/static'; -import { VALUES, IMPORTS, EXPORTS, DECLARATIONS, FUNCTIONS, CLASSES, MODULES } from './fixtures'; +import { VALUES, IMPORTS, EXPORTS, DECLARATIONS, FUNCTIONS, CLASSES, MODULES, MODULES_STRINGS } from './fixtures'; const parser = new Parser(); -describe('parser/Parser', () => +describe('Parser', () => { - describe('.parseValue(code)', () => + describe('Statements', () => { it('should parse an array', () => { - const value = parser.parseValue(VALUES.ARRAY); - expect(value).toBeInstanceOf(ESArray); - expect(value.definition).toBe('[ 1 , "foo" , false , new Person ( "Peter" , 42 ) , { a : 1 , b : 2 } ]'); + const expression = parser.parseStatement(VALUES.ARRAY); + expect(expression).toBeInstanceOf(ESExpression); + expect(expression.toString(false)).toEqual('[1,"foo",false,new Person("Peter",42),{a:1,b:2}]'); }); it('should parse an object', () => { - const value = parser.parseValue(VALUES.OBJECT); - expect(value).toBeInstanceOf(ESObject); - expect(value.definition).toBe('{ key1 : "value1" , "key2" : new Person ( ) .toString ( ) }'); + const expression = parser.parseStatement(VALUES.OBJECT); + expect(expression).toBeInstanceOf(ESExpression); + expect(expression.toString(false)).toEqual('{key1:"value1","key2":new Person().toString()}'); }); it('should parse an expression', () => { - const value = parser.parseValue(VALUES.EXPRESSION); - expect(value).toBeInstanceOf(ESExpression); - expect(value.definition).toBe('new Number ( Math.ceil ( Math.random ( ) ) + 10 ) .toString ( )'); + const expression = parser.parseStatement(VALUES.EXPRESSION); + expect(expression).toBeInstanceOf(ESExpression); + expect(expression.toString(false)).toEqual('new Number(Math.ceil(Math.random())+10).toString()'); }); it('should parse a grouped expression', () => { - const value = parser.parseValue(VALUES.EXPRESSION_GROUP); - expect(value).toBeInstanceOf(ESExpression); - expect(value.definition).toBe('( a + b ) * c'); + const expression = parser.parseStatement(VALUES.EXPRESSION_GROUP); + expect(expression).toBeInstanceOf(ESExpression); + expect(expression.toString(false)).toEqual('(a+b)*c'); }); it('should parse an if...else expression', () => { - const value = parser.parseValue(VALUES.IF_ELSE); - expect(value).toBeInstanceOf(ESExpression); - expect(value.definition).toBe('if ( true ) { return "value1" ; } else { return "value2" ; }'); + const expression = parser.parseStatement(VALUES.IF_ELSE); + expect(expression).toBeInstanceOf(ESExpression); + expect(expression.toString(false)).toEqual('if(true){return "value1";}else{return "value2";}'); }); it('should parse an try...catch...finally expression', () => { - const value = parser.parseValue(VALUES.TRY_CATCH_FINALLY); - expect(value).toBeInstanceOf(ESExpression); - expect(value.definition).toBe('try { sum ( 1 , 2 ) ; } catch ( error ) { console.error ( error ) ; } finally { console.log ( "finally" ) ; }'); + const expression = parser.parseStatement(VALUES.TRY_CATCH_FINALLY); + expect(expression).toBeInstanceOf(ESExpression); + expect(expression.toString(false)).toEqual('try{sum(1,2);}catch(error){console.error(error);}finally{console.log("finally");}'); }); }); - describe('.parseImport(code)', () => + describe('Imports', () => { it('should parse loading a module', () => { const imported = parser.parseImport(IMPORTS.LOAD); - expect(imported.members.length).toBe(0); - expect(imported.from).toBe("'module'"); + expect(imported.members).toHaveLength(0); + expect(imported.from).toEqual('module'); }); it('should parse importing a single member', () => { const imported = parser.parseImport(IMPORTS.IMPORT); - expect(imported.members.length).toBe(1); - expect(imported.from).toBe("'module'"); + expect(imported.members).toHaveLength(1); + expect(imported.from).toEqual('module'); const member = imported.members[0]; - expect(member.name).toBe('member'); - expect(member.as).toBe('member'); + expect(member.identifier).toEqual('member'); + expect(member.alias).toBeUndefined(); }); it('should parse importing a single member with alias', () => { const imported = parser.parseImport(IMPORTS.IMPORT_AS); - expect(imported.members.length).toBe(1); - expect(imported.from).toBe("'module'"); + expect(imported.members).toHaveLength(1); + expect(imported.from).toEqual('module'); const member = imported.members[0]; - expect(member.name).toBe('member'); - expect(member.as).toBe('alias'); + expect(member.identifier).toEqual('member'); + expect(member.alias).toEqual('alias'); }); it('should parse importing all members', () => { const imported = parser.parseImport(IMPORTS.IMPORT_ALL); - expect(imported.members.length).toBe(1); - expect(imported.from).toBe("'module'"); + expect(imported.members).toHaveLength(1); + expect(imported.from).toEqual('module'); const member = imported.members[0]; - expect(member.name).toBe('*'); - expect(member.as).toBe('name'); + expect(member.identifier).toEqual('*'); + expect(member.alias).toEqual('name'); }); it('should parse importing the default member', () => { const imported = parser.parseImport(IMPORTS.IMPORT_DEFAULT); - expect(imported.members.length).toBe(1); - expect(imported.from).toBe("'module'"); + expect(imported.members).toHaveLength(1); + expect(imported.from).toEqual('module'); const member = imported.members[0]; - expect(member.name).toBe('default'); - expect(member.as).toBe('name'); + expect(member.identifier).toEqual('default'); + expect(member.alias).toEqual('name'); }); it('should parse importing the default with another member', () => { const imported = parser.parseImport(IMPORTS.IMPORT_DEFAULT_MEMBER); - expect(imported.members.length).toBe(2); - expect(imported.from).toBe("'module'"); + expect(imported.members).toHaveLength(2); + expect(imported.from).toEqual('module'); const first = imported.members[0]; - expect(first.name).toBe('default'); - expect(first.as).toBe('name'); + expect(first.identifier).toEqual('default'); + expect(first.alias).toEqual('name'); const second = imported.members[1]; - expect(second.name).toBe('member'); - expect(second.as).toBe('member'); + expect(second.identifier).toEqual('member'); + expect(second.alias).toBeUndefined(); }); it('should parse importing the default with another member with alias', () => { const imported = parser.parseImport(IMPORTS.IMPORT_DEFAULT_MEMBER_AS); - expect(imported.members.length).toBe(2); - expect(imported.from).toBe("'module'"); + expect(imported.members).toHaveLength(2); + expect(imported.from).toEqual('module'); const first = imported.members[0]; - expect(first.name).toBe('default'); - expect(first.as).toBe('name'); + expect(first.identifier).toEqual('default'); + expect(first.alias).toEqual('name'); const second = imported.members[1]; - expect(second.name).toBe('member'); - expect(second.as).toBe('alias'); + expect(second.identifier).toEqual('member'); + expect(second.alias).toEqual('alias'); }); it('should parse importing the default with other members and aliases', () => { const imported = parser.parseImport(IMPORTS.IMPORT_MULTIPLE_MEMBERS_AS); - expect(imported.members.length).toBe(2); - expect(imported.from).toBe("'module'"); + expect(imported.members).toHaveLength(2); + expect(imported.from).toEqual('module'); const first = imported.members[0]; - expect(first.name).toBe('name'); - expect(first.as).toBe('name'); + expect(first.identifier).toEqual('name'); + expect(first.alias).toBeUndefined(); const second = imported.members[1]; - expect(second.name).toBe('member'); - expect(second.as).toBe('alias'); + expect(second.identifier).toEqual('member'); + expect(second.alias).toEqual('alias'); }); it('should parse importing modules dynamically', () => { const imported = parser.parseImport(IMPORTS.IMPORT_DYNAMIC); - expect(imported.members.length).toBe(0); - expect(imported.from).toBe("'module'"); + expect(imported.members).toHaveLength(0); + expect(imported.from).toEqual('module'); }); }); - describe('.parseExport(code)', () => + describe('Exports', () => { it('should parse exporting a single member', () => { const exported = parser.parseExport(EXPORTS.EXPORT); - expect(exported.members.length).toBe(1); - expect(exported.from).toBe(undefined); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); const member = exported.members[0]; - expect(member.name).toBe('member'); - expect(member.as).toBe('member'); + expect(member.identifier).toEqual('member'); + expect(member.alias).toBeUndefined(); }); it('should parse exporting a single member with alias', () => { const exported = parser.parseExport(EXPORTS.EXPORT_AS); - expect(exported.members.length).toBe(1); - expect(exported.from).toBe(undefined); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); const member = exported.members[0]; - expect(member.name).toBe('member'); - expect(member.as).toBe('alias'); + expect(member.identifier).toEqual('member'); + expect(member.alias).toEqual('alias'); }); it('should parse exporting a default', () => { const exported = parser.parseExport(EXPORTS.EXPORT_DEFAULT); - expect(exported.members.length).toBe(1); - expect(exported.from).toBe(undefined); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); const member = exported.members[0]; - expect(member.name).toBe('name'); - expect(member.as).toBe('default'); + expect(member.identifier).toEqual('name'); + expect(member.alias).toEqual('default'); }); it('should parse exporting multiple members', () => { const exported = parser.parseExport(EXPORTS.EXPORT_MULTIPLE); - expect(exported.members.length).toBe(2); - expect(exported.from).toBe(undefined); + expect(exported.members).toHaveLength(2); + expect(exported.from).toBeUndefined(); const first = exported.members[0]; - expect(first.name).toBe('name'); - expect(first.as).toBe('name'); + expect(first.identifier).toEqual('name'); + expect(first.alias).toBeUndefined(); const second = exported.members[1]; - expect(second.name).toBe('member'); - expect(second.as).toBe('member'); + expect(second.identifier).toEqual('member'); + expect(second.alias).toBeUndefined(); }); it('should parse exporting multiple members with alias', () => { const exported = parser.parseExport(EXPORTS.EXPORT_MULTIPLE_AS); - expect(exported.members.length).toBe(2); - expect(exported.from).toBe(undefined); + expect(exported.members).toHaveLength(2); + expect(exported.from).toBeUndefined(); const first = exported.members[0]; - expect(first.name).toBe('name'); - expect(first.as).toBe('name'); + expect(first.identifier).toEqual('name'); + expect(first.alias).toBeUndefined(); const second = exported.members[1]; - expect(second.name).toBe('member'); - expect(second.as).toBe('alias'); + expect(second.identifier).toEqual('member'); + expect(second.alias).toEqual('alias'); }); it('should parse exporting a class declaration', () => { const exported = parser.parseExport(EXPORTS.EXPORT_CLASS_DECLARATION); - expect(exported.members.length).toBe(1); - expect(exported.from).toBe(undefined); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); const member = exported.members[0]; - expect(member.name).toBe('name'); - expect(member.as).toBe('name'); + expect(member.identifier).toEqual('name'); + expect(member.alias).toBeUndefined(); }); it('should parse exporting a function declaration', () => { const exported = parser.parseExport(EXPORTS.EXPORT_FUNCTION_DECLARATION); - expect(exported.members.length).toBe(1); - expect(exported.from).toBe(undefined); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); const member = exported.members[0]; - expect(member.name).toBe('name'); - expect(member.as).toBe('name'); + expect(member.identifier).toEqual('name'); + expect(member.alias).toBeUndefined(); }); - it('should parse exporting a async function declaration', () => + it('should parse exporting a field declaration', () => { const exported = parser.parseExport(EXPORTS.EXPORT_FIELD_DECLARATION); - expect(exported.members.length).toBe(1); - expect(exported.from).toBe(undefined); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); + + const member = exported.members[0]; + expect(member.identifier).toEqual('name'); + expect(member.alias).toBeUndefined(); + }); + + it('should parse exporting a default value', () => + { + const identifier = '$_EXPORT_15_17'; + + const module = parser.parse(EXPORTS.EXPORT_DEFAULT_VALUE); + expect(module.statements).toHaveLength(2); + + const exported = module.statements[0] as ESExport; + expect(exported).toBeInstanceOf(ESExport); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); + + const member = exported.members[0]; + expect(member.identifier).toEqual(identifier); + expect(member.alias).toEqual('default'); + + const declaration = module.statements[1] as ESVariable; + expect(declaration).toBeInstanceOf(ESVariable); + expect(declaration.type).toEqual('const'); + expect(declaration.identifier).toEqual(identifier); + expect(declaration.initializer).toBeInstanceOf(ESExpression); + expect(declaration.initializer?.toString(false)).toEqual('42'); + }); + + it('should parse exporting a default instance', () => + { + const identifier = '$_EXPORT_15_17'; + + const module = parser.parse(EXPORTS.EXPORT_DEFAULT_INSTANCE); + expect(module.statements).toHaveLength(2); + + const exported = module.statements[0] as ESExport; + expect(exported).toBeInstanceOf(ESExport); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); + + const member = exported.members[0]; + expect(member.identifier).toEqual(identifier); + expect(member.alias).toEqual('default'); + + const declaration = module.statements[1] as ESVariable; + expect(declaration).toBeInstanceOf(ESVariable); + expect(declaration.type).toEqual('const'); + expect(declaration.identifier).toEqual(identifier); + expect(declaration.initializer).toBeInstanceOf(ESExpression); + expect(declaration.initializer?.toString(false)).toEqual('new Date()'); + }); + + it('should parse exporting a function call', () => + { + const identifier = '$_EXPORT_15_18'; + + const module = parser.parse(EXPORTS.EXPORT_DEFAULT_CALL); + expect(module.statements).toHaveLength(2); + + const exported = module.statements[0] as ESExport; + expect(exported).toBeInstanceOf(ESExport); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); const member = exported.members[0]; - expect(member.name).toBe('name'); - expect(member.as).toBe('name'); + expect(member.identifier).toEqual(identifier); + expect(member.alias).toEqual('default'); + + const declaration = module.statements[1] as ESVariable; + expect(declaration).toBeInstanceOf(ESVariable); + expect(declaration.type).toEqual('const'); + expect(declaration.identifier).toEqual(identifier); + expect(declaration.initializer).toBeInstanceOf(ESExpression); + expect(declaration.initializer?.toString(false)).toEqual('name()'); + }); + + it('should parse exporting a default function', () => + { + const module = parser.parse(EXPORTS.EXPORT_DEFAULT_FUNCTION); + expect(module.statements).toHaveLength(2); + + const exported = module.statements[0] as ESExport; + expect(exported).toBeInstanceOf(ESExport); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); + + const member = exported.members[0]; + expect(member.identifier).toEqual(''); + expect(member.alias).toEqual('default'); + + const declaration = module.statements[1] as ESFunction; + expect(declaration).toBeInstanceOf(ESFunction); + }); + + it('should parse exporting a default class', () => + { + const module = parser.parse(EXPORTS.EXPORT_DEFAULT_CLASS); + expect(module.statements).toHaveLength(2); + + const exported = module.statements[0] as ESExport; + expect(exported).toBeInstanceOf(ESExport); + expect(exported.members).toHaveLength(1); + expect(exported.from).toBeUndefined(); + + const member = exported.members[0]; + expect(member.identifier).toEqual(''); + expect(member.alias).toEqual('default'); + + const declaration = module.statements[1] as ESClass; + expect(declaration).toBeInstanceOf(ESClass); }); it('should parse reexporting a module', () => { const exported = parser.parseExport(EXPORTS.REEXPORT_ALL); - expect(exported.members.length).toBe(1); - expect(exported.from).toBe("'module'"); + expect(exported.members).toHaveLength(1); + expect(exported.from).toEqual('module'); const member = exported.members[0]; - expect(member.name).toBe(''); - expect(member.as).toBe(''); + expect(member.identifier).toEqual(''); + expect(member.alias).toBeUndefined(); }); it('should parse reexporting a member', () => { const exported = parser.parseExport(EXPORTS.REEXPORT_MEMBER); - expect(exported.members.length).toBe(1); - expect(exported.from).toBe("'module'"); + expect(exported.members).toHaveLength(1); + expect(exported.from).toEqual('module'); const member = exported.members[0]; - expect(member.name).toBe('member'); - expect(member.as).toBe('member'); + expect(member.identifier).toEqual('member'); + expect(member.alias).toBeUndefined(); }); }); - describe('.parseDeclaration(code)', () => + describe('Variables', () => { it('should parse an empty declaration', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.EMPTY); - - expect(declaration.name).toBe('name'); - expect(declaration.value).toBe(undefined); + const variable = parser.parseVariable(DECLARATIONS.EMPTY); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('let'); + expect(variable.initializer).toBeUndefined(); }); - it('should parse a const declaration', () => + it('should parse a const variable', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.CONST); - - expect(declaration.name).toBe('name'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.value?.definition).toBe("'const'"); + const variable = parser.parseVariable(DECLARATIONS.CONST); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual("'const'"); }); it('should parse a let declaration with value', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.LET); - - expect(declaration.name).toBe('name'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.value?.definition).toBe("'let'"); + const variable = parser.parseVariable(DECLARATIONS.LET); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('let'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual("'let'"); }); it('should parse a var declaration with value', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.VAR); - - expect(declaration.name).toBe('name'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.value?.definition).toBe("'var'"); + const variable = parser.parseVariable(DECLARATIONS.VAR); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('var'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual("'var'"); }); it('should parse a declaration with multiple declarations', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.MULTIPLE); - - expect(declaration.name).toBe('name1'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.value?.definition).toBe('( 1 + 2 ) * 3'); + const variable = parser.parseVariable(DECLARATIONS.MULTIPLE); + expect(variable.identifier).toEqual('name1'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('let'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual('(1+2)*3'); }); it('should parse a declaration with an expression', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.EXPRESSION); - - expect(declaration.name).toBe('number'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.value?.definition).toBe("new Number ( Math.ceil ( Math.random ( ) ) + 10 ) .toString ( )"); + const variable = parser.parseVariable(DECLARATIONS.EXPRESSION); + expect(variable.identifier).toEqual('number'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual('new Number(Math.ceil(Math.random())+10).toString()'); }); it('should parse a declaration with an array value', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.ARRAY); - - expect(declaration.name).toBe('array'); - expect(declaration.value).toBeInstanceOf(ESArray); - expect(declaration.value?.definition).toBe("[ 'value1' , 'value2' ]"); + const variable = parser.parseVariable(DECLARATIONS.ARRAY); + expect(variable.identifier).toEqual('array'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual("['value1','value2']"); }); - it('should parse a declaration with an array value', () => + it('should parse a declaration with an object value', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.OBJECT); - - expect(declaration.name).toBe('object'); - expect(declaration.value).toBeInstanceOf(ESObject); - expect(declaration.value?.definition).toBe("{ key1 : 'value1' , key2 : 'value2' }"); + const variable = parser.parseVariable(DECLARATIONS.OBJECT); + expect(variable.identifier).toEqual('object'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual("{key1:'value1',key2:'value2'}"); }); it('should parse a declaration with a regex value', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.REGEX); - - expect(declaration.name).toBe('regex'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.value?.definition).toBe("/regex/g"); + const variable = parser.parseVariable(DECLARATIONS.REGEX); + expect(variable.identifier).toEqual('regex'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual("/regex/g"); }); it('should parse a declaration that is destructuring an array', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.DESTRUCTURING_ARRAY); + const variable = parser.parseVariable(DECLARATIONS.DESTRUCTURING_ARRAY); + expect(variable.identifier).toEqual('[value1,value2=true]'); + expect(variable.binding).toBeInstanceOf(ESArrayBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual('array'); - expect(declaration.name).toBe('[ value1 , value2 = true ]'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.identifier).toBeInstanceOf(ESDestructuredArray); - - const identifier = declaration.identifier as ESDestructuredArray; - expect(identifier.members.length).toBe(2); + const binding = variable.binding as ESArrayBinding; + expect(binding.elements).toHaveLength(2); - const firstMember = identifier.members[0] as ESField; - expect(firstMember).toBeInstanceOf(ESField); - expect(firstMember.name).toBe('value1'); - expect(firstMember.value).toBe(undefined); + const firstElement = binding.elements[0]; + expect(firstElement).toBeInstanceOf(ESBindingElement); + expect(firstElement.binding).toBeInstanceOf(ESIdentifierBinding); + expect(firstElement.binding.toString()).toEqual('value1'); + expect(firstElement.initializer).toBeUndefined(); - const secondMember = identifier.members[1] as ESField; - expect(secondMember).toBeInstanceOf(ESField); - expect(secondMember.name).toBe('value2'); - expect(secondMember.value).toBeInstanceOf(ESExpression); - expect(secondMember.value?.definition).toBe('true'); + const secondElement = binding.elements[1]; + expect(secondElement).toBeInstanceOf(ESBindingElement); + expect(secondElement.binding).toBeInstanceOf(ESIdentifierBinding); + expect(secondElement.binding.toString()).toEqual('value2'); + expect(secondElement.initializer).toBeInstanceOf(ESExpression); + expect(secondElement.initializer?.toString(false)).toEqual('true'); }); it('should parse a declaration that is destructuring an object', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.DESTRUCTURING_OBJECT); - - expect(declaration.name).toBe('{ key1 , key2 = false }'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.identifier).toBeInstanceOf(ESDestructuredObject); + const variable = parser.parseVariable(DECLARATIONS.DESTRUCTURING_OBJECT); + expect(variable.identifier).toEqual('{key1,key2=false}'); + expect(variable.binding).toBeInstanceOf(ESObjectBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual('object'); - const identifier = declaration.identifier as ESDestructuredArray; - expect(identifier.members.length).toBe(2); + const binding = variable.binding as ESObjectBinding; + expect(binding.elements).toHaveLength(2); - const firstMember = identifier.members[0] as ESField; - expect(firstMember).toBeInstanceOf(ESField); - expect(firstMember.name).toBe('key1'); + const firstElement = binding.elements[0]; + expect(firstElement.binding).toBeInstanceOf(ESIdentifierBinding); + expect(firstElement.binding.toString()).toEqual('key1'); + expect(firstElement.initializer).toBeUndefined(); - const secondMember = identifier.members[1] as ESField; - expect(secondMember).toBeInstanceOf(ESField); - expect(secondMember.name).toBe('key2'); - expect(secondMember.value).toBeInstanceOf(ESExpression); - expect(secondMember.value?.definition).toBe('false'); + const secondElement = binding.elements[1]; + expect(secondElement).toBeInstanceOf(ESBindingElement); + expect(secondElement.binding).toBeInstanceOf(ESIdentifierBinding); + expect(secondElement.binding.toString()).toEqual('key2'); + expect(secondElement.initializer).toBeInstanceOf(ESExpression); + expect(secondElement.initializer?.toString(false)).toEqual('false'); }); it('should parse a declaration with a non reserved keyword as name', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.KEYWORD_AS_NAME); - - expect(declaration.name).toBe('as'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.value?.definition).toBe("'value'"); + const variable = parser.parseVariable(DECLARATIONS.KEYWORD_AS_NAME); + expect(variable.identifier).toEqual('as'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual("'value'"); }); it('should parse a declaration that refers to a non reserved keyword as value', () => { - const declaration = parser.parseDeclaration(DECLARATIONS.KEYWORD_AS_VALUE); - - expect(declaration.name).toBe('alias'); - expect(declaration.value).toBeInstanceOf(ESExpression); - expect(declaration.value?.definition).toBe('as'); + const variable = parser.parseVariable(DECLARATIONS.KEYWORD_AS_VALUE); + expect(variable.identifier).toEqual('alias'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESExpression); + expect(variable.initializer?.toString(false)).toEqual('as'); }); }); - describe('.parseFunction(code)', () => + describe('Functions', () => { it('should parse a simple function declaration', () => { const funktion = parser.parseFunction(FUNCTIONS.DECLARATION); - - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse an async function declaration', () => { const funktion = parser.parseFunction(FUNCTIONS.ASYNC_DECLARATION); - - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(true); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeTruthy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse an expression function declaration', () => { - const funktion = parser.parseFunction(FUNCTIONS.EXPRESSION); + const variable = parser.parseVariable(FUNCTIONS.EXPRESSION); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESFunction); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + const funktion = variable.initializer as ESFunction; + expect(funktion.identifier).toBeUndefined(); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse an async expression function declaration', () => { - const funktion = parser.parseFunction(FUNCTIONS.ASYNC_EXPRESSION); - - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(true); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + const variable = parser.parseVariable(FUNCTIONS.ASYNC_EXPRESSION); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESFunction); + + const funktion = variable.initializer as ESFunction; + expect(funktion.identifier).toBeUndefined(); + expect(funktion.isAsync).toBeTruthy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse an arrow function declaration', () => { - const funktion = parser.parseFunction(FUNCTIONS.ARROW); - - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + const variable = parser.parseVariable(FUNCTIONS.ARROW); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESArrowFunction); + + const funktion = variable.initializer as ESArrowFunction; + expect(funktion.identifier).toBeUndefined(); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse an arrow function expression declaration', () => { - const funktion = parser.parseFunction(FUNCTIONS.ARROW_EXPRESSION); - - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe("'value'"); + const variable = parser.parseVariable(FUNCTIONS.ARROW_EXPRESSION); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESArrowFunction); + + const funktion = variable.initializer as ESArrowFunction; + expect(funktion.identifier).toBeUndefined(); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual("'value'"); }); it('should parse an arrow function argument declaration', () => { - const funktion = parser.parseFunction(FUNCTIONS.ARROW_ARGUMENT); - - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.parameters.length).toBe(1); - expect(funktion.body).toBe('arg'); + const variable = parser.parseVariable(FUNCTIONS.ARROW_ARGUMENT); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESArrowFunction); + + const funktion = variable.initializer as ESArrowFunction; + expect(funktion.identifier).toBeUndefined(); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.parameters).toHaveLength(1); + expect(funktion.body.toString()).toEqual('arg'); }); it('should parse an async arrow function declaration', () => { - const funktion = parser.parseFunction(FUNCTIONS.ASYNC_ARROW); - - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(true); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + const variable = parser.parseVariable(FUNCTIONS.ASYNC_ARROW); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESArrowFunction); + + const funktion = variable.initializer as ESArrowFunction; + expect(funktion.identifier).toBeUndefined(); + expect(funktion.isAsync).toBeTruthy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse a generator function declaration', () => { const funktion = parser.parseFunction(FUNCTIONS.GENERATOR); - - expect(funktion).toBeInstanceOf(ESGenerator); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + expect(funktion).toBeInstanceOf(ESGeneratorFunction); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse an async generator function declaration', () => { const funktion = parser.parseFunction(FUNCTIONS.ASYNC_GENERATOR); - - expect(funktion).toBeInstanceOf(ESGenerator); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(true); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + expect(funktion).toBeInstanceOf(ESGeneratorFunction); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeTruthy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse an expression generator function declaration', () => { - const funktion = parser.parseFunction(FUNCTIONS.EXPRESSION_GENERATOR); - - expect(funktion).toBeInstanceOf(ESGenerator); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + const variable = parser.parseVariable(FUNCTIONS.EXPRESSION_GENERATOR); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESGeneratorFunction); + + const funktion = variable.initializer as ESGeneratorFunction; + expect(funktion.identifier).toBeUndefined(); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse an async expression generator function declaration', () => { - const funktion = parser.parseFunction(FUNCTIONS.ASYNC_EXPRESSION_GENERATOR); - - expect(funktion).toBeInstanceOf(ESGenerator); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(true); - expect(funktion.parameters.length).toBe(0); - expect(funktion.body).toBe('{ }'); + const variable = parser.parseVariable(FUNCTIONS.ASYNC_EXPRESSION_GENERATOR); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESGeneratorFunction); + + const funktion = variable.initializer as ESGeneratorFunction; + expect(funktion.identifier).toBeUndefined(); + expect(funktion.isAsync).toBeTruthy(); + expect(funktion.parameters).toHaveLength(0); + expect(funktion.body.toString()).toEqual('{}'); }); it('should parse a function with parameters', () => { const funktion = parser.parseFunction(FUNCTIONS.PARAMETERS); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.body).toBe('{ }'); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.body.toString()).toEqual('{}'); const parameters = funktion.parameters; - expect(parameters.length).toBe(2); + expect(parameters).toHaveLength(2); - const first = parameters[0] as ESField; - expect(first).toBeInstanceOf(ESField); - expect(first.name).toBe('param1'); - expect(first.value).toBe(undefined); + const first = parameters[0]; + expect(first.binding).toBeInstanceOf(ESIdentifierBinding); + expect(first.binding.toString()).toEqual('param1'); + expect(first.initializer).toBeUndefined(); - const second = parameters[1] as ESField; - expect(second).toBeInstanceOf(ESField); - expect(second.name).toBe('param2'); - expect(second.value).toBe(undefined); + const second = parameters[1]; + expect(second.binding).toBeInstanceOf(ESIdentifierBinding); + expect(second.binding.toString()).toEqual('param2'); + expect(second.initializer).toBeUndefined(); }); it('should parse a function with default parameters', () => { const funktion = parser.parseFunction(FUNCTIONS.DEFAULT_PARAMETERS); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.body).toBe('{ }'); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.body.toString()).toEqual('{}'); const parameters = funktion.parameters; - expect(parameters.length).toBe(2); + expect(parameters).toHaveLength(2); - const first = parameters[0] as ESField; - expect(first).toBeInstanceOf(ESField); - expect(first.name).toBe('param1'); - expect(first.value).toEqual(new ESExpression("'value1'")); + const first = parameters[0]; + expect(first.binding).toBeInstanceOf(ESIdentifierBinding); + expect(first.binding.toString()).toEqual('param1'); + expect(first.initializer).toBeInstanceOf(ESExpression); + expect(first.initializer?.toString(false)).toEqual("'value1'"); - const second = parameters[1] as ESField; - expect(second).toBeInstanceOf(ESField); - expect(second.name).toBe('param2'); - expect(second.value).toEqual(new ESExpression("true")); + const second = parameters[1]; + expect(second.binding).toBeInstanceOf(ESIdentifierBinding); + expect(second.binding.toString()).toEqual('param2'); + expect(second.initializer).toBeInstanceOf(ESExpression); + expect(second.initializer?.toString(false)).toEqual('true'); }); it('should parse a function with a rest parameter', () => { const funktion = parser.parseFunction(FUNCTIONS.REST_PARAMETERS); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.body).toBe('{ }'); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.body.toString()).toEqual('{}'); const parameters = funktion.parameters; - expect(parameters.length).toBe(1); + expect(parameters).toHaveLength(1); - const first = parameters[0] as ESField; - expect(first).toBeInstanceOf(ESField); - expect(first.name).toBe('...param1'); - expect(first.value).toEqual(undefined); + const first = parameters[0]; + expect(first.binding).toBeInstanceOf(ESIdentifierBinding); + expect(first.binding.toString()).toEqual('...param1'); + expect(first.initializer).toBeUndefined(); }); it('should parse a function with destructuring parameters', () => { const funktion = parser.parseFunction(FUNCTIONS.DESTRUCTURING_PARAMETERS); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.body).toBe('{ }'); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.body.toString()).toEqual('{}'); const parameters = funktion.parameters; - expect(parameters.length).toBe(2); + expect(parameters).toHaveLength(2); + + const firstParameter = parameters[0]; + expect(firstParameter.binding).toBeInstanceOf(ESObjectBinding); + expect(firstParameter.initializer).toBeUndefined(); + + const firstBinding = firstParameter.binding as ESObjectBinding; + expect(firstBinding.elements).toHaveLength(2); + expect(firstBinding.toString()).toEqual('{param1,param2}'); - const firstParameter = parameters[0] as ESDestructuredObject; - expect(firstParameter).toBeInstanceOf(ESDestructuredObject); - expect(firstParameter.members.length).toBe(2); + const firstMember = firstBinding.elements[0]; + expect(firstMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(firstMember.binding.toString()).toEqual('param1'); + expect(firstMember.initializer).toBeUndefined(); - const firstMember = firstParameter.members[0] as ESField; - expect(firstMember).toBeInstanceOf(ESField); - expect(firstMember.name).toBe('param1'); - expect(firstMember.value).toBe(undefined); + const secondMember = firstBinding.elements[1]; + expect(secondMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(secondMember.binding.toString()).toEqual('param2'); + expect(secondMember.initializer).toBeUndefined(); - const secondMember = firstParameter.members[1] as ESField; - expect(secondMember).toBeInstanceOf(ESField); - expect(secondMember.name).toBe('param2'); - expect(secondMember.value).toBe(undefined); + const secondParameter = parameters[1]; + expect(secondParameter.binding).toBeInstanceOf(ESArrayBinding); + expect(secondParameter.initializer).toBeUndefined(); - const secondParameter = parameters[1] as ESDestructuredArray; - expect(secondParameter).toBeInstanceOf(ESDestructuredArray); - expect(secondParameter.members.length).toBe(2); + const secondBinding = secondParameter.binding as ESArrayBinding; + expect(secondBinding.elements).toHaveLength(2); + expect(secondBinding.toString()).toEqual('[param3,param4]'); - const thirdMember = secondParameter.members[0] as ESField; - expect(thirdMember).toBeInstanceOf(ESField); - expect(thirdMember.name).toBe('param3'); - expect(thirdMember.value).toBe(undefined); + const thirdMember = secondBinding.elements[0]; + expect(thirdMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(thirdMember.binding.toString()).toEqual('param3'); + expect(thirdMember.initializer).toBeUndefined(); - const fourthMember = secondParameter.members[1] as ESField; - expect(fourthMember).toBeInstanceOf(ESField); - expect(fourthMember.name).toBe('param4'); - expect(fourthMember.value).toBe(undefined); + const fourthMember = secondBinding.elements[1]; + expect(fourthMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(fourthMember.binding.toString()).toEqual('param4'); + expect(fourthMember.initializer).toBeUndefined(); }); it('should parse a function with destructuring default parameters', () => { const funktion = parser.parseFunction(FUNCTIONS.DESTRUCTURING_DEFAULT_PARAMETERS); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.body).toBe('{ }'); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.body.toString()).toEqual('{}'); const parameters = funktion.parameters; - expect(parameters.length).toBe(2); + expect(parameters).toHaveLength(2); - const firstParameter = parameters[0] as ESDestructuredObject; - expect(firstParameter).toBeInstanceOf(ESDestructuredObject); - expect(firstParameter.members.length).toBe(2); - - const firstMember = firstParameter.members[0] as ESField; - expect(firstMember).toBeInstanceOf(ESField); - expect(firstMember.name).toBe('param1'); - expect(firstMember.value).toBeInstanceOf(ESExpression); - - const secondMember = firstParameter.members[1] as ESField; - expect(secondMember).toBeInstanceOf(ESField); - expect(secondMember.name).toBe('param2'); - expect(secondMember.value).toBeInstanceOf(ESExpression); - - const secondParameter = parameters[1] as ESDestructuredArray; - expect(secondParameter).toBeInstanceOf(ESDestructuredArray); - expect(secondParameter.members.length).toBe(2); - - const thirdMember = secondParameter.members[0] as ESField; - expect(thirdMember).toBeInstanceOf(ESField); - expect(thirdMember.name).toBe('param3'); - expect(thirdMember.value).toBeInstanceOf(ESExpression); + const firstParameter = parameters[0]; + expect(firstParameter.binding).toBeInstanceOf(ESObjectBinding); + expect(firstParameter.initializer).toBeUndefined(); + + const firstBinding = firstParameter.binding as ESObjectBinding; + expect(firstBinding.elements).toHaveLength(2); + expect(firstBinding.toString()).toEqual("{param1='value1',param2=true}"); - const fourthMember = secondParameter.members[1] as ESField; - expect(fourthMember).toBeInstanceOf(ESField); - expect(fourthMember.name).toBe('param4'); - expect(fourthMember.value).toBeInstanceOf(ESExpression); + const firstMember = firstBinding.elements[0]; + expect(firstMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(firstMember.binding.toString()).toEqual('param1'); + expect(firstMember.initializer).toBeInstanceOf(ESExpression); + + const secondMember = firstBinding.elements[1]; + expect(secondMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(secondMember.binding.toString()).toEqual('param2'); + expect(secondMember.initializer).toBeInstanceOf(ESExpression); + + const secondParameter = parameters[1]; + expect(secondParameter.binding).toBeInstanceOf(ESArrayBinding); + expect(secondParameter.initializer).toBeUndefined(); + + const secondBinding = secondParameter.binding as ESArrayBinding; + expect(secondBinding.elements).toHaveLength(2); + expect(secondBinding.toString()).toEqual("[param3='value3',param4=true]"); + + const thirdMember = secondBinding.elements[0]; + expect(thirdMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(thirdMember.binding.toString()).toEqual('param3'); + expect(thirdMember.initializer).toBeInstanceOf(ESExpression); + + const fourthMember = secondBinding.elements[1]; + expect(fourthMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(fourthMember.binding.toString()).toEqual('param4'); + expect(fourthMember.initializer).toBeInstanceOf(ESExpression); }); it('should parse a function with destructuring rest parameters', () => { const funktion = parser.parseFunction(FUNCTIONS.DESTRUCTURING_REST_PARAMETERS); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.body).toBe('{ }'); + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.body.toString()).toEqual('{}'); const parameters = funktion.parameters; - expect(parameters.length).toBe(2); + expect(parameters).toHaveLength(2); - const firstParameter = parameters[0] as ESDestructuredObject; - expect(firstParameter).toBeInstanceOf(ESDestructuredObject); - expect(firstParameter.members.length).toBe(2); - - const firstMember = firstParameter.members[0] as ESField; - expect(firstMember).toBeInstanceOf(ESField); - expect(firstMember.name).toBe('param1'); - expect(firstMember.value).toBe(undefined); - - const secondMember = firstParameter.members[1] as ESField; - expect(secondMember).toBeInstanceOf(ESField); - expect(secondMember.name).toBe('param2'); - expect(secondMember.value).toBe(undefined); - - const secondParameter = parameters[1] as ESDestructuredArray; - expect(secondParameter).toBeInstanceOf(ESDestructuredArray); - expect(secondParameter.members.length).toBe(2); - - const thirdMember = secondParameter.members[0] as ESField; - expect(thirdMember).toBeInstanceOf(ESField); - expect(thirdMember.name).toBe('param3'); - expect(thirdMember.value).toBe(undefined); + const firstParameter = parameters[0]; + expect(firstParameter.binding).toBeInstanceOf(ESObjectBinding); + expect(firstParameter.initializer).toBeUndefined(); - const fourthMember = secondParameter.members[1] as ESField; - expect(fourthMember).toBeInstanceOf(ESField); - expect(fourthMember.name).toBe('...param4'); - expect(fourthMember.value).toBe(undefined); + const firstBinding = firstParameter.binding as ESObjectBinding; + expect(firstBinding.elements).toHaveLength(2); + expect(firstBinding.toString()).toEqual('{param1,param2}'); + + const firstMember = firstBinding.elements[0]; + expect(firstMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(firstMember.binding.toString()).toEqual('param1'); + expect(firstMember.initializer).toBeUndefined(); + + const secondMember = firstBinding.elements[1]; + expect(secondMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(secondMember.binding.toString()).toEqual('param2'); + expect(secondMember.initializer).toBeUndefined(); + + const secondParameter = parameters[1]; + expect(secondParameter.binding).toBeInstanceOf(ESArrayBinding); + expect(secondParameter.initializer).toBeUndefined(); + + const secondBinding = secondParameter.binding as ESArrayBinding; + expect(secondBinding.elements).toHaveLength(2); + expect(secondBinding.toString()).toEqual('[param3,...param4]'); + + const thirdMember = secondBinding.elements[0]; + expect(thirdMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(thirdMember.binding.toString()).toEqual('param3'); + expect(thirdMember.initializer).toBeUndefined(); + + const fourthMember = secondBinding.elements[1]; + expect(fourthMember.binding).toBeInstanceOf(ESIdentifierBinding); + expect(fourthMember.binding.toString()).toEqual('...param4'); + expect(fourthMember.initializer).toBeUndefined(); }); it('should parse a simple function body', () => { const funktion = parser.parseFunction(FUNCTIONS.SIMPLE_BODY); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.body).toBe("{ return 'value' ; }"); + + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.body.toString()).toEqual("{return 'value';}"); const parameters = funktion.parameters; - expect(parameters.length).toBe(0); + expect(parameters).toHaveLength(0); }); it('should parse a block function body', () => { const funktion = parser.parseFunction(FUNCTIONS.BLOCK_BODY); - expect(funktion.name).toBe('name'); - expect(funktion.isAsync).toBe(false); - expect(funktion.body).toBe("{ if ( true ) { return 'value' ; } }"); + + expect(funktion.identifier).toEqual('name'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.body.toString()).toEqual("{if(true){return 'value';}}"); const parameters = funktion.parameters; - expect(parameters.length).toBe(0); + expect(parameters).toHaveLength(0); }); it('should parse function with a non reserved keyword as name', () => { const funktion = parser.parseFunction(FUNCTIONS.KEYWORD_AS_NAME); - expect(funktion.name).toBe('as'); - expect(funktion.isAsync).toBe(false); - expect(funktion.body).toBe("{ }"); + + expect(funktion.identifier).toEqual('as'); + expect(funktion.isAsync).toBeFalsy(); + expect(funktion.body.toString()).toEqual("{}"); const parameters = funktion.parameters; - expect(parameters.length).toBe(0); + expect(parameters).toHaveLength(0); }); }); - describe('.parseClass(code)', () => + describe('Classes', () => { it('should parse a simple class declaration', () => { const clazz = parser.parseClass(CLASSES.DECLARATION); - expect(clazz.name).toBe('Name'); - expect(clazz.parentName).toBe(undefined); + expect(clazz.identifier).toEqual('Name'); + expect(clazz.parent).toBeUndefined(); const members = clazz.members; - expect(members.length).toBe(0); + expect(members).toHaveLength(0); }); it('should parse a simple class declaration with parent class', () => { const clazz = parser.parseClass(CLASSES.EXTENDS); - expect(clazz.name).toBe('Name'); - expect(clazz.parentName).toBe('Parent'); + expect(clazz.identifier).toEqual('Name'); + expect(clazz.parent).toEqual('Parent'); const members = clazz.members; - expect(members.length).toBe(0); + expect(members).toHaveLength(0); }); it('should parse an expression class declaration', () => { - const clazz = parser.parseClass(CLASSES.EXPRESSION); - expect(clazz.name).toBe('name'); - expect(clazz.parentName).toBe(undefined); + const variable = parser.parseVariable(CLASSES.EXPRESSION); + expect(variable.identifier).toEqual('name'); + expect(variable.binding).toBeInstanceOf(ESIdentifierBinding); + expect(variable.type).toEqual('const'); + expect(variable.initializer).toBeInstanceOf(ESClass); + + const clazz = variable.initializer as ESClass; + expect(clazz.identifier).toBeUndefined(); + expect(clazz.parent).toBeUndefined(); const members = clazz.members; - expect(members.length).toBe(0); + expect(members).toHaveLength(0); }); it('should parse class members', () => { const clazz = parser.parseClass(CLASSES.MEMBERS); - expect(clazz.name).toBe('Name'); - expect(clazz.parentName).toBe(undefined); + expect(clazz.identifier).toEqual('Name'); + expect(clazz.parent).toBeUndefined(); const members = clazz.members; - expect(members.length).toBe(21); + expect(members).toHaveLength(21); - const declarations = clazz.declarations; - expect(declarations.length).toBe(4); + const fields = clazz.fields; + expect(fields).toHaveLength(4); - expect(declarations[0].name).toBe('field1'); - expect(declarations[0].isPrivate).toBe(true); - expect(declarations[0].isStatic).toBe(false); - expect(declarations[0].value?.definition).toBe("'value1'"); + expect(fields[0].identifier).toEqual('field1'); + expect(fields[0].visibility).toEqual('private'); + expect(fields[0].location).toEqual('instance'); + expect(fields[0].initializer?.toString(false)).toEqual("'value1'"); - expect(declarations[1].name).toBe('field2'); - expect(declarations[1].isPrivate).toBe(false); - expect(declarations[1].isStatic).toBe(false); - expect(declarations[1].value).toBe(undefined); + expect(fields[1].identifier).toEqual('field2'); + expect(fields[1].visibility).toEqual('public'); + expect(fields[1].location).toEqual('instance'); + expect(fields[1].initializer).toBeUndefined(); - expect(declarations[2].name).toBe('field3'); - expect(declarations[2].isPrivate).toBe(true); - expect(declarations[2].isStatic).toBe(true); - expect(declarations[2].value?.definition).toBe('"value3"'); + expect(fields[2].identifier).toEqual('field3'); + expect(fields[2].visibility).toEqual('private'); + expect(fields[2].location).toEqual('static'); + expect(fields[2].initializer?.toString(false)).toEqual('"value3"'); - expect(declarations[3].name).toBe('field4'); - expect(declarations[3].isPrivate).toBe(false); - expect(declarations[3].isStatic).toBe(true); - expect(declarations[3].value).toBe(undefined); + expect(fields[3].identifier).toEqual('field4'); + expect(fields[3].visibility).toEqual('public'); + expect(fields[3].location).toEqual('static'); + expect(fields[3].initializer).toBeUndefined(); const getters = clazz.getters; - expect(getters.length).toBe(4); + expect(getters).toHaveLength(4); - expect(getters[0].name).toBe('getter1'); - expect(getters[0].isPrivate).toBe(true); - expect(getters[0].isStatic).toBe(false); + expect(getters[0].identifier).toEqual('getter1'); + expect(getters[0].visibility).toEqual('private'); + expect(getters[0].location).toEqual('instance'); + expect(getters[0].body.code).toEqual('{return this.#field1;}'); - expect(getters[1].name).toBe('getter2'); - expect(getters[1].isPrivate).toBe(false); - expect(getters[1].isStatic).toBe(false); + expect(getters[1].identifier).toEqual('getter2'); + expect(getters[1].visibility).toEqual('public'); + expect(getters[1].location).toEqual('instance'); + expect(getters[1].body.code).toEqual('{return this.field2;}'); - expect(getters[2].name).toBe('getter3'); - expect(getters[2].isPrivate).toBe(true); - expect(getters[2].isStatic).toBe(true); + expect(getters[2].identifier).toEqual('getter3'); + expect(getters[2].visibility).toEqual('private'); + expect(getters[2].location).toEqual('static'); + expect(getters[2].body.code).toEqual('{return this.#field3;}'); - expect(getters[3].name).toBe('getter4'); - expect(getters[3].isPrivate).toBe(false); - expect(getters[3].isStatic).toBe(true); + expect(getters[3].identifier).toEqual('getter4'); + expect(getters[3].visibility).toEqual('public'); + expect(getters[3].location).toEqual('static'); + expect(getters[3].body.code).toEqual('{return this.field4;}'); const setters = clazz.setters; - expect(setters.length).toBe(4); - - expect(setters[0].name).toBe('setter1'); - expect(setters[0].isPrivate).toBe(true); - expect(setters[0].isStatic).toBe(false); - - expect(setters[1].name).toBe('setter2'); - expect(setters[1].isPrivate).toBe(false); - expect(setters[1].isStatic).toBe(false); - - expect(setters[2].name).toBe('setter3'); - expect(setters[2].isPrivate).toBe(true); - expect(setters[2].isStatic).toBe(true); - - expect(setters[3].name).toBe('setter4'); - expect(setters[3].isPrivate).toBe(false); - expect(setters[3].isStatic).toBe(true); - - const functions = clazz.functions; - expect(functions.length).toBe(6); - - expect(functions[0].name).toBe('constructor'); - expect(functions[0].isPrivate).toBe(false); - expect(functions[0].isStatic).toBe(false); - expect(functions[0].isAsync).toBe(false); - - expect(functions[1].name).toBe('method1'); - expect(functions[1].isPrivate).toBe(false); - expect(functions[1].isStatic).toBe(false); - expect(functions[1].isAsync).toBe(false); - - expect(functions[2].name).toBe('method2'); - expect(functions[2].isPrivate).toBe(false); - expect(functions[2].isStatic).toBe(false); - expect(functions[2].isAsync).toBe(true); - - expect(functions[3].name).toBe('method3'); - expect(functions[3].isPrivate).toBe(false); - expect(functions[3].isStatic).toBe(true); - expect(functions[3].isAsync).toBe(false); - - expect(functions[4].name).toBe('method4'); - expect(functions[4].isPrivate).toBe(false); - expect(functions[4].isStatic).toBe(true); - expect(functions[4].isAsync).toBe(true); - - expect(functions[5].name).toBe('method5'); - expect(functions[5].isPrivate).toBe(true); - expect(functions[5].isStatic).toBe(false); - expect(functions[5].isAsync).toBe(false); - - const generators = clazz.generators; - expect(generators.length).toBe(3); - - expect(generators[0].name).toBe('generator1'); - expect(generators[0].isPrivate).toBe(false); - expect(generators[0].isStatic).toBe(false); - expect(generators[0].isAsync).toBe(false); - - expect(generators[1].name).toBe('generator2'); - expect(generators[1].isPrivate).toBe(false); - expect(generators[1].isStatic).toBe(false); - expect(generators[1].isAsync).toBe(true); - - expect(generators[2].name).toBe('generator3'); - expect(generators[2].isPrivate).toBe(false); - expect(generators[2].isStatic).toBe(true); - expect(generators[2].isAsync).toBe(true); + expect(setters).toHaveLength(4); + + expect(setters[0].identifier).toEqual('setter1'); + expect(setters[0].visibility).toEqual('private'); + expect(setters[0].location).toEqual('instance'); + expect(setters[0].parameter.binding.toString()).toEqual('value'); + expect(setters[0].body.code).toEqual('{this.#field1=value;}'); + + expect(setters[1].identifier).toEqual('setter2'); + expect(setters[1].visibility).toEqual('public'); + expect(setters[1].location).toEqual('instance'); + expect(setters[1].parameter.binding.toString()).toEqual('value'); + expect(setters[1].body.code).toEqual('{this.field2=value;}'); + + expect(setters[2].identifier).toEqual('setter3'); + expect(setters[2].visibility).toEqual('private'); + expect(setters[2].location).toEqual('static'); + expect(setters[2].parameter.binding.toString()).toEqual('value'); + expect(setters[2].body.code).toEqual('{this.#field3=value;}'); + + expect(setters[3].identifier).toEqual('setter4'); + expect(setters[3].visibility).toEqual('public'); + expect(setters[3].location).toEqual('static'); + expect(setters[3].parameter.binding.toString()).toEqual('value'); + expect(setters[3].body.code).toEqual('{this.field4=value;}'); + + const construct = clazz.construct as ESConstructor; + expect(construct).toBeDefined(); + expect(construct.identifier).toEqual('constructor'); + expect(construct.visibility).toEqual('public'); + expect(construct.location).toEqual('instance'); + expect(construct.parameters).toHaveLength(2); + expect(construct.parameters[0].toString()).toEqual('field1'); + expect(construct.parameters[1].toString()).toEqual('...field2'); + expect(construct.body.toString()).toEqual('{this.#field1=field1;this.field2=field2;}'); + + const methods = clazz.methods; + expect(methods).toHaveLength(8); + + expect(methods[0].identifier).toEqual('method1'); + expect(methods[0].visibility).toEqual('public'); + expect(methods[0].location).toEqual('instance'); + expect(methods[0].isAsync).toBeFalsy(); + expect(methods[0].parameters).toHaveLength(0); + expect(methods[0].body.toString()).toEqual('{return this.#field1;}'); + + expect(methods[1].identifier).toEqual('method2'); + expect(methods[1].visibility).toEqual('public'); + expect(methods[1].location).toEqual('instance'); + expect(methods[1].isAsync).toBeTruthy(); + expect(methods[1].parameters).toHaveLength(0); + expect(methods[1].body.toString()).toEqual('{return this.field2;}'); + + expect(methods[2].identifier).toEqual('method3'); + expect(methods[2].visibility).toEqual('public'); + expect(methods[2].location).toEqual('static'); + expect(methods[2].isAsync).toBeFalsy(); + expect(methods[2].parameters).toHaveLength(0); + expect(methods[2].body.toString()).toEqual('{return this.#field3;}'); + + expect(methods[3].identifier).toEqual('method4'); + expect(methods[3].visibility).toEqual('public'); + expect(methods[3].location).toEqual('static'); + expect(methods[3].isAsync).toBeTruthy(); + expect(methods[3].parameters).toHaveLength(0); + expect(methods[3].body.toString()).toEqual('{return this.field4;}'); + + expect(methods[4].identifier).toEqual('method5'); + expect(methods[4].visibility).toEqual('private'); + expect(methods[4].location).toEqual('instance'); + expect(methods[4].isAsync).toBeFalsy(); + expect(methods[4].parameters).toHaveLength(2); + expect(methods[4].parameters[0].toString()).toEqual('a'); + expect(methods[4].parameters[1].toString()).toEqual('b'); + expect(methods[4].body.toString()).toEqual('{return a+b;}'); + + expect(methods[5]).toBeInstanceOf(ESGeneratorMethod); + expect(methods[5].identifier).toEqual('generator1'); + expect(methods[5].visibility).toEqual('public'); + expect(methods[5].location).toEqual('instance'); + expect(methods[5].isAsync).toBeFalsy(); + expect(methods[5].parameters).toHaveLength(0); + expect(methods[5].body.toString()).toEqual('{yield 1;}'); + + expect(methods[6]).toBeInstanceOf(ESGeneratorMethod); + expect(methods[6].identifier).toEqual('generator2'); + expect(methods[6].visibility).toEqual('public'); + expect(methods[6].location).toEqual('instance'); + expect(methods[6].isAsync).toBeTruthy(); + expect(methods[6].parameters).toHaveLength(0); + expect(methods[6].body.toString()).toEqual('{yield 1;}'); + + expect(methods[7]).toBeInstanceOf(ESGeneratorMethod); + expect(methods[7].identifier).toEqual('generator3'); + expect(methods[7].visibility).toEqual('public'); + expect(methods[7].location).toEqual('static'); + expect(methods[7].isAsync).toBeTruthy(); + expect(methods[7].parameters).toHaveLength(0); + expect(methods[7].body.toString()).toEqual('{yield 1;}'); }); }); - describe('.parse(code)', () => + describe('Modules', () => { it('should parse an empty module', () => { const module = parser.parse(''); - - const members = module.members; - expect(members.length).toBe(0); - }); - - it('should parse omit root level expressions', () => - { - const module = parser.parse(VALUES.EXPRESSION); - - const members = module.members; - expect(members.length).toBe(0); + expect(module.statements).toHaveLength(0); }); it('should parse a module with terminated statements', () => { const module = parser.parse(MODULES.TERMINATED); - - const members = module.members; - expect(members.length).toBe(14); - - const imports = module.imports; - expect(imports.length).toBe(2); - - const exports = module.exports; - expect(exports.length).toBe(4); - - const declarations = module.declarations; - expect(declarations.length).toBe(4); - - const functions = module.functions; - expect(functions.length).toBe(3); - - const classes = module.classes; - expect(classes.length).toBe(1); + expect(module.statements).toHaveLength(16); + expect(module.imports).toHaveLength(2); + expect(module.exports).toHaveLength(4); + expect(module.expressions).toHaveLength(2); + expect(module.variables).toHaveLength(5); + expect(module.functions).toHaveLength(2); + expect(module.classes).toHaveLength(1); + expect(module.toString()).toEqual(MODULES_STRINGS.TERMINATED); }); it('should parse a module with unterminated statements', () => { const module = parser.parse(MODULES.UNTERMINATED); - - const members = module.members; - expect(members.length).toBe(9); - - const imports = module.imports; - expect(imports.length).toBe(2); - - const exports = module.exports; - expect(exports.length).toBe(3); - - const declarations = module.declarations; - expect(declarations.length).toBe(2); - - const functions = module.functions; - expect(functions.length).toBe(1); - - const classes = module.classes; - expect(classes.length).toBe(1); + expect(module.statements).toHaveLength(10); + expect(module.imports).toHaveLength(2); + expect(module.exports).toHaveLength(3); + expect(module.expressions).toHaveLength(1); + expect(module.variables).toHaveLength(2); + expect(module.functions).toHaveLength(1); + expect(module.classes).toHaveLength(1); + // NOTE: Unterminated code is read only as it can not be outputted to working code }); }); }); diff --git a/packages/analysis/test/static/fixtures/classes.fixture.ts b/packages/analysis/test/static/fixtures/classes.fixture.ts index 89cd3421..7c5c1f49 100644 --- a/packages/analysis/test/static/fixtures/classes.fixture.ts +++ b/packages/analysis/test/static/fixtures/classes.fixture.ts @@ -18,31 +18,31 @@ export const CLASSES = this.field2 = field2; } - get #getter1() { return field1; } + get #getter1() { return this.#field1; } - get getter2() { return field2; } + get getter2() { return this.field2; } - static get #getter3() { return field3; } + static get #getter3() { return this.#field3; } - static get getter4() { return field4; } + static get getter4() { return this.field4; } set #setter1(value) { this.#field1 = value; } - set setter2(value) { this.#field2 = value; } + set setter2(value) { this.field2 = value; } static set #setter3(value) { this.#field3 = value; } - static set setter4(value) { this.#field4 = value; } + static set setter4(value) { this.field4 = value; } method1() { return this.#field1; } - async method2() { return this.#field1; } + async method2() { return this.field2; } - static method3() { return this.#field1; } + static method3() { return this.#field3; } - static async method4() { return this.#field1; } + static async method4() { return this.field4; } - #method5() { return this.#field1; } + #method5(a, b) { return a + b; } *generator1() { yield 1; } diff --git a/packages/analysis/test/static/fixtures/code.fixture.ts b/packages/analysis/test/static/fixtures/code.fixture.ts index 7b83c6f1..7f2cbc06 100644 --- a/packages/analysis/test/static/fixtures/code.fixture.ts +++ b/packages/analysis/test/static/fixtures/code.fixture.ts @@ -1,7 +1,9 @@ export const CODE = { + VALUES: '42 hello "world" true false undefined null', OPERATORS: `=====!=+=*!=`, + NUMBERS: '-12-10 12_345_678.90 0x124_a4Bc 0b11_0110', LITERALS: '`foo\\`ter`"bar\\"becue"\'baz\'', KEYWORDS_IDENTIFIERS: 'class Foo function bar', WHITESPACE: `const identifier\n=\t"value" ;`, diff --git a/packages/analysis/test/static/fixtures/exports.fixture.ts b/packages/analysis/test/static/fixtures/exports.fixture.ts index ccd5de02..8287a676 100644 --- a/packages/analysis/test/static/fixtures/exports.fixture.ts +++ b/packages/analysis/test/static/fixtures/exports.fixture.ts @@ -10,6 +10,11 @@ export const EXPORTS = EXPORT_FIELD_DECLARATION: "export const name = 'value'", EXPORT_FUNCTION_DECLARATION: "export function name() {}", EXPORT_ASYNC_FUNCTION_DECLARATION: "export async function name() {}", + EXPORT_DEFAULT_VALUE: "export default 42", + EXPORT_DEFAULT_INSTANCE: "export default new Date()", + EXPORT_DEFAULT_CALL: "export default name()", + EXPORT_DEFAULT_FUNCTION: "export default function() {}", + EXPORT_DEFAULT_CLASS: "export default class {}", REEXPORT_ALL: "export * from 'module'", - REEXPORT_MEMBER: "export { member } from 'module'", + REEXPORT_MEMBER: "export { member } from 'module'" }; diff --git a/packages/analysis/test/static/fixtures/modules.fixture.ts b/packages/analysis/test/static/fixtures/modules.fixture.ts index 743f33a7..43c0e7ce 100644 --- a/packages/analysis/test/static/fixtures/modules.fixture.ts +++ b/packages/analysis/test/static/fixtures/modules.fixture.ts @@ -8,7 +8,7 @@ import { member as alias } from 'module2'; const name = 'Peter' + ' van ' + 'Vliet'; -export default function sum(a, b) { return a + b; } +export default function sum(a = 2, b = 3) { return a + b; } [1, 2, 3, 4, 5].sort((a, b) => a - b); @@ -39,31 +39,52 @@ export { name, peter }; `, UNTERMINATED: ` - import { member } from 'module' - import { member as alias } from 'module2' - - const name = 'Peter' + ' van ' + 'Vliet' - - export default function sum(a, b) { return a + b } - - [1, 2, 3, 4, 5].sort((a, b) => a - b) - - try { sum(1, 2) } catch (error) { console.error(error) } - - export class Person +import { member } from 'module' +import { member as alias } from 'module2' + +const name = 'Peter' + ' van ' + 'Vliet' + +export default function sum(a, b) { return a + b } + +[1, 2, 3, 4, 5].sort((a, b) => a - b) + +try { sum(1, 2) } catch (error) { console.error(error) } + +export class Person +{ + #name + #age + + constructor(name, age) { - #name - #age - - constructor(name, age) - { - this.#name = name - this.#age = age - } + this.#name = name + this.#age = age } - - const peter = new Person(name, 42) - - export { name, peter } +} + +const peter = new Person(name, 42) + +export { name, peter } ` }; + +export const MODULES_STRINGS = +{ + TERMINATED: +`import {member} from 'module'; +import {member as alias} from 'module2'; +const name='Peter'+' van '+'Vliet'; +export {sum as default}; +function sum(a=2,b=3){return a+b;} +[1,2,3,4,5].sort((a,b)=>a-b); +try{sum(1,2);}catch(error){console.error(error);} +export {Person}; +class Person{#name;#age;constructor(name,age) {this.#name=name;this.#age=age;}} +const peter=new Person(name,42); +async function async(){} +const a=async; +const b=async ()=>{}; +const as=12; +export {as as hi}; +export {name,peter};` +}; diff --git a/packages/analysis/tsconfig.json b/packages/analysis/tsconfig.json index 4ca2247d..db3953f7 100644 --- a/packages/analysis/tsconfig.json +++ b/packages/analysis/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/build/src/BuildHelper.ts b/packages/build/src/BuildHelper.ts new file mode 100644 index 00000000..0d9651fb --- /dev/null +++ b/packages/build/src/BuildHelper.ts @@ -0,0 +1,93 @@ + +import type { RuntimeConfiguration } from '@jitar/configuration'; +import { Files, LocalFileManager } from '@jitar/sourcing'; + +import { Application, ApplicationReader } from './source'; +import { LocalModuleGenerator, RemoteModuleGenerator } from './target'; +import { FileHelper } from './utils'; + +import ApplicationNotRead from './errors/ApplicationNotRead'; +import ApplicationModuleNotFound from './errors/ApplicationModuleNotFound'; + +import ProjectFileManager from './ProjectFileManager'; + +export default class BuildHelper +{ + readonly #fileHelper: FileHelper = new FileHelper(); + readonly #fileManager: ProjectFileManager; + readonly #applicationReader: ApplicationReader; + + #application?: Application; + + constructor(configuration: RuntimeConfiguration) + { + const source = this.#fileHelper.makePathAbsolute(configuration.source, configuration.meta.root); + const target = this.#fileHelper.makePathAbsolute(configuration.target, configuration.meta.root); + const resources = this.#fileHelper.makePathAbsolute(configuration.resources, configuration.meta.root); + const segments = this.#fileHelper.makePathAbsolute(configuration.segments, configuration.meta.root); + + const sourceFileManager = new LocalFileManager(source); + const targetFileManager = new LocalFileManager(target); + const resourceFileManager = new LocalFileManager(resources); + const segmentFileManager = new LocalFileManager(segments); + + this.#fileManager = new ProjectFileManager(sourceFileManager, targetFileManager, resourceFileManager, segmentFileManager); + + this.#applicationReader = new ApplicationReader(this.#fileManager); + } + + async readApplication(): Promise + { + const sourceFileManager = this.#fileManager.source; + const resourceFileManager = this.#fileManager.resource; + const segmentFileManager = this.#fileManager.segment; + + const moduleFiles = await sourceFileManager.filter(Files.MODULE_PATTERN); + const resourceFiles = await resourceFileManager.filter(Files.RESOURCE_PATTERN); + const segmentFiles = await segmentFileManager.filter(Files.SEGMENT_PATTERN); + + this.#application = await this.#applicationReader.read(moduleFiles, resourceFiles, segmentFiles); + } + + generateModuleCode(filename: string, segmentNames: string[] = []): string + { + if (this.#application === undefined) + { + throw new ApplicationNotRead(); + } + + const repository = this.#application.repository; + + const module = repository.get(filename); + + if (module === undefined) + { + throw new ApplicationModuleNotFound(filename); + } + + const resources = this.#application.resources; + const segmentation = this.#application.segmentation; + + const segments = segmentation.getSegments(filename); + + if (segments.length === 0) + { + const generator = new LocalModuleGenerator(module, resources, segmentation); + + return generator.generate(); + } + + const segment = segments.find(segment => segmentNames.includes(segment.name)); + + if (segment === undefined) + { + const generator = new RemoteModuleGenerator(module, segments); + + return generator.generate(); + } + + const generator = new LocalModuleGenerator(module, resources, segmentation); + + return generator.generate(); + } +} diff --git a/packages/build/src/BuildManager.ts b/packages/build/src/BuildManager.ts index edfdc677..067902d9 100644 --- a/packages/build/src/BuildManager.ts +++ b/packages/build/src/BuildManager.ts @@ -5,11 +5,14 @@ import { Files, LocalFileManager } from '@jitar/sourcing'; import { ApplicationReader } from './source'; import { ApplicationBuilder } from './target'; +import { FileHelper } from './utils'; + import ProjectFileManager from './ProjectFileManager'; export default class BuildManager { readonly #logger: Logger; + readonly #fileHelper: FileHelper; readonly #fileManager: ProjectFileManager; @@ -19,11 +22,17 @@ export default class BuildManager constructor(configuration: RuntimeConfiguration, logLevel?: LogLevel) { this.#logger = new Logger(logLevel); + this.#fileHelper = new FileHelper(); + + const source = this.#fileHelper.makePathAbsolute(configuration.source, configuration.meta.root); + const target = this.#fileHelper.makePathAbsolute(configuration.target, configuration.meta.root); + const resources = this.#fileHelper.makePathAbsolute(configuration.resources, configuration.meta.root); + const segments = this.#fileHelper.makePathAbsolute(configuration.segments, configuration.meta.root); - const sourceFileManager = new LocalFileManager(configuration.source); - const targetFileManager = new LocalFileManager(configuration.target); - const resourceFileManager = new LocalFileManager(configuration.resources); - const segmentFileManager = new LocalFileManager(configuration.segments); + const sourceFileManager = new LocalFileManager(source); + const targetFileManager = new LocalFileManager(target); + const resourceFileManager = new LocalFileManager(resources); + const segmentFileManager = new LocalFileManager(segments); this.#fileManager = new ProjectFileManager(sourceFileManager, targetFileManager, resourceFileManager, segmentFileManager); diff --git a/packages/build/src/definitions/Patterns.ts b/packages/build/src/definitions/Patterns.ts deleted file mode 100644 index c984095e..00000000 --- a/packages/build/src/definitions/Patterns.ts +++ /dev/null @@ -1,6 +0,0 @@ - -export const Patterns = -{ - IMPORT: /import\s(?:["'\s]*([\w*{}\n, ]+)from\s*)?["'\s]*([@\w/._-]+)["'\s].*/g, - EXPORT: /export\s(?:["'\s]*([\w*{}\n, ]+)from\s*)?["'\s]*([@\w/._-]+)["'\s].*/g -} as const; diff --git a/packages/build/src/definitions/index.ts b/packages/build/src/definitions/index.ts index d3364cb1..370cd076 100644 --- a/packages/build/src/definitions/index.ts +++ b/packages/build/src/definitions/index.ts @@ -2,5 +2,4 @@ export * from './Defaults'; export * from './Files'; export * from './Keywords'; -export * from './Patterns'; export * from './Values'; diff --git a/packages/build/src/errors/ApplicationModuleNotFound.ts b/packages/build/src/errors/ApplicationModuleNotFound.ts new file mode 100644 index 00000000..65a6d3bd --- /dev/null +++ b/packages/build/src/errors/ApplicationModuleNotFound.ts @@ -0,0 +1,8 @@ + +export default class ApplicationModuleNotFound extends Error +{ + constructor(filename: string) + { + super(`Application module not found '${filename}'`); + } +} diff --git a/packages/build/src/errors/ApplicationNotRead.ts b/packages/build/src/errors/ApplicationNotRead.ts new file mode 100644 index 00000000..ce08caea --- /dev/null +++ b/packages/build/src/errors/ApplicationNotRead.ts @@ -0,0 +1,8 @@ + +export default class ApplicationNotRead extends Error +{ + constructor() + { + super('Application not read'); + } +} diff --git a/packages/build/src/index.ts b/packages/build/src/index.ts index 09fd95ee..c3ced94a 100644 --- a/packages/build/src/index.ts +++ b/packages/build/src/index.ts @@ -1,2 +1,3 @@ +export { default as BuildHelper } from './BuildHelper'; export { default as BuildManager } from './BuildManager'; diff --git a/packages/build/src/source/module/LocationRewriter.ts b/packages/build/src/source/module/LocationRewriter.ts index 8233f133..39c5d3ff 100644 --- a/packages/build/src/source/module/LocationRewriter.ts +++ b/packages/build/src/source/module/LocationRewriter.ts @@ -1,8 +1,8 @@ -import { Parser } from '@jitar/analysis'; +import type { ESModule, ESImport, ESExport } from '@jitar/analysis'; import type { FileManager } from '@jitar/sourcing'; -import { Files, Patterns } from '../../definitions'; +import { Files } from '../../definitions'; import { FileHelper } from '../../utils'; // The location rewriter ensures the '.js' for all application imports and re-exports @@ -12,7 +12,6 @@ export default class LocationRewriter { readonly #sourceFileManager: FileManager; - readonly #parser = new Parser(); readonly #fileHelper = new FileHelper(); constructor(sourceFileManager: FileManager) @@ -20,69 +19,45 @@ export default class LocationRewriter this.#sourceFileManager = sourceFileManager; } - rewrite(filename: string, code: string): string + rewrite(module: ESModule, filename: string): void { - const replacedImports = this.#rewriteImports(filename, code); - - return this.#rewriteExports(filename, replacedImports); + module.imports.forEach(item => this.#rewriteImport(item, filename)); + module.exports.forEach(item => this.#rewriteExport(item, filename)); } - #rewriteImports(filename: string, code: string): string + #rewriteImport(item: ESImport, filename: string): void { - const replacer = (statement: string) => this.#replaceImport(filename, statement); - - return code.replaceAll(Patterns.IMPORT, replacer); - } - - #rewriteExports(filename: string, code: string): string - { - const replacer = (statement: string) => this.#replaceExport(filename, statement); - - return code.replaceAll(Patterns.EXPORT, replacer); - } - - #replaceImport(filename: string, statement: string): string - { - const dependency = this.#parser.parseImport(statement); - const from = this.#fileHelper.stripPath(dependency.from); - const normalizedFrom = this.#sourceFileManager.normalizeLocation(from); + const normalizedFrom = this.#sourceFileManager.normalizeLocation(item.from); if (this.#fileHelper.isApplicationModule(normalizedFrom) === false) { - return statement; + return; } - const rewrittenFrom = this.#rewriteFrom(filename, normalizedFrom); - - return statement.replace(from, rewrittenFrom); + item.from = this.#rewriteFrom(filename, normalizedFrom); } - #replaceExport(filename: string, statement: string): string + #rewriteExport(item: ESExport, filename: string): void { - const dependency = this.#parser.parseExport(statement); - - if (dependency.from === undefined) + if (item.from === undefined) { - return statement; + return; } - const from = this.#fileHelper.stripPath(dependency.from); - const normalizedFrom = this.#sourceFileManager.normalizeLocation(from); + const normalizedFrom = this.#sourceFileManager.normalizeLocation(item.from); if (this.#fileHelper.isApplicationModule(normalizedFrom) === false) { - return statement; + return; } - const rewrittenFrom = this.#rewriteFrom(filename, normalizedFrom); - - return statement.replace(from, rewrittenFrom); + item.from = this.#rewriteFrom(filename, normalizedFrom); } #rewriteFrom(filename: string, from: string): string { const callingModulePath = this.#fileHelper.extractPath(filename); - const translated = this.#fileHelper.makePathAbsolute(from, callingModulePath); + const translated = this.#fileHelper.makePathAbsolute(from, callingModulePath, ''); return this.#sourceFileManager.isDirectory(translated) ? `${from}/${Files.INDEX}` diff --git a/packages/build/src/source/module/Reader.ts b/packages/build/src/source/module/Reader.ts index 6bd85263..da9b7bdc 100644 --- a/packages/build/src/source/module/Reader.ts +++ b/packages/build/src/source/module/Reader.ts @@ -31,22 +31,16 @@ export default class Reader } async read(filename: string): Promise - { - const relativeLocation = this.#sourceFileManager.getRelativeLocation(filename); - const code = await this.#loadCode(filename); - const rewrittenCode = this.#locationRewriter.rewrite(relativeLocation, code); - const module = this.#parser.parse(rewrittenCode); - - return new Module(relativeLocation, rewrittenCode, module); - } - - async #loadCode(filename: string): Promise { try { - const content = await this.#sourceFileManager.getContent(filename); + const relativeLocation = this.#sourceFileManager.getRelativeLocation(filename); + const code = await this.#loadCode(filename); + const module = this.#parser.parse(code); - return content.toString(); + this.#locationRewriter.rewrite(module, relativeLocation); + + return new Module(relativeLocation, module); } catch (error: unknown) { @@ -55,4 +49,11 @@ export default class Reader throw new FileNotLoaded(filename, message); } } + + async #loadCode(filename: string): Promise + { + const content = await this.#sourceFileManager.getContent(filename); + + return content.toString(); + } } diff --git a/packages/build/src/source/module/models/Module.ts b/packages/build/src/source/module/models/Module.ts index 39de0281..1138a37d 100644 --- a/packages/build/src/source/module/models/Module.ts +++ b/packages/build/src/source/module/models/Module.ts @@ -4,19 +4,15 @@ import type { ESModule } from '@jitar/analysis'; export default class Module { readonly #filename: string; - readonly #code: string; readonly #model: ESModule; - constructor(filename: string, code: string, model: ESModule) + constructor(filename: string, model: ESModule) { - this.#code = code; this.#filename = filename; this.#model = model; } get filename() { return this.#filename; } - get code() { return this.#code; } - get model() { return this.#model; } } diff --git a/packages/build/src/source/segment/MemberLocator.ts b/packages/build/src/source/segment/MemberLocator.ts index 0e5e07ac..a4d3b0ad 100644 --- a/packages/build/src/source/segment/MemberLocator.ts +++ b/packages/build/src/source/segment/MemberLocator.ts @@ -1,5 +1,5 @@ -import { ESMember } from '@jitar/analysis'; +import { ESDeclaration } from '@jitar/analysis'; import { FileHelper } from '../../utils'; import type { Module, ModuleRepository } from '../module'; @@ -12,7 +12,7 @@ import ImportInfo from './types/ImportInfo'; type TraceEntry = { filename: string, importKey: string }; type Trace = TraceEntry[]; -type LocatedMember = { trace: Trace, model: ESMember}; +type LocatedMember = { trace: Trace, model: ESDeclaration}; export default class MemberLocator { @@ -33,7 +33,7 @@ export default class MemberLocator return { trace, model }; } - #locate(filename: string, importKey: string, trace: Trace): ESMember + #locate(filename: string, importKey: string, trace: Trace): ESDeclaration { trace.push({ filename, importKey }); @@ -65,7 +65,7 @@ export default class MemberLocator || exportItem?.from !== undefined; } - #relocate(module: Module, importKey: string, trace: Trace): ESMember + #relocate(module: Module, importKey: string, trace: Trace): ESDeclaration { const relocateInfo = this.#getImportInfo(module, importKey) ?? this.#getExportInfo(module, importKey); @@ -74,13 +74,13 @@ export default class MemberLocator const relocateKey = relocateInfo?.name as string; const callingModulePath = this.#fileHelper.extractPath(module.filename); - const relativeFrom = this.#fileHelper.stripPath(relocatePath); - const absoluteFrom = this.#fileHelper.makePathAbsolute(relativeFrom, callingModulePath); + const relativeFrom = relocatePath; + const absoluteFrom = this.#fileHelper.makePathAbsolute(relativeFrom, callingModulePath, ''); return this.#locate(absoluteFrom, relocateKey, trace); } - #extract(module: Module, importKey: string): ESMember + #extract(module: Module, importKey: string): ESDeclaration { const exportInfo = this.#getExportInfo(module, importKey); @@ -89,7 +89,7 @@ export default class MemberLocator throw new MissingModuleExport(module.filename, importKey); } - const member = module.model.getMember(exportInfo.name); + const member = module.model.getDeclaration(exportInfo.name); if (member === undefined) { @@ -102,26 +102,26 @@ export default class MemberLocator #getExportInfo(module: Module, importKey: string): ExportInfo | undefined { const exportItem = module.model.getExport(importKey); - const exportAlias = exportItem?.getMember(importKey); + const exportMember = exportItem?.getMember(importKey); - if (exportAlias === undefined) + if (exportMember === undefined) { return undefined; } - return { from: exportItem?.from, name: exportAlias.name }; + return { from: exportItem?.from, name: exportMember.identifier }; } #getImportInfo(module: Module, importKey: string): ImportInfo | undefined { const importItem = module.model.getImport(importKey); - const importAlias = importItem?.getMember(importKey); + const importMember = importItem?.getMember(importKey); - if (importItem === undefined || importAlias === undefined) + if (importItem === undefined || importMember === undefined) { return undefined; } - return { from: importItem.from, name: importAlias.name }; + return { from: importItem.from, name: importMember.identifier }; } } diff --git a/packages/build/src/source/segment/Reader.ts b/packages/build/src/source/segment/Reader.ts index 25e4dfa8..e9719fb6 100644 --- a/packages/build/src/source/segment/Reader.ts +++ b/packages/build/src/source/segment/Reader.ts @@ -1,5 +1,5 @@ -import { ESFunction, ESClass, ESMember } from '@jitar/analysis'; +import { ESFunction, ESClass, ESDeclaration } from '@jitar/analysis'; import type { FileManager } from '@jitar/sourcing'; import { Defaults, Files, Keywords } from '../../definitions'; @@ -169,12 +169,12 @@ export default class SegmentReader } } - #constructProperties(module: Module, model: ESMember, importKey: string, idGenerator: IdGenerator): MemberProperties + #constructProperties(module: Module, model: ESDeclaration, importKey: string, idGenerator: IdGenerator): MemberProperties { const configuration = module.imports[importKey]; const id = idGenerator.next(); - const name = configuration.as ?? model.name; + const name = configuration.as ?? model.identifier!; const access = configuration.access ?? Defaults.ACCESS_LEVEL; const version = configuration.version ?? Defaults.VERSION_NUMBER; const fqn = this.#constructFqn(module, name, importKey); @@ -212,7 +212,7 @@ export default class SegmentReader return importKey === Keywords.DEFAULT; } - #createMember(segment: Segment, module: Module, model: ESMember, properties: MemberProperties): void + #createMember(segment: Segment, module: Module, model: ESDeclaration, properties: MemberProperties): void { if (model instanceof ESClass) { diff --git a/packages/build/src/target/index.ts b/packages/build/src/target/index.ts index 56aac9e2..9705a88f 100644 --- a/packages/build/src/target/index.ts +++ b/packages/build/src/target/index.ts @@ -1,2 +1,4 @@ export { default as ApplicationBuilder } from './application/Builder'; +export { default as LocalModuleGenerator } from './module/LocalGenerator'; +export { default as RemoteModuleGenerator } from './module/RemoteGenerator'; diff --git a/packages/build/src/target/module/Builder.ts b/packages/build/src/target/module/Builder.ts index 55d78f53..79a80a21 100644 --- a/packages/build/src/target/module/Builder.ts +++ b/packages/build/src/target/module/Builder.ts @@ -1,18 +1,15 @@ import type { FileManager } from '@jitar/sourcing'; -import type { Application, SegmentImplementation as Implementation, Module, Segment, Segmentation, ResourcesList } from '../../source'; +import type { Application, Module, Segment, Segmentation, ResourcesList } from '../../source'; import { FileHelper } from '../../utils'; -import LocalBuilder from './LocalBuilder'; -import RemoteBuilder from './RemoteBuilder'; +import LocalGenerator from './LocalGenerator'; +import RemoteGenerator from './RemoteGenerator'; export default class Builder { readonly #targetFileManager: FileManager; - - readonly #localBuilder = new LocalBuilder(); - readonly #remoteBuilder = new RemoteBuilder(); readonly #fileHelper = new FileHelper(); constructor(targetFileManager: FileManager) @@ -62,7 +59,9 @@ export default class Builder async #buildCommonModule(module: Module, resources: ResourcesList, segmentation: Segmentation): Promise { const filename = module.filename; - const code = this.#localBuilder.build(module, resources, segmentation); + + const generator = new LocalGenerator(module, resources, segmentation); + const code = generator.generate(); return this.#targetFileManager.write(filename, code); } @@ -70,7 +69,9 @@ export default class Builder async #buildSegmentModule(module: Module, resources: ResourcesList, segment: Segment, segmentation: Segmentation): Promise { const filename = this.#fileHelper.addSubExtension(module.filename, segment.name); - const code = this.#localBuilder.build(module, resources, segmentation, segment); + + const generator = new LocalGenerator(module, resources, segmentation, segment); + const code = generator.generate(); return this.#targetFileManager.write(filename, code); } @@ -79,30 +80,11 @@ export default class Builder { // The remote module contains calls to segmented procedures only - const implementations = this.#getImplementations(module, segments); const filename = this.#fileHelper.addSubExtension(module.filename, 'remote'); - const code = this.#remoteBuilder.build(implementations); + + const generator = new RemoteGenerator(module, segments); + const code = generator.generate(); return this.#targetFileManager.write(filename, code); } - - #getImplementations(module: Module, segments: Segment[]): Implementation[] - { - const segmentModules = segments.map(segment => segment.getModule(module.filename)); - const implementations = segmentModules.flatMap(segmentModule => segmentModule!.getImplementations()); - - // Implementation can be duplicated across segments - // We need to ensure that each implementation is unique - - const unique = new Map(); - - for (const implementation of implementations) - { - const key = `${implementation.fqn}:${implementation.version.toString()}`; - - unique.set(key, implementation); - } - - return [...unique.values()]; - } } diff --git a/packages/build/src/target/module/ExportRewriter.ts b/packages/build/src/target/module/ExportRewriter.ts deleted file mode 100644 index 7e7ed85a..00000000 --- a/packages/build/src/target/module/ExportRewriter.ts +++ /dev/null @@ -1,57 +0,0 @@ - -import type { ESExport } from '@jitar/analysis'; - -import { Patterns, Values } from '../../definitions'; - -import LocationRewriter from './LocationRewriter'; - -export default class ExportRewriter extends LocationRewriter -{ - get replacementPattern() { return Patterns.EXPORT; } - - parseStatement(statement: string): ESExport - { - return this.parser.parseExport(statement); - } - - includeInBundle(dependency: ESExport, from: string, keys: string[]): string - { - if (dependency.members.length === 0) - { - return `export "${from}";`; - } - - const members = this.#rewriteStaticExportMembers(dependency, keys); - - return `export ${members} from "${from}";`; - } - - excludeFromBundle(dependency: ESExport, from: string): string - { - // Exports cannot be excluded from the bundle. - - // This only called when a resource module is re-exported from an application module. - // Although the resource module is not included in the bundle, the re-exported module is. - // Depending on the situation, this is desired, but is most likely a configuration error. - - const keys = dependency.members.map(member => member.name); - - return this.includeInBundle(dependency, from, keys); - } - - #rewriteStaticExportMembers(dependency: ESExport, keys: string[]): string - { - const members = dependency.members.filter(member => keys.includes(member.name)); - - if (members.length === 1 && members[0].name === '') - { - const member = members[0]; - - return member.name !== member.as ? `${Values.ASTERISK} as ${member.as}` : Values.ASTERISK; - } - - const memberExports = members.map(member => member.name !== member.as ? `${member.name} as ${member.as}` : member.name); - - return `{ ${memberExports.join(', ')} }`; - } -} diff --git a/packages/build/src/target/module/ImportRewriter.ts b/packages/build/src/target/module/ImportRewriter.ts deleted file mode 100644 index 5bbafb86..00000000 --- a/packages/build/src/target/module/ImportRewriter.ts +++ /dev/null @@ -1,80 +0,0 @@ - -import type { ESImport } from '@jitar/analysis'; - -import { Patterns, Keywords, Values } from '../../definitions'; - -import LocationRewriter from './LocationRewriter'; - -export default class ImportRewriter extends LocationRewriter -{ - get replacementPattern() { return Patterns.IMPORT; } - - parseStatement(statement: string): ESImport - { - return this.parser.parseImport(statement); - } - - includeInBundle(dependency: ESImport, from: string, keys: string[]): string - { - if (dependency.members.length === 0) - { - return `import "${from}";`; - } - - const members = this.#rewriteStaticImportMembers(dependency, keys); - - return `import ${members} from "${from}";`; - } - - excludeFromBundle(dependency: ESImport, from: string): string - { - // Dynamic imports are used to distinct excludes from includes. - // This has to be configured in the bundler, in case this isn't the default behavior. - - if (dependency.members.length === 0) - { - return `await import("${from}");`; - } - - const members = this.#rewriteDynamicImportMembers(dependency); - - return `const ${members} = await import("${from}");`; - } - - #rewriteStaticImportMembers(dependency: ESImport, keys: string[]): string - { - const members = dependency.members.filter(member => keys.includes(member.name)); - - const defaultMember = members.find(member => member.name === Keywords.DEFAULT); - const hasDefaultMember = defaultMember !== undefined; - const defaultMemberImport = hasDefaultMember ? defaultMember.as : ''; - - const namedMembers = members.filter(member => member.name !== Keywords.DEFAULT); - const namedMemberImports = namedMembers.map(member => member.name !== member.as ? `${member.name} as ${member.as}` : member.name); - const hasNamedMembers = namedMemberImports.length > 0; - const groupedNamedMemberImports = hasNamedMembers ? `{ ${namedMemberImports.join(', ')} }` : ''; - - const separator = hasDefaultMember && hasNamedMembers ? ', ' : ''; - - return `${defaultMemberImport}${separator}${groupedNamedMemberImports}`; - } - - #rewriteDynamicImportMembers(dependency: ESImport): string - { - if (this.#doesImportAll(dependency)) - { - return dependency.members[0].as; - } - - const members = dependency.members; - const memberImports = members.map(member => member.name !== member.as ? `${member.name} : ${member.as}` : member.name); - - return `{ ${memberImports.join(', ')} }`; - } - - #doesImportAll(dependency: ESImport): boolean - { - return dependency.members.length === 1 - && dependency.members[0].name === Values.ASTERISK; - } -} diff --git a/packages/build/src/target/module/LocalBuilder.ts b/packages/build/src/target/module/LocalBuilder.ts deleted file mode 100644 index e9a3daf8..00000000 --- a/packages/build/src/target/module/LocalBuilder.ts +++ /dev/null @@ -1,18 +0,0 @@ - -import { Module, Segmentation, Segment, ResourcesList } from '../../source'; - -import ImportRewriter from './ImportRewriter'; -import ExportRewriter from './ExportRewriter'; - -export default class LocalBuilder -{ - build(module: Module, resources: ResourcesList, segmentation: Segmentation, segment?: Segment): string - { - const importRewriter = new ImportRewriter(module, resources, segmentation, segment); - const exportRewriter = new ExportRewriter(module, resources, segmentation, segment); - - const importCode = importRewriter.rewrite(module.code); - - return exportRewriter.rewrite(importCode); - } -} diff --git a/packages/build/src/target/module/LocalGenerator.ts b/packages/build/src/target/module/LocalGenerator.ts new file mode 100644 index 00000000..c4f12fbb --- /dev/null +++ b/packages/build/src/target/module/LocalGenerator.ts @@ -0,0 +1,237 @@ + +import type { ESImport, ESExport, ESModuleMember, ESModule } from '@jitar/analysis'; + +import type { Module, Segmentation, Segment, ResourcesList } from '../../source'; +import { FileHelper } from '../../utils'; + +import RewriteItemNotFound from './errors/RewriteItemNotFound'; + +type ModuleImportKeys = { segmentKeys: string[], remoteKeys: string[], commonKeys: string[] }; + +export default class LocalGenerator +{ + readonly #module: Module; + readonly #resources: ResourcesList; + readonly #segmentation: Segmentation; + readonly #segment: Segment | undefined; + + readonly #fileHelper = new FileHelper(); + + constructor(module: Module, resources: ResourcesList, segmentation: Segmentation, segment?: Segment) + { + this.#module = module; + this.#resources = resources; + this.#segmentation = segmentation; + this.#segment = segment; + } + + generate(): string + { + const model = this.#module.model.clone(); + + this.#rewriteItems(model, model.imports); + this.#rewriteItems(model, model.exports); + + return model.toString(); + } + + #rewriteItems(model: ESModule, items: T[]): void + { + for (const item of items) + { + const rewritten = this.#rewriteItem(item); + + if (rewritten.length === 0) + { + continue; + } + + const index = model.statements.indexOf(item); + + if (index === -1) + { + throw new RewriteItemNotFound(item.toString()); + } + + model.statements.splice(index, 1, ...rewritten); + } + } + + #rewriteItem(item: T): T[] + { + if (this.#skipRewrite(item)) return []; + + const targetModuleFilename = this.#getTargetModuleFilename(item); + + if (this.#resources.isResourceModule(targetModuleFilename)) + { + return this.#rewriteToResource(item, targetModuleFilename,); + } + + return this.#rewriteToApplication(item, targetModuleFilename); + } + + #skipRewrite(item: T): boolean + { + return item.from === undefined || this.#isRuntimeModule(item); + } + + #isRuntimeModule(item: T): boolean + { + return this.#fileHelper.isApplicationModule(item.from!) === false; + } + + #getTargetModuleFilename(item: T): string + { + const from = item.from!; + const callingModulePath = this.#fileHelper.extractPath(this.#module.filename); + + return this.#fileHelper.makePathAbsolute(from, callingModulePath, ''); + } + + #rewriteToResource(item: T, targetModuleFilename: string): T[] + { + // Rewrite to dynamic import? + const clone = item.clone() as T; + clone.from = this.#rewriteApplicationFrom(targetModuleFilename); + + return [clone]; + } + + #rewriteToApplication(item: T, targetModuleFilename: string): T[] + { + if (item.members.length === 0) + { + return [this.#rewriteToCommon(targetModuleFilename, item, [])]; + } + + const { segmentKeys, remoteKeys, commonKeys } = this.#getModuleImportKeys(targetModuleFilename, item); + + const rewrites: T[] = []; + + if (segmentKeys.length > 0) + { + rewrites.push(this.#rewriteToSegment(targetModuleFilename, item, segmentKeys)); + } + + if (remoteKeys.length > 0) + { + rewrites.push(this.#rewriteToRemote(targetModuleFilename, item, remoteKeys)); + } + + if (commonKeys.length > 0) + { + rewrites.push(this.#rewriteToCommon(targetModuleFilename, item, commonKeys)); + } + + return rewrites; + } + + #getModuleImportKeys(targetModuleFilename: string, item: ESImport | ESExport): ModuleImportKeys + { + const moduleSegmentKeys = this.#getSegmentImportKeys(targetModuleFilename, this.#segment); + const moduleRemoteKeys = this.#getRemoteImportKeys(targetModuleFilename, moduleSegmentKeys); + + const segmentKeys = this.#filterMemberKeys(item, moduleSegmentKeys); + const remoteKeys = this.#filterMemberKeys(item, moduleRemoteKeys); + const commonKeys = this.#extractUnsegmentedImportKeys(item, [...segmentKeys, ...remoteKeys]); + + return { segmentKeys, remoteKeys, commonKeys }; + } + + #getSegmentImportKeys(targetModuleFilename: string, segment?: Segment): string[] + { + if (segment === undefined) + { + return []; + } + + const module = segment.getModule(targetModuleFilename); + + if (module === undefined) + { + return []; + } + + return Object.keys(module.imports); + } + + #getRemoteImportKeys(targetModuleFilename: string, segmentKeys: string[]): string[] + { + const segments = this.#segmentation.getSegments(targetModuleFilename).filter(segment => segment !== this.#segment); + const importKeys = segments.flatMap(segment => this.#getSegmentImportKeys(targetModuleFilename, segment)); + const uniqueKeys = [...new Set(importKeys)]; + + return uniqueKeys.filter(key => segmentKeys.includes(key) === false); + } + + #filterMemberKeys(item: ESImport | ESExport, keys: string[]): string[] + { + return item.members + .filter(member => keys.includes(member.identifier)) + .map(member => member.identifier); + } + + #extractUnsegmentedImportKeys(item: ESImport | ESExport, segmentedKeys: string[]): string[] + { + return item.members + .filter(member => segmentedKeys.includes(member.identifier) === false) + .map(member => member.identifier); + } + + #rewriteToSegment(targetModuleFilename: string, item: ESImport | ESExport, keys: string[]): T + { + const clone = item.clone() as T; + clone.from = this.#rewriteApplicationFrom(targetModuleFilename, this.#segment?.name); + clone.members = this.#filterMembers(item.members, keys); + + return clone; + } + + #rewriteToRemote(targetModuleFilename: string, item: ESImport | ESExport, keys: string[]): T + { + const clone = item.clone() as T; + clone.from = this.#rewriteApplicationFrom(targetModuleFilename, 'remote'); + clone.members = this.#filterMembers(item.members, keys); + + return clone; + } + + #rewriteToCommon(targetModuleFilename: string, item: ESImport | ESExport, keys: string[]): T + { + const clone = item.clone() as T; + clone.from = this.#rewriteApplicationFrom(targetModuleFilename); + clone.members = this.#filterMembers(item.members, keys); + + return clone; + } + + #filterMembers(members: ESModuleMember[], keys: string[]): ESModuleMember[] + { + const filtered = []; + + for (const key of keys) + { + const member = members.find(member => member.identifier === key); + + if (member === undefined) + { + continue; + } + + filtered.push(member); + } + + return filtered; + } + + #rewriteApplicationFrom(filename: string, scope?: string): string + { + const callingModulePath = this.#fileHelper.extractPath(this.#module.filename); + const relativeFilename = this.#fileHelper.makePathRelative(filename, callingModulePath); + + return scope !== undefined + ? this.#fileHelper.addSubExtension(relativeFilename, scope) + : relativeFilename; + } +} diff --git a/packages/build/src/target/module/LocationRewriter.ts b/packages/build/src/target/module/LocationRewriter.ts deleted file mode 100644 index d1615e18..00000000 --- a/packages/build/src/target/module/LocationRewriter.ts +++ /dev/null @@ -1,214 +0,0 @@ - -import { Parser } from '@jitar/analysis'; -import type { ESImport, ESExport } from '@jitar/analysis'; - -import type { Module, Segmentation, Segment, ResourcesList } from '../../source'; -import { FileHelper } from '../../utils'; - -type ModuleImportKeys = { segmentKeys: string[], remoteKeys: string[], commonKeys: string[] }; - -export default abstract class LocationRewriter -{ - readonly #module: Module; - readonly #resources: ResourcesList; - readonly #segmentation: Segmentation; - readonly #segment: Segment | undefined; - - readonly #parser = new Parser(); - readonly #fileHelper = new FileHelper(); - - constructor(module: Module, resources: ResourcesList, segmentation: Segmentation, segment?: Segment) - { - this.#module = module; - this.#resources = resources; - this.#segmentation = segmentation; - this.#segment = segment; - } - - get parser() { return this.#parser; } - - abstract get replacementPattern(): RegExp; - - rewrite(code: string): string - { - const replacer = (statement: string) => this.replaceStatement(statement); - - return code.replaceAll(this.replacementPattern, replacer); - } - - replaceStatement(statement: string): string - { - const dependency = this.parseStatement(statement); - - if (dependency.from === undefined) - { - return statement; - } - - return this.#isApplicationModule(dependency) - ? this.#rewriteForApplication(dependency) - : this.#rewriteForRuntime(dependency); - } - - abstract parseStatement(statement: string): ESImport | ESExport; - - #rewriteForApplication(dependency: ESImport | ESExport): string - { - const targetModuleFilename = this.#getTargetModuleFilename(dependency); - - if (this.#resources.isResourceModule(targetModuleFilename)) - { - return this.#rewriteToResource(targetModuleFilename, dependency); - } - - return this.#rewriteModule(targetModuleFilename, dependency); - } - - #rewriteForRuntime(dependency: ESImport | ESExport): string - { - const from = this.#rewriteRuntimeFrom(dependency); - const keys = dependency.members.map(member => member.name); - - return this.includeInBundle(dependency, from, keys); - } - - #rewriteModule(targetModuleFilename: string, dependency: ESImport | ESExport): string - { - if (dependency.members.length === 0) - { - return this.#rewriteToCommon(targetModuleFilename, dependency, []); - } - - const { segmentKeys, remoteKeys, commonKeys } = this.#getModuleImportKeys(targetModuleFilename, dependency); - - const imports: string[] = []; - - if (segmentKeys.length > 0) - { - imports.push(this.#rewriteToSegment(targetModuleFilename, dependency, segmentKeys)); - } - - if (remoteKeys.length > 0) - { - imports.push(this.#rewriteToRemote(targetModuleFilename, dependency, remoteKeys)); - } - - if (commonKeys.length > 0) - { - imports.push(this.#rewriteToCommon(targetModuleFilename, dependency, commonKeys)); - } - - return imports.filter(item => item.length > 0).join('\n'); - } - - #rewriteToResource(targetModuleFilename: string, dependency: ESImport | ESExport): string - { - const from = this.#rewriteApplicationFrom(targetModuleFilename); - - return this.excludeFromBundle(dependency, from); - } - - #rewriteToSegment(targetModuleFilename: string, dependency: ESImport | ESExport, keys: string[]): string - { - const from = this.#rewriteApplicationFrom(targetModuleFilename, this.#segment?.name); - - return this.includeInBundle(dependency, from, keys); - } - - #rewriteToRemote(targetModuleFilename: string, dependency: ESImport | ESExport, keys: string[]): string - { - const from = this.#rewriteApplicationFrom(targetModuleFilename, 'remote'); - - return this.includeInBundle(dependency, from, keys); - } - - #rewriteToCommon(targetModuleFilename: string, dependency: ESImport | ESExport, keys: string[]): string - { - const from = this.#rewriteApplicationFrom(targetModuleFilename); - - return this.includeInBundle(dependency, from, keys); - } - - #isApplicationModule(dependency: ESImport | ESExport): boolean - { - const from = this.#fileHelper.stripPath(dependency.from as string); - - return this.#fileHelper.isApplicationModule(from); - } - - #getTargetModuleFilename(dependency: ESImport | ESExport): string - { - const from = this.#fileHelper.stripPath(dependency.from as string); - const callingModulePath = this.#fileHelper.extractPath(this.#module.filename); - - return this.#fileHelper.makePathAbsolute(from, callingModulePath); - } - - #getModuleImportKeys(targetModuleFilename: string, dependency: ESImport | ESExport): ModuleImportKeys - { - const moduleSegmentKeys = this.#getSegmentImportKeys(targetModuleFilename, this.#segment); - const moduleRemoteKeys = this.#getRemoteImportKeys(targetModuleFilename, moduleSegmentKeys); - - const segmentKeys = this.#filterMemberKeys(dependency, moduleSegmentKeys); - const remoteKeys = this.#filterMemberKeys(dependency, moduleRemoteKeys); - const commonKeys = this.#extractUnsegmentedImportKeys(dependency, [...segmentKeys, ...remoteKeys]); - - return { segmentKeys, remoteKeys, commonKeys }; - } - - #getSegmentImportKeys(targetModuleFilename: string, segment?: Segment): string[] - { - if (segment === undefined) - { - return []; - } - - const module = segment.getModule(targetModuleFilename); - - return module !== undefined - ? Object.keys(module.imports) - : []; - } - - #getRemoteImportKeys(targetModuleFilename: string, segmentKeys: string[]): string[] - { - const segments = this.#segmentation.getSegments(targetModuleFilename).filter(segment => segment !== this.#segment); - const importKeys = segments.map(segment => this.#getSegmentImportKeys(targetModuleFilename, segment)).flat(); - const uniqueKeys = [...new Set(importKeys)]; - - return uniqueKeys.filter(key => segmentKeys.includes(key) === false); - } - - #filterMemberKeys(dependency: ESImport | ESExport, keys: string[]): string[] - { - return dependency.members - .filter(member => keys.includes(member.name)) - .map(member => member.name); - } - - #extractUnsegmentedImportKeys(dependency: ESImport | ESExport, segmentedKeys: string[]): string[] - { - return dependency.members - .filter(member => segmentedKeys.includes(member.name) === false) - .map(member => member.name); - } - - #rewriteApplicationFrom(filename: string, scope?: string): string - { - const callingModulePath = this.#fileHelper.extractPath(this.#module.filename); - const relativeFilename = this.#fileHelper.makePathRelative(filename, callingModulePath); - - return scope !== undefined - ? this.#fileHelper.addSubExtension(relativeFilename, scope) - : relativeFilename; - } - - #rewriteRuntimeFrom(dependency: ESImport | ESExport): string - { - return this.#fileHelper.stripPath(dependency.from as string); - } - - abstract includeInBundle(dependency: ESImport | ESExport, from: string, keys: string[]): string; - - abstract excludeFromBundle(dependency: ESImport | ESExport, from: string): string; -} diff --git a/packages/build/src/target/module/RemoteBuilder.ts b/packages/build/src/target/module/RemoteBuilder.ts deleted file mode 100644 index 4d761f7e..00000000 --- a/packages/build/src/target/module/RemoteBuilder.ts +++ /dev/null @@ -1,129 +0,0 @@ - -import { ESDestructuredArray, ESDestructuredObject, ESDestructuredValue, ESField } from '@jitar/analysis'; -import type { ESParameter } from '@jitar/analysis'; -import { AccessLevels } from '@jitar/execution'; - -import { Keywords } from '../../definitions'; -import type { SegmentImplementation as Implementation } from '../../source'; - -export default class RemoteBuilder -{ - build(implementations: Implementation[]): string - { - let code = ''; - - for (const implementation of implementations) - { - code += implementation.access === AccessLevels.PRIVATE - ? this.#createPrivateCode(implementation) - : this.#createPublicCode(implementation); - } - - return code.trim(); - } - - #createPrivateCode(implementation: Implementation): string - { - // Private procedures are not accessible from the outside. - // Therefore we need to throw an error when they are called. - - const fqn = implementation.fqn; - const version = implementation.version; - - const declaration = this.#createDeclaration(implementation); - const body = `throw new ProcedureNotAccessible('${fqn}', '${version}');`; - - return this.#createFunction(declaration, body); - } - - #createPublicCode(implementation: Implementation): string - { - // Public procedures are accessible from the outside. - // Therefore we need to create a remote implementation. - - const fqn = implementation.fqn; - const version = implementation.version; - const args = this.#createArguments(implementation.model.parameters); - - const declaration = this.#createDeclaration(implementation); - const body = `return __run('${fqn}', '${version}', { ${args} }, this);`; - - return this.#createFunction(declaration, body); - } - - #createParameters(parameters: ESParameter[]): string - { - const result: string[] = []; - - for (const parameter of parameters) - { - if (parameter instanceof ESField) - { - result.push(parameter.name); - } - else if (parameter instanceof ESDestructuredArray) - { - result.push(parameter.toString()); - } - else if (parameter instanceof ESDestructuredObject) - { - result.push(parameter.toString()); - } - } - - return result.join(', '); - } - - #createArguments(parameters: ESParameter[]): string - { - const result = this.#extractArguments(parameters); - - return result.join(', '); - } - - #extractArguments(parameters: ESParameter[]): string[] - { - const result: string[] = []; - - for (const parameter of parameters) - { - if (parameter instanceof ESDestructuredValue) - { - const argumentz = this.#extractArguments(parameter.members); - - result.push(...argumentz); - } - else if (parameter instanceof ESField) - { - const argument = this.#createNamedArgument(parameter); - - result.push(argument); - } - } - - return result; - } - - #createNamedArgument(parameter: ESField): string - { - const key = parameter.name; - const value = key.startsWith('...') ? key.substring(3) : key; - - return `'${key}': ${value}`; - } - - #createDeclaration(implementation: Implementation): string - { - const name = implementation.model.name; - const parameters = this.#createParameters(implementation.model.parameters); - - const prefix = implementation.importKey === Keywords.DEFAULT ? `${Keywords.DEFAULT} ` : ''; - - return `\nexport ${prefix}async function ${name}(${parameters})`; - } - - #createFunction(declaration: string, body: string): string - { - return `${declaration} {\n\t${body}\n}\n`; - } -} diff --git a/packages/build/src/target/module/RemoteGenerator.ts b/packages/build/src/target/module/RemoteGenerator.ts new file mode 100644 index 00000000..f4b68970 --- /dev/null +++ b/packages/build/src/target/module/RemoteGenerator.ts @@ -0,0 +1,152 @@ + +import { ESModule, ESArrayBinding, ESObjectBinding, ESIdentifierBinding, ESBlock, ESModuleMember, ESExport, ESFunction, ESParameter, ESStatement } from '@jitar/analysis'; +import { AccessLevels } from '@jitar/execution'; + +import { Keywords } from '../../definitions'; +import type { SegmentImplementation as Implementation, Module, Segment } from '../../source'; + +export default class RemoteGenerator +{ + readonly #module: Module; + readonly #segments: Segment[]; + + constructor(module: Module, segments: Segment[]) + { + this.#module = module; + this.#segments = segments; + } + + generate(): string + { + const statements: ESStatement[] = []; + const implementations = this.#getImplementations(); + + for (const implementation of implementations) + { + const declarations = implementation.access === AccessLevels.PRIVATE + ? this.#createPrivate(implementation) + : this.#createPublic(implementation); + + statements.push(...declarations); + } + + const model = new ESModule(statements); + + return model.toString(); + } + + #getImplementations(): Implementation[] + { + const segmentModules = this.#segments.map(segment => segment.getModule(this.#module.filename)); + const implementations = segmentModules.flatMap(segmentModule => segmentModule!.getImplementations()); + + // Implementation can be duplicated across segments + // We need to ensure that each implementation is unique + + const unique = new Map(); + + for (const implementation of implementations) + { + const key = `${implementation.fqn}:${implementation.version.toString()}`; + + unique.set(key, implementation); + } + + return [...unique.values()]; + } + + #createPrivate(implementation: Implementation): ESStatement[] + { + // Private procedures are not accessible from the outside. + // Therefore we need to throw an error when they are called. + + const fqn = implementation.fqn; + const version = implementation.version; + const code = `throw new ProcedureNotAccessible('${fqn}','${version}');`; + + return [ + this.#createExport(implementation), + this.#createFunction(implementation, code) + ]; + } + + #createPublic(implementation: Implementation): ESStatement[] + { + // Public procedures are accessible from the outside. + // Therefore we need to create a remote implementation. + + const fqn = implementation.fqn; + const version = implementation.version; + const args = this.#createArguments(implementation.model.parameters); + const code = `return __run('${fqn}','${version}',{${args}},this);`; + + return [ + this.#createExport(implementation), + this.#createFunction(implementation, code) + ]; + } + + #createExport(implementation: Implementation): ESExport + { + const identifier = implementation.model.identifier!; + const alias = implementation.importKey === Keywords.DEFAULT ? Keywords.DEFAULT : undefined; + + const member = new ESModuleMember(identifier, alias); + + return new ESExport([member], undefined); + } + + #createFunction(implementation: Implementation, code: string): ESFunction + { + const identifier = implementation.model.identifier; + const parameters = implementation.model.parameters; + const body = new ESBlock(`{${code}}`); + const isAsync = implementation.model.isAsync; + + return new ESFunction(identifier, parameters, body, isAsync); + } + + #createArguments(parameters: ESParameter[]): string + { + const result = this.#extractArguments(parameters); + + return result.join(','); + } + + #extractArguments(parameters: ESParameter[]): string[] + { + const result: string[] = []; + + for (const parameter of parameters) + { + if (parameter.binding instanceof ESIdentifierBinding) + { + const argument = this.#createNamedArgument(parameter.binding); + + result.push(argument); + } + else if (parameter.binding instanceof ESArrayBinding) + { + const argumentz = this.#extractArguments(parameter.binding.elements); + + result.push(...argumentz); + } + else if (parameter.binding instanceof ESObjectBinding) + { + const argumentz = this.#extractArguments(parameter.binding.elements); + + result.push(...argumentz); + } + } + + return result; + } + + #createNamedArgument(binding: ESIdentifierBinding): string + { + const key = binding.identifier; + const value = binding.toString(); + + return `'${key}':${value}`; + } +} diff --git a/packages/build/src/target/module/errors/RewriteItemNotFound.ts b/packages/build/src/target/module/errors/RewriteItemNotFound.ts new file mode 100644 index 00000000..6f993be1 --- /dev/null +++ b/packages/build/src/target/module/errors/RewriteItemNotFound.ts @@ -0,0 +1,8 @@ + +export default class RewriteItemFound extends Error +{ + constructor(definition: string) + { + super(`Could not find the rewrite item: ${definition}`); + } +} diff --git a/packages/build/src/target/segment/Builder.ts b/packages/build/src/target/segment/Builder.ts index 5a35049d..4cfeff6e 100644 --- a/packages/build/src/target/segment/Builder.ts +++ b/packages/build/src/target/segment/Builder.ts @@ -1,23 +1,16 @@ -import type { ESField, ESFunction, ESParameter } from '@jitar/analysis'; -import { ESDestructuredArray, ESDestructuredObject } from '@jitar/analysis'; -import { VersionParser } from '@jitar/execution'; import { Logger } from '@jitar/logging'; import type { FileManager } from '@jitar/sourcing'; -import { Keywords } from '../../definitions'; -import type { Application, Segment, SegmentModule } from '../../source'; -import { FileHelper } from '../../utils'; +import type { Application, Segment } from '../../source'; -const RUNTIME_IMPORTS = 'import { Segment, Class, Procedure, Implementation, Version, NamedParameter, ArrayParameter, ObjectParameter } from "jitar";'; +import Generator from './Generator'; export default class Builder { readonly #targetFileManager: FileManager; readonly #logger: Logger; - readonly #fileHelper = new FileHelper(); - readonly #versionParser = new VersionParser(); constructor(targetFileManager: FileManager, logger: Logger) { @@ -38,160 +31,11 @@ export default class Builder { const filename = `${segment.name}.segment.js`; - const code = this.#createCode(segment); + const generator = new Generator(segment); + const code = generator.generate(); await this.#targetFileManager.write(filename, code); this.#logger.info(`Built ${segment.name} segment (${segment.modules.length} modules, ${segment.procedures.length} procedures, ${segment.classes.length} classes)`); } - - #createCode(segment: Segment): string - { - const importCode = this.#createImportCode(segment); - const segmentCode = this.#createSegmentCode(segment); - - return `${importCode}\n${segmentCode}`; - } - - #createImportCode(segment: Segment): string - { - const moduleImports = this.#createModuleImports(segment); - - return `${RUNTIME_IMPORTS}\n${moduleImports}`; - } - - #createModuleImports(segment: Segment): string - { - const imports = []; - - // We only want to include modules that are defined in the segment configuration. - // The other modules contain classes and procedures that are re-exported by at least one segmented module. - // Adding them would cause a duplicate declaration error. - - for (const module of segment.getSegmentedModules()) - { - if (module.members.length === 0) - { - continue; - } - - const filename = this.#fileHelper.addSubExtension(module.filename, segment.name); - const members = this.#createModuleImportMembers(module); - - const importRule = `import ${members} from "./${filename}";`; - - imports.push(importRule); - } - - return imports.join('\n'); - } - - #createModuleImportMembers(module: SegmentModule): string - { - const members = module.members; - - const defaultImplementation = members.find(member => member.importKey === Keywords.DEFAULT); - const hasDefaultImplementation = defaultImplementation !== undefined; - const defaultMemberImport = hasDefaultImplementation ? defaultImplementation.id : ''; - - const namedImplementations = members.filter(member => member.importKey !== Keywords.DEFAULT); - const nameImplementationImports = namedImplementations.map(member => `${member.importKey} as ${member.id}`); - const hasNamedImplementations = namedImplementations.length > 0; - const groupedNamedMemberImports = hasNamedImplementations ? `{ ${nameImplementationImports.join(', ')} }` : ''; - - const separator = hasDefaultImplementation && hasNamedImplementations ? ', ' : ''; - - return `${defaultMemberImport}${separator}${groupedNamedMemberImports}`; - } - - #createSegmentCode(segment: Segment): string - { - const lines: string[] = []; - - lines.push(`export default new Segment("${segment.name}")`); - - for (const clazz of segment.classes) - { - lines.push(`\t.addClass(new Class("${clazz.fqn}", ${clazz.id}))`); - } - - for (const procedure of segment.procedures) - { - lines.push(`\t.addProcedure(new Procedure("${procedure.fqn}")`); - - for (const implementation of procedure.implementations) - { - const version = this.#createVersionCode(implementation.version); - const parameters = this.#createParametersCode(implementation.model); - - lines.push(`\t\t.addImplementation(new Implementation(${version}, "${implementation.access}", ${parameters}, ${implementation.id}))`); - } - - lines.push('\t)'); - } - - return lines.join('\n'); - } - - #createVersionCode(versionString: string): string - { - const version = this.#versionParser.parse(versionString); - - return `new Version(${version.major}, ${version.minor}, ${version.patch})`; - } - - #createParametersCode(model: ESFunction): string - { - const result = this.#extractParameters(model.parameters); - - return `[${result.join(', ')}]`; - } - - #extractParameters(parameters: ESParameter[]): string[] - { - const result = []; - - // Named parameters are identified by their name. - // Destructured parameters are identified by their index. - - for (const parameter of parameters) - { - result.push(this.#extractParameter(parameter)); - } - - return result; - } - - #extractParameter(parameter: ESParameter): string - { - if (parameter instanceof ESDestructuredArray) - { - return this.#createArrayParameter(parameter); - } - else if (parameter instanceof ESDestructuredObject) - { - return this.#createObjectParameter(parameter); - } - - return this.#createNamedParameter(parameter); - } - - #createNamedParameter(parameter: ESField): string - { - return `new NamedParameter("${parameter.name}", ${parameter.value !== undefined})`; - } - - #createArrayParameter(parameter: ESDestructuredArray): string - { - const members = this.#extractParameters(parameter.members); - - return `new ArrayParameter([${members.join(', ')}])`; - } - - #createObjectParameter(parameter: ESDestructuredObject): string - { - const members = this.#extractParameters(parameter.members); - - return `new ObjectParameter([${members.join(', ')}])`; - } } diff --git a/packages/build/src/target/segment/Generator.ts b/packages/build/src/target/segment/Generator.ts new file mode 100644 index 00000000..376ba5f5 --- /dev/null +++ b/packages/build/src/target/segment/Generator.ts @@ -0,0 +1,194 @@ + +import { ESModule, ESFunction, ESIdentifierBinding, ESParameter, ESArrayBinding, ESObjectBinding, ESImport, ESModuleMember, ESVariable, ESExport, ESExpression } from '@jitar/analysis'; +import { VersionParser } from '@jitar/execution'; + +import type { Segment } from '../../source'; +import { FileHelper } from '../../utils'; + + +const JITAR_MODULE = 'jitar'; +const JITAR_IMPORTS = ['Segment', 'Class', 'Procedure', 'Implementation', 'Version', 'NamedParameter', 'ArrayParameter', 'ObjectParameter']; + +export default class Generator +{ + readonly #segment: Segment; + + readonly #fileHelper = new FileHelper(); + readonly #versionParser = new VersionParser(); + + constructor(segment: Segment) + { + this.#segment = segment; + } + + generate(): string + { + const imports = this.#createImports(); + const exported = this.#createExport(); + const declaration = this.#createDeclaration(); + + const model = new ESModule([...imports, exported, declaration]); + + return model.toString(); + } + + #createImports(): ESImport[] + { + const runtimeImport = this.#createRuntimeImport(); + const moduleImports = this.#createModuleImports(); + + return [runtimeImport, ...moduleImports]; + } + + #createRuntimeImport(): ESImport + { + const members = JITAR_IMPORTS.map(identifier => new ESModuleMember(identifier)); + + return new ESImport(members, JITAR_MODULE); + } + + #createModuleImports(): ESImport[] + { + const imports = []; + + // We only want to include modules that are defined in the segment configuration. + // The other modules contain classes and procedures that are re-exported by at least one segmented module. + // Adding them would cause a duplicate declaration error. + + const segmentName = this.#segment.name; + const modules = this.#segment.getSegmentedModules(); + + for (const module of modules) + { + const filename = this.#fileHelper.addSubExtension(module.filename, segmentName); + const from = `./${filename}`; + + if (module.members.length === 0) + { + const model = new ESImport([], from); + + imports.push(model); + + continue; + } + + const members = module.members.map(member => new ESModuleMember(member.importKey, member.id)); + const model = new ESImport(members, from); + + imports.push(model); + } + + return imports; + } + + #createExport(): ESExport + { + const member = new ESModuleMember('segment', 'default'); + + return new ESExport([member], undefined); + } + + #createDeclaration(): ESVariable + { + const lines: string[] = []; + + const segmentName = this.#segment.name; + const classes = this.#segment.classes; + const procedures = this.#segment.procedures; + + lines.push(`new Segment("${segmentName}")`); + + for (const clazz of classes) + { + lines.push(`\t.addClass(new Class("${clazz.fqn}", ${clazz.id}))`); + } + + for (const procedure of procedures) + { + lines.push(`\t.addProcedure(new Procedure("${procedure.fqn}")`); + + for (const implementation of procedure.implementations) + { + const version = this.#createVersionCode(implementation.version); + const parameters = this.#createParametersCode(implementation.model); + + lines.push(`\t\t.addImplementation(new Implementation(${version}, "${implementation.access}", ${parameters}, ${implementation.id}))`); + } + + lines.push('\t)'); + } + + const code = lines.join('\n'); + + const initializer = new ESExpression(code); + const binding = new ESIdentifierBinding('segment'); + + return new ESVariable('const', binding, initializer); + } + + #createVersionCode(versionString: string): string + { + const version = this.#versionParser.parse(versionString); + + return `new Version(${version.major}, ${version.minor}, ${version.patch})`; + } + + #createParametersCode(model: ESFunction): string + { + const result = this.#extractParameters(model.parameters); + + return `[${result.join(', ')}]`; + } + + #extractParameters(parameters: ESParameter[]): string[] + { + const result = []; + + // Named parameters are identified by their name. + // Destructured parameters are identified by their index. + + for (const parameter of parameters) + { + result.push(this.#extractParameter(parameter)); + } + + return result; + } + + #extractParameter(parameter: ESParameter): string + { + if (parameter.binding instanceof ESArrayBinding) + { + return this.#createArrayParameter(parameter); + } + else if (parameter.binding instanceof ESObjectBinding) + { + return this.#createObjectParameter(parameter); + } + + return this.#createNamedParameter(parameter); + } + + #createNamedParameter(parameter: ESParameter): string + { + const binding = parameter.binding as ESIdentifierBinding; + + return `new NamedParameter("${binding}", ${parameter.initializer !== undefined})`; + } + + #createArrayParameter(parameter: ESParameter): string + { + const binding = parameter.binding as ESArrayBinding; + const members = this.#extractParameters(binding.elements); + + return `new ArrayParameter([${members.join(', ')}])`; + } + + #createObjectParameter(parameter: ESParameter): string + { + const binding = parameter.binding as ESObjectBinding; + const members = this.#extractParameters(binding.elements); + + return `new ObjectParameter([${members.join(', ')}])`; + } +} diff --git a/packages/build/src/utils/FileHelper.ts b/packages/build/src/utils/FileHelper.ts index b7d0f81e..dbb93d1f 100644 --- a/packages/build/src/utils/FileHelper.ts +++ b/packages/build/src/utils/FileHelper.ts @@ -51,13 +51,15 @@ export default class FileHelper return `${prefix}/${suffix}`; } - makePathAbsolute(relativeFilename: string, relativeToPath: string): string + makePathAbsolute(relativeFilename: string, relativeToPath: string, prefix = '/'): string { const fullPath = relativeToPath !== '' ? `${relativeToPath}/${relativeFilename}` : relativeFilename; - return this.translatePath(fullPath); + const translated = this.translatePath(fullPath); + + return `${prefix}${translated}`; } extractPath(filename: string) @@ -65,11 +67,6 @@ export default class FileHelper return filename.split('/').slice(0, -1).join('/'); } - stripPath(path: string): string - { - return path.substring(1, path.length - 1); - } - extractFilename(filename: string) { return filename.split('/').pop(); diff --git a/packages/build/tsconfig.json b/packages/build/tsconfig.json index 4ca2247d..db3953f7 100644 --- a/packages/build/tsconfig.json +++ b/packages/build/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 4ca2247d..db3953f7 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/configuration/src/ConfigurationManager.ts b/packages/configuration/src/ConfigurationManager.ts index bb7f41bf..578ea42f 100644 --- a/packages/configuration/src/ConfigurationManager.ts +++ b/packages/configuration/src/ConfigurationManager.ts @@ -22,7 +22,7 @@ export default class ConfigurationManager const reader = new ConfigurationReader(fileManager); const validator = new Validator(); - this.#environmentConfigurator = new EnvironmentConfigurator(); + this.#environmentConfigurator = new EnvironmentConfigurator(fileManager); this.#runtimeConfigurationBuilder = new RuntimeConfigurationBuilder(reader, validator); this.#serverConfigurationBuilder = new ServerConfigurationBuilder(reader, validator); } diff --git a/packages/configuration/src/environment/Configurator.ts b/packages/configuration/src/environment/Configurator.ts index 319769ba..1e2d5a65 100644 --- a/packages/configuration/src/environment/Configurator.ts +++ b/packages/configuration/src/environment/Configurator.ts @@ -1,10 +1,21 @@ import dotenv from 'dotenv'; +import { FileManager } from '@jitar/sourcing'; + export default class Configurator { + readonly #fileManager: FileManager; + + constructor(fileManager: FileManager) + { + this.#fileManager = fileManager; + } + async configure(filename: string): Promise { - dotenv.config({ path: filename }); + const path = this.#fileManager.getAbsoluteLocation(filename); + + dotenv.config({ path }); } } diff --git a/packages/configuration/src/runtime/ConfigurationBuilder.ts b/packages/configuration/src/runtime/ConfigurationBuilder.ts index 1dcfe127..6d7a8a64 100644 --- a/packages/configuration/src/runtime/ConfigurationBuilder.ts +++ b/packages/configuration/src/runtime/ConfigurationBuilder.ts @@ -32,6 +32,12 @@ export default class ConfigurationBuilder configuration.segments ??= DefaultValues.SEGMENTS; configuration.resources ??= DefaultValues.RESOURCES; + configuration.meta = + { + root: this.#reader.getRootLocation(), + configFile: filename + }; + return configuration; } } diff --git a/packages/configuration/src/runtime/definitions/RuntimeConfiguration.ts b/packages/configuration/src/runtime/definitions/RuntimeConfiguration.ts index d5e34a6c..736e242d 100644 --- a/packages/configuration/src/runtime/definitions/RuntimeConfiguration.ts +++ b/packages/configuration/src/runtime/definitions/RuntimeConfiguration.ts @@ -7,6 +7,12 @@ type RuntimeConfiguration = target: string; segments: string; resources: string; + + meta: + { + root: string; + configFile: string; + } }; export default RuntimeConfiguration; diff --git a/packages/configuration/src/server/ConfigurationBuilder.ts b/packages/configuration/src/server/ConfigurationBuilder.ts index c45b0945..df47fb55 100644 --- a/packages/configuration/src/server/ConfigurationBuilder.ts +++ b/packages/configuration/src/server/ConfigurationBuilder.ts @@ -27,6 +27,12 @@ export default class ConfigurationBuilder throw new ServerConfigurationInvalid(validation); } + configuration.meta = + { + root: this.#reader.getRootLocation(), + configFile: filename + }; + return configuration; } } diff --git a/packages/configuration/src/server/definitions/ServerConfiguration.ts b/packages/configuration/src/server/definitions/ServerConfiguration.ts index 1443591b..d4e827fd 100644 --- a/packages/configuration/src/server/definitions/ServerConfiguration.ts +++ b/packages/configuration/src/server/definitions/ServerConfiguration.ts @@ -22,6 +22,12 @@ type ServerConfiguration = standalone?: StandaloneConfiguration; worker?: WorkerConfiguration; remoteWorker?: RemoteWorkerConfiguration; + + meta: + { + root: string; + configFile: string; + } }; export default ServerConfiguration; diff --git a/packages/configuration/src/utils/ConfigurationReader.ts b/packages/configuration/src/utils/ConfigurationReader.ts index 1524decd..88c64ee4 100644 --- a/packages/configuration/src/utils/ConfigurationReader.ts +++ b/packages/configuration/src/utils/ConfigurationReader.ts @@ -14,6 +14,11 @@ export default class ConfigurationReader this.#fileManager = fileManager; } + getRootLocation(): string + { + return this.#fileManager.getAbsoluteLocation('.'); + } + async read(filename: string): Promise> { const fileExists = await this.#fileManager.exists(filename); diff --git a/packages/configuration/test/fixtures/fileManager.fixture.ts b/packages/configuration/test/fixtures/fileManager.fixture.ts index e03d5be4..c28c4c2a 100644 --- a/packages/configuration/test/fixtures/fileManager.fixture.ts +++ b/packages/configuration/test/fixtures/fileManager.fixture.ts @@ -10,6 +10,11 @@ export default class TestFileManager this.#files = files; } + getAbsoluteLocation(filename: string): string + { + return filename; + } + exists(filename: string): Promise { for (const key in this.#files) diff --git a/packages/configuration/test/runtime/ConfigurationBuilder.spec.ts b/packages/configuration/test/runtime/ConfigurationBuilder.spec.ts index 07ce6f9b..505e529b 100644 --- a/packages/configuration/test/runtime/ConfigurationBuilder.spec.ts +++ b/packages/configuration/test/runtime/ConfigurationBuilder.spec.ts @@ -9,21 +9,21 @@ describe('runtime/ConfigurationBuilder', () => { const promise = configurationBuilder.build(); - await expect(promise).resolves.toEqual(CONFIGURATIONS.DEFAULT); + await expect(promise).resolves.toEqual(CONFIGURATIONS.RESULT.DEFAULT); }); it('should build a valid runtime configuration from a valid file', async () => { const promise = configurationBuilder.build(FILENAMES.VALID); - await expect(promise).resolves.toEqual(CONFIGURATIONS.RUNTIME); + await expect(promise).resolves.toEqual(CONFIGURATIONS.RESULT.RUNTIME); }); it('should build a default runtime when the configuration file does not exist', async () => { const promise = configurationBuilder.build(FILENAMES.MISSING); - await expect(promise).resolves.toEqual(CONFIGURATIONS.DEFAULT); + await expect(promise).resolves.toEqual(CONFIGURATIONS.RESULT.MISSING); }); it('should reject an invalid runtime configuration', async () => diff --git a/packages/configuration/test/runtime/fixtures/configuration.fixture.ts b/packages/configuration/test/runtime/fixtures/configuration.fixture.ts index 450fdc61..931c8e58 100644 --- a/packages/configuration/test/runtime/fixtures/configuration.fixture.ts +++ b/packages/configuration/test/runtime/fixtures/configuration.fixture.ts @@ -1,30 +1,78 @@ import { RuntimeConfiguration } from '../../../src/runtime'; -const defaultConfiguration: RuntimeConfiguration = +const defaultInput: Record = +{ + source: './src', + target: './dist', + segments: './segments', + resources: './resources' +} as const; + +const defaultResult: RuntimeConfiguration = { source: './src', target: './dist', segments: './segments', resources: './resources', + meta: + { + configFile: './jitar.json', + root: '.' + } } as const; -const runtimeConfiguration: RuntimeConfiguration = +const runtimeInput: Record = +{ + source: './source', + target: './target', + segments: './segment', + resources: './resource' +} as const; + +const runtimeResult: RuntimeConfiguration = { source: './source', target: './target', segments: './segment', resources: './resource', + meta: + { + configFile: 'valid-runtime-configuration.json', + root: '.' + } +} as const; + +const missingResult: RuntimeConfiguration = +{ + source: './src', + target: './dist', + segments: './segments', + resources: './resources', + meta: + { + configFile: 'missing-runtime-configuration.json', + root: '.' + } } as const; -const invalidConfiguration: any = +const invalidInput: Record = { - invalid: true + invalid: 'true' } as const; -export const CONFIGURATIONS: Record = +export const CONFIGURATIONS = { - DEFAULT: defaultConfiguration, - RUNTIME: runtimeConfiguration, - INVALID: invalidConfiguration, + INPUT: + { + DEFAULT: defaultInput, + RUNTIME: runtimeInput, + INVALID: invalidInput + }, + RESULT: + { + DEFAULT: defaultResult, + RUNTIME: runtimeResult, + MISSING: missingResult + } } as const; diff --git a/packages/configuration/test/runtime/fixtures/files.fixture.ts b/packages/configuration/test/runtime/fixtures/files.fixture.ts index e1de9d57..39f60a3d 100644 --- a/packages/configuration/test/runtime/fixtures/files.fixture.ts +++ b/packages/configuration/test/runtime/fixtures/files.fixture.ts @@ -6,7 +6,7 @@ import { FILENAMES } from './filenames.fixture'; export const FILES: Record = { - DEFAULT: new File(FILENAMES.DEFAULT, 'text/json', JSON.stringify(CONFIGURATIONS.DEFAULT)), - VALID: new File(FILENAMES.VALID, 'text/json', JSON.stringify(CONFIGURATIONS.RUNTIME)), - INVALID: new File(FILENAMES.INVALID, 'text/json', JSON.stringify(CONFIGURATIONS.INVALID)) + DEFAULT: new File(FILENAMES.DEFAULT, 'text/json', JSON.stringify(CONFIGURATIONS.INPUT.DEFAULT)), + VALID: new File(FILENAMES.VALID, 'text/json', JSON.stringify(CONFIGURATIONS.INPUT.RUNTIME)), + INVALID: new File(FILENAMES.INVALID, 'text/json', JSON.stringify(CONFIGURATIONS.INPUT.INVALID)) } as const; diff --git a/packages/configuration/test/server/ConfigurationBuilder.spec.ts b/packages/configuration/test/server/ConfigurationBuilder.spec.ts index 396d87b9..ea6d392f 100644 --- a/packages/configuration/test/server/ConfigurationBuilder.spec.ts +++ b/packages/configuration/test/server/ConfigurationBuilder.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { configurationBuilder, FILENAMES, SERVER_CONFIGURATION, ServerConfigurationInvalid, VALIDATION_RESULT } from './fixtures'; +import { configurationBuilder, FILENAMES, CONFIGURATIONS, ServerConfigurationInvalid, VALIDATION_RESULT } from './fixtures'; describe('server/ConfigurationBuilder', () => { @@ -9,7 +9,7 @@ describe('server/ConfigurationBuilder', () => { const promise = configurationBuilder.build(FILENAMES.VALID_CONFIGURATION); - await expect(promise).resolves.toEqual(SERVER_CONFIGURATION); + await expect(promise).resolves.toEqual(CONFIGURATIONS.RESULT.VALID); }); it('should reject an invalid server configuration', async () => diff --git a/packages/configuration/test/server/fixtures/configuration.fixture.ts b/packages/configuration/test/server/fixtures/configuration.fixture.ts index 326819bb..b764b86a 100644 --- a/packages/configuration/test/server/fixtures/configuration.fixture.ts +++ b/packages/configuration/test/server/fixtures/configuration.fixture.ts @@ -16,7 +16,7 @@ const standaloneConfiguration: StandaloneConfiguration = { segments, indexFilena const workerConfiguration: WorkerConfiguration = { gateway, segments, trustKey } as const; const remoteWorkerConfiguration: RemoteWorkerConfiguration = { unavailableThreshold: 6000, stoppedThreshold: 18000 } as const; -export const SERVER_CONFIGURATION: ServerConfiguration = +const validInput: Record = { url: 'https://server', setUp: ['setup'], @@ -31,3 +31,37 @@ export const SERVER_CONFIGURATION: ServerConfiguration = worker: workerConfiguration, remoteWorker: remoteWorkerConfiguration } as const; + +const validResult: ServerConfiguration = +{ + url: 'https://server', + setUp: ['setup'], + tearDown: ['tearDown'], + middleware: ['middleware'], + healthChecks: ['healthChecks'], + + gateway: gatewayConfiguration, + proxy: proxyConfiguration, + repository: repositoryConfiguration, + standalone: standaloneConfiguration, + worker: workerConfiguration, + remoteWorker: remoteWorkerConfiguration, + + meta: + { + configFile: 'valid-configuration.json', + root: '.' + } +} as const; + +export const CONFIGURATIONS = +{ + INPUT: + { + VALID: validInput, + }, + RESULT: + { + VALID: validResult + } +} as const; diff --git a/packages/configuration/test/server/fixtures/files.fixture.ts b/packages/configuration/test/server/fixtures/files.fixture.ts index 68f5297a..8064a6d0 100644 --- a/packages/configuration/test/server/fixtures/files.fixture.ts +++ b/packages/configuration/test/server/fixtures/files.fixture.ts @@ -1,11 +1,11 @@ import { File } from '@jitar/sourcing'; -import { SERVER_CONFIGURATION } from './configuration.fixture'; +import { CONFIGURATIONS } from './configuration.fixture'; import { FILENAMES } from './filenames.fixture'; export const FILES: Record = { - VALID_CONFIGURATION: new File(FILENAMES.VALID_CONFIGURATION, 'text/json', JSON.stringify(SERVER_CONFIGURATION)), + VALID_CONFIGURATION: new File(FILENAMES.VALID_CONFIGURATION, 'text/json', JSON.stringify(CONFIGURATIONS.INPUT.VALID)), INVALID_CONFIGURATION: new File(FILENAMES.INVALID_CONFIGURATION, 'text/json', JSON.stringify({})), } as const; diff --git a/packages/configuration/test/server/fixtures/index.ts b/packages/configuration/test/server/fixtures/index.ts index def4196c..3203c3f0 100644 --- a/packages/configuration/test/server/fixtures/index.ts +++ b/packages/configuration/test/server/fixtures/index.ts @@ -8,7 +8,7 @@ import { FileManager } from '../../fixtures'; import { FILES } from './files.fixture'; import { FILENAMES } from './filenames.fixture'; -import { SERVER_CONFIGURATION } from './configuration.fixture'; +import { CONFIGURATIONS } from './configuration.fixture'; import { VALIDATION_RESULT } from './validation.fixture'; const fileManager = new FileManager(FILES); @@ -17,4 +17,4 @@ const validator = new Validator(); const configurationBuilder = new ServerConfigurationBuilder(configurationReader, validator); -export { configurationBuilder, SERVER_CONFIGURATION, ServerConfigurationInvalid, VALIDATION_RESULT, FILENAMES }; +export { configurationBuilder, CONFIGURATIONS, ServerConfigurationInvalid, VALIDATION_RESULT, FILENAMES }; diff --git a/packages/configuration/tsconfig.json b/packages/configuration/tsconfig.json index e66fac12..db3953f7 100644 --- a/packages/configuration/tsconfig.json +++ b/packages/configuration/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/create-jitar/tsconfig.json b/packages/create-jitar/tsconfig.json index d578cb1a..db3953f7 100644 --- a/packages/create-jitar/tsconfig.json +++ b/packages/create-jitar/tsconfig.json @@ -1,22 +1,9 @@ { - "compilerOptions": { - "outDir": "dist", - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "Node", - "strict": true, - "skipLibCheck": true, - "declaration": false, - "sourceMap": false, - "noUnusedLocals": true, - "esModuleInterop": true, - "removeComments": true - }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "templates" - ] + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" + }, + "include": ["./src"] } \ No newline at end of file diff --git a/packages/errors/tsconfig.json b/packages/errors/tsconfig.json index e66fac12..db3953f7 100644 --- a/packages/errors/tsconfig.json +++ b/packages/errors/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/execution/tsconfig.json b/packages/execution/tsconfig.json index e66fac12..db3953f7 100644 --- a/packages/execution/tsconfig.json +++ b/packages/execution/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/health/tsconfig.json b/packages/health/tsconfig.json index e66fac12..db3953f7 100644 --- a/packages/health/tsconfig.json +++ b/packages/health/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/http/tsconfig.json b/packages/http/tsconfig.json index e66fac12..db3953f7 100644 --- a/packages/http/tsconfig.json +++ b/packages/http/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/init/tsconfig.json b/packages/init/tsconfig.json index d7267a2c..db3953f7 100644 --- a/packages/init/tsconfig.json +++ b/packages/init/tsconfig.json @@ -1,22 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "templates", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/jitar/src/lib.ts b/packages/jitar/src/lib.ts index 032953a8..5454f198 100644 --- a/packages/jitar/src/lib.ts +++ b/packages/jitar/src/lib.ts @@ -1,4 +1,6 @@ +export { BuildHelper } from '@jitar/build'; +export { ConfigurationManager } from '@jitar/configuration'; export { BadRequest, Forbidden, NotFound, NotImplemented, PaymentRequired, ServerError, Teapot, Unauthorized } from '@jitar/errors'; export { Request, Response, Segment, Class, Procedure, Implementation, Version, NamedParameter, ArrayParameter, ObjectParameter } from '@jitar/execution'; export { Middleware, NextHandler } from '@jitar/middleware'; diff --git a/packages/jitar/tsconfig.json b/packages/jitar/tsconfig.json index 0452487c..974a01e7 100644 --- a/packages/jitar/tsconfig.json +++ b/packages/jitar/tsconfig.json @@ -1,22 +1,10 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "declaration": true, + "extends": "../../tsconfig.json", + "compilerOptions": + { "declarationDir": "./dist/types", - "rootDir": "./src/", - "moduleResolution": "node", - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "node_modules", - "test", - "dist", - "vite.config.ts" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/logging/tsconfig.json b/packages/logging/tsconfig.json index e66fac12..db3953f7 100644 --- a/packages/logging/tsconfig.json +++ b/packages/logging/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/middleware/tsconfig.json b/packages/middleware/tsconfig.json index e66fac12..db3953f7 100644 --- a/packages/middleware/tsconfig.json +++ b/packages/middleware/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/plugin-vite/package.json b/packages/plugin-vite/package.json index 0059d348..e0b4a5a3 100644 --- a/packages/plugin-vite/package.json +++ b/packages/plugin-vite/package.json @@ -31,6 +31,7 @@ "vite": "^7.3.0" }, "peerDependencies": { + "jitar":"^0.10.7", "vite": ">=4.0.0 || >=5.0.0 || >=6.0.0 || >=7.0.0" }, "peerDependenciesMeta": { diff --git a/packages/plugin-vite/src/index.ts b/packages/plugin-vite/src/index.ts index 25981281..da7d6f03 100644 --- a/packages/plugin-vite/src/index.ts +++ b/packages/plugin-vite/src/index.ts @@ -2,29 +2,13 @@ import fs from 'node:fs'; import path from 'node:path'; +import { ConfigurationManager, BuildHelper } from 'jitar'; import { normalizePath, PluginOption, ResolvedConfig } from 'vite'; const JITAR_SOURCE_ID = 'jitar'; const JITAR_CLIENT_ID = 'jitar/client'; const JITAR_BUNDLE_ID = 'jitar-bundle'; -function formatDir(dir: string) -{ - dir = normalizePath(dir); - - if (dir.startsWith('/')) - { - dir = dir.substring(1); - } - - if (dir.endsWith('/')) - { - dir = dir.substring(0, dir.length - 1); - } - - return dir; -} - function assureExtension(filename: string) { if (filename.endsWith('.js')) @@ -35,33 +19,74 @@ function assureExtension(filename: string) return `${filename}.js`; } +function createJitarBundle(middlewares: string[], targetPath: string) +{ + const middlewareFiles = middlewares.map(name => assureExtension(`${targetPath}/${name}`)); + + const jitarImport = `import { ClientBuilder, HttpRemoteBuilder } from "${JITAR_CLIENT_ID}";`; + const middlewareImports = middlewareFiles.map((filename, index) => `import { default as $M${index} } from "${filename}";`).join(''); + const imports = [jitarImport, middlewareImports].join('\n'); + + const remoteUrl = 'const remoteUrl = document.location.origin;'; + const segmentsArray = `const segments = [];`; + const middlewareItems = middlewares.map((_, index) => `$M${index}`).join(', '); + const middlewareArray = `const middleware = [${middlewareItems}];`; + const declarations = [remoteUrl, segmentsArray, middlewareArray].join('\n'); + + const remoteBuilder = 'const remoteBuilder = new HttpRemoteBuilder();'; + const clientBuilder = 'const clientBuilder = new ClientBuilder(remoteBuilder);'; + const build = 'const client = clientBuilder.build({remoteUrl, segments, middleware});'; + const start = 'client.start();'; + const client = [remoteBuilder, clientBuilder, build, start].join('\n'); + + const exports = `export * from "${JITAR_CLIENT_ID}";`; + + return [imports, declarations, client, exports].join('\n'); +} + type PluginConfig = { - sourceDir: string; - targetDir: string; - jitarDir: string; + projectRoot: string; + sourceRoot: string; + configurationFile?: string; + environmentFile?: string; jitarUrl: string; segments?: string[]; middleware?: string[]; }; +type PluginPaths = { + vite: { + input?: string; + output?: string; + assetOutput?: string; + }; + project: { + root?: string; + source?: string; + }; + jitar: { + input?: string; + output?: string; + }; +}; + export type { PluginConfig as JitarConfig }; export default function viteJitar(pluginConfig: PluginConfig): PluginOption { - const sourceDir = formatDir(pluginConfig.sourceDir); - const targetDir = formatDir(pluginConfig.targetDir); - const jitarDir = formatDir(pluginConfig.jitarDir); const jitarUrl = pluginConfig.jitarUrl; const segments = pluginConfig.segments ?? []; const middlewares = pluginConfig.middleware ?? []; - const scopes = [ ...segments, 'remote']; + const paths: PluginPaths = + { + vite: { input: undefined, output: undefined, assetOutput: undefined }, + project: { root: undefined, source: undefined }, + jitar: { input: undefined, output: undefined } + }; + + let buildHelper: BuildHelper; - let rootPath: string | undefined; - let sourcePath: string | undefined; - let targetPath: string | undefined; - let outputPath: string | undefined; - let jitarPath: string | undefined; let jitarBundleFilename: string | undefined; let jitarBundleImported = false; @@ -79,11 +104,29 @@ export default function viteJitar(pluginConfig: PluginConfig): PluginOption configResolved(resolvedConfig: ResolvedConfig) { - rootPath = normalizePath(path.join(resolvedConfig.root)); - sourcePath = normalizePath(path.join(rootPath, sourceDir)); - targetPath = normalizePath(path.join(rootPath, targetDir)); - outputPath = normalizePath(path.join(targetPath, resolvedConfig.build.assetsDir)); - jitarPath = normalizePath(path.join(sourcePath, jitarDir)); + paths.vite.input = normalizePath(path.join(resolvedConfig.root)); + paths.vite.output = normalizePath(path.join(paths.vite.input, resolvedConfig.build.outDir)); + paths.vite.assetOutput = normalizePath(path.join(paths.vite.input, resolvedConfig.build.assetsDir)); + + paths.project.root = normalizePath(path.join(paths.vite.input, pluginConfig.projectRoot)); + paths.project.source = normalizePath(path.join(paths.vite.input, pluginConfig.sourceRoot)); + }, + + async buildStart() + { + const configurationManager = new ConfigurationManager(paths.project.root); + + if (pluginConfig.environmentFile !== undefined) + { + await configurationManager.configureEnvironment(pluginConfig.environmentFile); + } + + const configuration = await configurationManager.getRuntimeConfiguration(pluginConfig.configurationFile); + paths.jitar.input = normalizePath(path.join(paths.project.root!, configuration.source)); + paths.jitar.output = normalizePath(path.join(paths.project.root!, configuration.target)); + + buildHelper = new BuildHelper(configuration); + await buildHelper.readApplication(); }, options(options) @@ -113,7 +156,7 @@ export default function viteJitar(pluginConfig: PluginConfig): PluginOption resolveId: { order: 'pre', - async handler(source: string, importer: string | undefined, options: object) + async handler(source: string, importer: string | undefined) { if (source === JITAR_BUNDLE_ID) { @@ -135,76 +178,45 @@ export default function viteJitar(pluginConfig: PluginConfig): PluginOption return JITAR_BUNDLE_ID; } + } + }, - const resolution = await this.resolve(source, importer, options); + load(id) + { + if (id === JITAR_BUNDLE_ID) + { + return createJitarBundle(middlewares, paths.vite.output!); + } - if (resolution === null || jitarPath === undefined || resolution.id.includes(jitarPath) === false) + if (id.startsWith(paths.project.source!)) + { + if (id.startsWith(paths.vite.input!)) { return null; } - const cacheId = resolution.id.replace(sourcePath!, targetPath!); - - // First check if the module is a scoped module (segmented file) - - for (const scope of scopes) + const relativeId = id + .replace(paths.project.source!, '') + .replace('.ts', '.js'); + + if (relativeId.endsWith('.js')) { - const scopeId = cacheId.replace('.ts', `.${scope}.js`); - - if (fs.existsSync(scopeId)) + try { - return scopeId; + return buildHelper.generateModuleCode(relativeId, segments); } - } - - // Next, check if the module is a common module (unscoped file) - - const scopeId = cacheId.replace('.ts', '.js'); + catch (error) + { + const message = error instanceof Error ? error.message : String(error); - if (fs.existsSync(scopeId)) - { - return scopeId; + console.error('ERROR:', message); + + return null; + } } - - // It's doesn't seem to be a cached file, so we can return the original resolution id - - return resolution.id; } - }, - - load(id) - { - // Create the jitar client bundle content - if (id !== JITAR_BUNDLE_ID) - { - return null; - } - - const segmentFiles = segments.map(name => `${targetPath}/${name}.segment.js`); - const middlewareFiles = middlewares.map(name => assureExtension(`${targetPath}/${name}`)); - - const jitarImport = `import { ClientBuilder, HttpRemoteBuilder } from "${JITAR_CLIENT_ID}";`; - const segmentImports = segmentFiles.map((filename, index) => `import { default as $S${index} } from "${filename}";`).join(''); - const middlewareImports = middlewareFiles.map((filename, index) => `import { default as $M${index} } from "${filename}";`).join(''); - const imports = [jitarImport, segmentImports, middlewareImports].join('\n'); - - const remoteUrl = 'const remoteUrl = document.location.origin;'; - const segmentsItems = segments.map((_, index) => `$S${index}`).join(', '); - const segmentsArray = `const segments = [${segmentsItems}];`; - const middlewareItems = middlewares.map((_, index) => `$M${index}`).join(', '); - const middlewareArray = `const middleware = [${middlewareItems}];`; - const declarations = [remoteUrl, segmentsArray, middlewareArray].join('\n'); - - const remoteBuilder = 'const remoteBuilder = new HttpRemoteBuilder();'; - const clientBuilder = 'const clientBuilder = new ClientBuilder(remoteBuilder);'; - const build = 'const client = clientBuilder.build({remoteUrl, segments, middleware});'; - const start = 'client.start();'; - const client = [remoteBuilder, clientBuilder, build, start].join('\n'); - - const exports = `export * from "${JITAR_CLIENT_ID}";`; - - return [imports, declarations, client, exports].join('\n'); + return null; }, generateBundle(options, bundle) @@ -228,7 +240,7 @@ export default function viteJitar(pluginConfig: PluginConfig): PluginOption { // Add the jitar client bundle to the HTML if it wasn't imported // by any of the application files. - + if (jitarBundleImported === true) { return html; @@ -238,14 +250,14 @@ export default function viteJitar(pluginConfig: PluginConfig): PluginOption { // Dev mode: insert the pre generated jitar bundle - if (outputPath === undefined) + if (paths.vite.assetOutput === undefined) { console.warn('Output path not found!'); return html; } - const filenames = fs.readdirSync(outputPath); + const filenames = fs.readdirSync(paths.vite.assetOutput); const jitarFilename = filenames.find(fileName => fileName.startsWith(JITAR_BUNDLE_ID) && fileName.endsWith('.js')); @@ -256,7 +268,7 @@ export default function viteJitar(pluginConfig: PluginConfig): PluginOption return html; } - const jitarBundle = fs.readFileSync(path.join(outputPath, jitarFilename), 'utf-8'); + const jitarBundle = fs.readFileSync(path.join(paths.vite.assetOutput, jitarFilename), 'utf-8'); return html.replace('${jitarBundle}\n parameter.name); + return parameters.map(parameter => (parameter.binding as ESIdentifierBinding).identifier); } async #serializeConstructor(model: ESClass, includeNames: string[], object: object): Promise @@ -96,7 +96,7 @@ export default class ClassSerializer extends ValueSerializer for (const property of model.writable) { - const name = property.name; + const name = property.identifier!; if (excludeNames.includes(name) || model.canRead(name) === false) { @@ -141,8 +141,8 @@ export default class ClassSerializer extends ValueSerializer async #deserializeConstructor(clazz: Function, args: SerializableObject): Promise { const model = reflector.fromClass(clazz, true); - const constructor = model.getFunction('constructor'); - const parameters = (constructor?.parameters ?? []) as ESField[]; + const constructor = model.construct; + const parameters = (constructor?.parameters ?? []) as ESParameter[]; const values = parameters.map((_, index) => { diff --git a/packages/serialization/tsconfig.json b/packages/serialization/tsconfig.json index 8a1cb98d..db3953f7 100644 --- a/packages/serialization/tsconfig.json +++ b/packages/serialization/tsconfig.json @@ -1,20 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/services/tsconfig.json b/packages/services/tsconfig.json index e66fac12..db3953f7 100644 --- a/packages/services/tsconfig.json +++ b/packages/services/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/sourcing/src/files/index.ts b/packages/sourcing/src/files/index.ts index 7b3dd4a0..4589eaa1 100644 --- a/packages/sourcing/src/files/index.ts +++ b/packages/sourcing/src/files/index.ts @@ -5,6 +5,7 @@ export { default as InvalidLocation } from './errors/InvalidLocation'; export { default as FileNotFound } from './errors/FileNotFound'; export type { default as FileReader } from './interfaces/FileReader'; +export type { default as FileSystem } from './interfaces/FileSystem'; export { default as File } from './models/File'; diff --git a/packages/sourcing/src/index.ts b/packages/sourcing/src/index.ts index 0bd3943e..aac6ea53 100644 --- a/packages/sourcing/src/index.ts +++ b/packages/sourcing/src/index.ts @@ -1,5 +1,5 @@ -export type { FileReader } from './files'; +export type { FileReader, FileSystem } from './files'; export { Files, File, FileManager, LocalFileManager, RemoteFileManager, InvalidLocation, FileNotFound } from './files'; export type { Module, ModuleImporter } from './modules'; diff --git a/packages/sourcing/tsconfig.json b/packages/sourcing/tsconfig.json index 4ca2247d..db3953f7 100644 --- a/packages/sourcing/tsconfig.json +++ b/packages/sourcing/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "es2022", - "module": "es2022", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/packages/validation/tsconfig.json b/packages/validation/tsconfig.json index e66fac12..db3953f7 100644 --- a/packages/validation/tsconfig.json +++ b/packages/validation/tsconfig.json @@ -1,21 +1,9 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "rootDir": "./src/", - "moduleResolution": "node", - "declaration": true, - "outDir": "./dist", - "removeComments": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true + "extends": "../../tsconfig.json", + "compilerOptions": + { + "rootDir": "./src", + "outDir": "./dist" }, - "exclude": [ - "vite.config.ts", - "node_modules", - "dist", - "test" - ] + "include": ["./src"] } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..c7628688 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "types": ["node"], + "target": "esnext", + "module": "esnext", + "moduleResolution": "bundler", + "declaration": true, + "removeComments": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "exclude": [ + "vite.config.ts", + "node_modules", + "dist", + "test" + ] +} \ No newline at end of file