├── analyzer_playground ├── .gitignore ├── vercel.json ├── api │ └── index.ts ├── web.js └── index.html ├── analyzer_deno ├── tests │ ├── trees │ │ ├── tree_1 │ │ │ ├── file_6.ts │ │ │ ├── file_2.ts │ │ │ ├── file_4.ts │ │ │ ├── file_5.ts │ │ │ ├── file_1.ts │ │ │ └── file_3.ts │ │ ├── tree_2 │ │ │ ├── file_1.ts │ │ │ ├── file_3.ts │ │ │ └── file_4.ts │ │ ├── tree_1.ts │ │ └── tree_2.ts │ ├── deps.ts │ ├── toFileURL_test.ts │ └── dep_tree_test.ts ├── wasm.ts └── tree.ts ├── .rustfmt.toml ├── Cargo.toml ├── .gitignore ├── diagrams ├── analyzer.png ├── analyzer.svg └── analyzer.excalidraw ├── mod.ts ├── analyzer_cli ├── deps.ts └── mod.ts ├── analyzer_runtime ├── rules.ts ├── tests │ ├── deps.ts │ ├── deno_plugin_test.ts │ └── deno_run_test.ts ├── mod.ts └── example.ts ├── analyzer_wasm ├── pkg │ ├── nest_analyzer_wasm_bg.wasm │ ├── nest_analyzer_wasm_bg.d.ts │ ├── package.json │ ├── nest_analyzer_wasm.d.ts │ └── nest_analyzer_wasm.js ├── tests │ ├── deps.ts │ └── tree_test.ts ├── package.json ├── src │ └── lib.rs ├── Cargo.toml ├── scripts │ └── build.ts └── Cargo.lock ├── analyzer_tree ├── lib.rs ├── tools.rs ├── Cargo.toml ├── tree.rs ├── parser.rs ├── Cargo.lock └── scopes.rs ├── examples └── deno │ └── mod.ts ├── egg.json ├── tools ├── lint.ts ├── obfuscator.ts └── format.ts ├── LICENSE ├── .github └── workflows │ └── ci.yml ├── README.md └── Cargo.lock /analyzer_playground/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_1/file_6.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | tab_spaces = 2 3 | edition = "2018" -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "analyzer_tree" 5 | ] 6 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_1/file_2.ts: -------------------------------------------------------------------------------- 1 | import {} from "./file_4.ts"; 2 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_1/file_4.ts: -------------------------------------------------------------------------------- 1 | import {} from "./file_6.ts"; 2 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_1/file_5.ts: -------------------------------------------------------------------------------- 1 | import {} from "./file_6.ts"; 2 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_2/file_1.ts: -------------------------------------------------------------------------------- 1 | import {} from "./file_3.ts"; 2 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_2/file_3.ts: -------------------------------------------------------------------------------- 1 | import {} from "./file_4.ts"; 2 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_2/file_4.ts: -------------------------------------------------------------------------------- 1 | import {} from "./file_1.ts"; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | 4 | eggs-debug.log 5 | 6 | target/ 7 | 8 | .vercel 9 | .now -------------------------------------------------------------------------------- /diagrams/analyzer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nestdotland/analyzer/HEAD/diagrams/analyzer.png -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | export * from "./analyzer_deno/tree.ts"; 2 | export * from "./analyzer_deno/wasm.ts"; 3 | -------------------------------------------------------------------------------- /analyzer_cli/deps.ts: -------------------------------------------------------------------------------- 1 | export { 2 | Command, 3 | } from "https://x.nest.land/cliffy@0.14.1/command/mod.ts"; 4 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_1/file_1.ts: -------------------------------------------------------------------------------- 1 | import {} from "./file_2.ts"; 2 | import {} from "./file_3.ts"; 3 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_1/file_3.ts: -------------------------------------------------------------------------------- 1 | import {} from "./file_4.ts"; 2 | import {} from "./file_5.ts"; 3 | -------------------------------------------------------------------------------- /analyzer_runtime/rules.ts: -------------------------------------------------------------------------------- 1 | export const FnRules: Function[] = [ 2 | Deno.run, 3 | Deno.openPlugin, 4 | ]; 5 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_1.ts: -------------------------------------------------------------------------------- 1 | import {} from "./tree_1/file_1.ts"; 2 | import {} from "./tree_1/file_6.ts"; 3 | -------------------------------------------------------------------------------- /analyzer_deno/tests/trees/tree_2.ts: -------------------------------------------------------------------------------- 1 | import {} from "./tree_2/file_1.ts"; 2 | import {} from "./tree_2/file_2.ts"; 3 | -------------------------------------------------------------------------------- /analyzer_runtime/tests/deps.ts: -------------------------------------------------------------------------------- 1 | export { Rhum as Test } from "https://deno.land/x/rhum@v1.1.2/mod.ts"; 2 | export { Analyze } from "../mod.ts"; 3 | -------------------------------------------------------------------------------- /analyzer_wasm/pkg/nest_analyzer_wasm_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nestdotland/analyzer/HEAD/analyzer_wasm/pkg/nest_analyzer_wasm_bg.wasm -------------------------------------------------------------------------------- /analyzer_wasm/tests/deps.ts: -------------------------------------------------------------------------------- 1 | export { Rhum as Test } from "https://deno.land/x/rhum@v1.1.2/mod.ts"; 2 | export { tree as Tree } from "../../mod.ts"; 3 | -------------------------------------------------------------------------------- /analyzer_playground/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": { 3 | "api/**/*.[jt]s": { "runtime": "now-deno@0.4.0" } 4 | }, 5 | "env": { 6 | "DENO_UNSTABLE": "true" 7 | } 8 | } -------------------------------------------------------------------------------- /analyzer_tree/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 nest.land authors and friends. 2 | #![feature(box_patterns)] 3 | 4 | mod parser; 5 | mod scopes; 6 | mod tools; 7 | 8 | pub mod tree; 9 | -------------------------------------------------------------------------------- /analyzer_tree/tools.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 nest.land core team. 2 | 3 | #[macro_export] 4 | macro_rules! unwrap_or_return { 5 | ( $e:expr ) => { 6 | match $e { 7 | Some(x) => x, 8 | None => return, 9 | } 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /analyzer_wasm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nest_analyzer", 3 | "version": "1.0.0", 4 | "main": "wasm.js", 5 | "author": "Divy Srivastava", 6 | "license": "MIT", 7 | "private": false, 8 | "type": "module" 9 | } 10 | -------------------------------------------------------------------------------- /analyzer_deno/tests/deps.ts: -------------------------------------------------------------------------------- 1 | export { Rhum as Test } from "https://deno.land/x/rhum@v1.1.2/mod.ts"; 2 | export * from "https://x.nest.land/std@0.66.0/testing/asserts.ts"; 3 | export * as path from "https://x.nest.land/std@0.66.0/path/mod.ts"; 4 | export * from "../tree.ts"; 5 | -------------------------------------------------------------------------------- /examples/deno/mod.ts: -------------------------------------------------------------------------------- 1 | import { analyze } from "../../mod.ts"; 2 | 3 | let diagnostics = await analyze( 4 | ` 5 | Deno.run({ cmd: "echo 2".split(" ")}); 6 | console.log("Something else"); 7 | `, 8 | ); 9 | 10 | console.log("===Diagnostics==="); 11 | console.log(diagnostics); 12 | -------------------------------------------------------------------------------- /analyzer_wasm/pkg/nest_analyzer_wasm_bg.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export const memory: WebAssembly.Memory; 4 | export function tree(a: number, b: number, c: number, d: number): number; 5 | export function __wbindgen_malloc(a: number): number; 6 | export function __wbindgen_realloc(a: number, b: number, c: number): number; 7 | export function __wbindgen_free(a: number, b: number): void; 8 | -------------------------------------------------------------------------------- /analyzer_wasm/tests/tree_test.ts: -------------------------------------------------------------------------------- 1 | import { Test, Tree } from "./deps.ts"; 2 | 3 | Test.testPlan("tree_test.ts", () => { 4 | Test.testSuite("static_analyze", () => { 5 | Test.testCase("basic imports", async () => { 6 | let deps = Tree("test.ts", `import * as x from "x.ts";`); 7 | Test.asserts.assertEquals(deps, [ 8 | "x.ts", 9 | ]); 10 | }); 11 | }); 12 | }); 13 | 14 | Test.run(); 15 | -------------------------------------------------------------------------------- /egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "analyzer", 3 | "description": "Analyze and detect broken and malicious JavaScript and TypeScript modules.", 4 | "stable": true, 5 | "version": "0.0.6", 6 | "repository": "https://github.com/nestdotland/analyzer", 7 | "files": [ 8 | "./analyzer_deno/**/*", 9 | "./analyzer_wasm/wasm.js", 10 | "./mod.ts", 11 | "./analyzer_runtime/**/*", 12 | "./README.md" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /analyzer_wasm/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nest_analyzer_wasm", 3 | "collaborators": [ 4 | "Divy Srivastava" 5 | ], 6 | "description": "WASM module for the nest_analyzer", 7 | "version": "0.1.0", 8 | "files": [ 9 | "nest_analyzer_wasm_bg.wasm", 10 | "nest_analyzer_wasm.js", 11 | "nest_analyzer_wasm.d.ts" 12 | ], 13 | "module": "nest_analyzer_wasm.js", 14 | "types": "nest_analyzer_wasm.d.ts", 15 | "sideEffects": false 16 | } -------------------------------------------------------------------------------- /analyzer_cli/mod.ts: -------------------------------------------------------------------------------- 1 | import { analyze } from "../mod.ts"; 2 | import { Command } from "./deps.ts"; 3 | 4 | const result = await new Command() 5 | .arguments("") 6 | .parse(Deno.args); 7 | 8 | const input: string = result.args[0]; 9 | 10 | const src: string = await Deno.readTextFile(input); 11 | console.log(src); 12 | let diagnostics = await analyze( 13 | src, 14 | ); 15 | 16 | console.log("===Diagnostics==="); 17 | console.log(diagnostics); 18 | -------------------------------------------------------------------------------- /tools/lint.ts: -------------------------------------------------------------------------------- 1 | const release = Deno.args.includes("--release"); 2 | console.log("clippy"); 3 | 4 | const mode = release ? ["--release"] : []; 5 | const clippy = [ 6 | "cargo", 7 | "clippy", 8 | "--all-targets", 9 | ...mode, 10 | "--locked", 11 | "--", 12 | "-D", 13 | "clippy::all", 14 | "-A", 15 | "clippy::cmp-owned", 16 | ]; 17 | 18 | let s1 = await Deno.run({ 19 | cmd: clippy, 20 | stdin: "null", 21 | }).status(); 22 | 23 | if (s1.code !== 0) { 24 | throw new Error(`Failed: ${clippy.join(" ")}`); 25 | } 26 | -------------------------------------------------------------------------------- /tools/obfuscator.ts: -------------------------------------------------------------------------------- 1 | import "https://cdn.jsdelivr.net/npm/javascript-obfuscator/dist/index.browser.js"; 2 | 3 | export function obfuscator(code: string = Deno.args[0]) { 4 | // @ts-ignore 5 | let obfuscationResult = window.JavaScriptObfuscator.obfuscate(code, { 6 | compact: false, 7 | controlFlowFlattening: true, 8 | numbersToExpressions: true, 9 | simplify: true, 10 | shuffleStringArray: true, 11 | splitStrings: true, 12 | }).getObfuscatedCode(); 13 | console.log(obfuscationResult); 14 | } 15 | 16 | obfuscator(); 17 | -------------------------------------------------------------------------------- /analyzer_wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | use console_error_panic_hook::hook; 2 | use js_sys::Array; 3 | use std::panic; 4 | use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; 5 | 6 | use analyzer_tree::tree; 7 | 8 | #[cfg(feature = "wee_alloc")] 9 | #[global_allocator] 10 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 11 | 12 | #[wasm_bindgen] 13 | pub fn tree(filename: String, src: String) -> Array { 14 | panic::set_hook(Box::new(hook)); 15 | tree::analyze_dependencies(&filename, &src, true) 16 | .unwrap() 17 | .into_iter() 18 | .map(JsValue::from) 19 | .collect() 20 | } 21 | -------------------------------------------------------------------------------- /analyzer_wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "nest_analyzer_wasm" 5 | description = "WASM module for the nest_analyzer" 6 | version = "0.1.0" 7 | authors = ["Divy Srivastava"] 8 | edition = "2018" 9 | publish = false 10 | 11 | [lib] 12 | crate-type = ["cdylib"] 13 | 14 | [dependencies] 15 | wasm-bindgen = { version = "0.2.64", features = ["serde-serialize"] } 16 | wee_alloc = { version = "0.4.5", optional = true } 17 | analyzer_tree = { path = "../analyzer_tree" } 18 | js-sys = "0.3.44" 19 | console_error_panic_hook = "0.1.6" 20 | 21 | [profile.release] 22 | lto = true 23 | opt-level = "z" 24 | 25 | [features] 26 | default = ["wee_alloc"] 27 | -------------------------------------------------------------------------------- /analyzer_playground/api/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | APIGatewayProxyEvent, 3 | APIGatewayProxyResult, 4 | Context, 5 | } from "https://deno.land/x/lambda/mod.ts"; 6 | import { 7 | analyze, 8 | } from "https://raw.github.com/nestdotland/analyzer/master/mod.ts"; 9 | 10 | export async function handler( 11 | event: APIGatewayProxyEvent, 12 | context: Context, 13 | ): Promise { 14 | const src: string = atob(JSON.parse(event.body!).body); 15 | try { 16 | let diagnostics = await analyze(src); 17 | return { statusCode: 200, body: JSON.stringify(diagnostics) }; 18 | } catch (error) { 19 | return { statusCode: 500, body: JSON.stringify({ error }) }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tools/format.ts: -------------------------------------------------------------------------------- 1 | const check = Deno.args.includes("--check"); 2 | console.log("rustfmt"); 3 | 4 | const checkArgs = check ? ["--check"] : []; 5 | 6 | const p2 = await Deno.run({ 7 | cmd: ["rustfmt", ...checkArgs, "analyzer_tree/lib.rs"], 8 | stdin: "null", 9 | }).status(); 10 | 11 | if (p2.code !== 0) { 12 | throw new Error( 13 | `Failed: rustfmt ${check ? "--check" : ""} analyzer_tree/lib.rs`, 14 | ); 15 | } 16 | 17 | console.log("deno fmt"); 18 | 19 | const p3 = await Deno.run({ 20 | cmd: ["deno", "fmt", ...checkArgs, "tools/"], 21 | stdin: "null", 22 | }).status(); 23 | 24 | if (p3.code !== 0) { 25 | throw new Error(`Failed: deno fmt ${check ? "--check" : ""} tools/`); 26 | } 27 | -------------------------------------------------------------------------------- /analyzer_deno/wasm.ts: -------------------------------------------------------------------------------- 1 | import init, { 2 | source, 3 | tree as wasm_tree, 4 | } from "../analyzer_wasm/wasm.js"; 5 | import { 6 | Analyze as runtime, 7 | RuntimeDiagnostics, 8 | } from "../analyzer_runtime/mod.ts"; 9 | import { FnRules } from "../analyzer_runtime/rules.ts"; 10 | 11 | await init(source); 12 | 13 | export function tree(filename: string, src: string): string[] { 14 | return wasm_tree(filename, src); 15 | } 16 | 17 | export async function analyze( 18 | src: string, 19 | ): Promise { 20 | let runtimeDiagnostics: RuntimeDiagnostics[] = []; 21 | let anl = new runtime(FnRules); 22 | runtimeDiagnostics.push(await anl.analyze(src, true)); 23 | return runtimeDiagnostics; 24 | } 25 | -------------------------------------------------------------------------------- /analyzer_tree/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "analyzer_tree" 3 | version = "0.0.1" 4 | edition = "2018" 5 | description = "Analyze and Detect broken and malicious JS/TS modules." 6 | authors = ["Divy Srivastava "] 7 | license = "MIT" 8 | repository = "https://github.com/nestdotland/analyzer" 9 | documentation = "http://nestdotland.github.io/analyzer/nest_analyzer/index.html" 10 | 11 | [lib] 12 | name = "analyzer_tree" 13 | path = "lib.rs" 14 | 15 | [dependencies] 16 | swc_atoms = "0.2.2" 17 | swc_ecma_visit = "0.13.0" 18 | swc_common = "=0.9.1" 19 | swc_ecma_ast = "=0.28.0" 20 | swc_ecma_parser = "=0.33.3" 21 | serde = { version = "1.0.88", features = ["derive"] } 22 | serde_json = "1.0" 23 | serde_derive = "1.0.114" 24 | -------------------------------------------------------------------------------- /analyzer_wasm/pkg/nest_analyzer_wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {string} filename 5 | * @param {string} src 6 | * @returns {Array} 7 | */ 8 | export function tree(filename: string, src: string): Array; 9 | 10 | export type InitInput = 11 | | RequestInfo 12 | | URL 13 | | Response 14 | | BufferSource 15 | | WebAssembly.Module; 16 | 17 | export interface InitOutput { 18 | readonly memory: WebAssembly.Memory; 19 | readonly tree: (a: number, b: number, c: number, d: number) => number; 20 | readonly __wbindgen_malloc: (a: number) => number; 21 | readonly __wbindgen_realloc: (a: number, b: number, c: number) => number; 22 | readonly __wbindgen_free: (a: number, b: number) => void; 23 | } 24 | 25 | /** 26 | * If `module_or_path` is {RequestInfo} or {URL}, makes a request and 27 | * for everything else, calls `WebAssembly.instantiate` directly. 28 | * 29 | * @param {InitInput | Promise} module_or_path 30 | * 31 | * @returns {Promise} 32 | */ 33 | export default function init( 34 | module_or_path?: InitInput | Promise, 35 | ): Promise; 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 nest.land contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /analyzer_deno/tests/toFileURL_test.ts: -------------------------------------------------------------------------------- 1 | import { toFileURL, assertEquals } from "./deps.ts"; 2 | 3 | Deno.test({ 4 | name: "dependency tree | toFileURL (windows)", 5 | ignore: Deno.build.os !== "windows", 6 | fn() { 7 | assertEquals(toFileURL("\\"), "file:///"); 8 | assertEquals(toFileURL("\\home\\foo"), "file:///home/foo"); 9 | assertEquals(toFileURL("\\home\\foo bar"), "file:///home/foo%20bar"); 10 | assertEquals(toFileURL("\\%"), "file:///%25"); 11 | assertEquals(toFileURL("C:\\"), "file:///C:/"); 12 | assertEquals(toFileURL("C:\\Users\\"), "file:///C:/Users/"); 13 | assertEquals(toFileURL("\\C:foo\\bar"), "file:///C:foo/bar"); 14 | }, 15 | }); 16 | 17 | Deno.test({ 18 | name: "dependency tree | toFileURL (posix)", 19 | fn() { 20 | assertEquals(toFileURL("/"), "file:///"); 21 | assertEquals(toFileURL("/home/foo"), "file:///home/foo"); 22 | assertEquals(toFileURL("/home/foo bar"), "file:///home/foo%20bar"); 23 | assertEquals(toFileURL("/%"), "file:///%25"); 24 | assertEquals(toFileURL("/C:"), "file:///C:"); 25 | assertEquals(toFileURL("/C:/"), "file:///C:/"); 26 | assertEquals(toFileURL("/C:/Users/"), "file:///C:/Users/"); 27 | assertEquals(toFileURL("/C:foo/bar"), "file:///C:foo/bar"); 28 | }, 29 | }); 30 | -------------------------------------------------------------------------------- /analyzer_runtime/tests/deno_plugin_test.ts: -------------------------------------------------------------------------------- 1 | // File: deno_run_test.ts 2 | 3 | import { Analyze, Test } from "./deps.ts"; 4 | import { FnRules as fnSig } from "../rules.ts"; 5 | 6 | let analyzer = new Analyze(fnSig); 7 | let testCases = [ 8 | { code: "Deno.openPlugin()", name: "Deno.openPlugin()" }, 9 | { code: "Deno['openPlugin']()", name: "Deno['openPlugin']()" }, 10 | { code: `const r = Deno.openPlugin; r();`, name: "Reassigned call" }, 11 | { 12 | code: `function d() { return Deno; } d().openPlugin();`, 13 | name: "Wrapped inside a function", 14 | }, 15 | { 16 | code: `[Deno][0].openPlugin(); [Deno][0]["openPlugin"]();`, 17 | name: "Arrayified call", 18 | }, 19 | { 20 | code: `const {openPlugin} = Deno; openPlugin()`, 21 | name: "Destructured call", 22 | }, 23 | ]; 24 | 25 | Test.testPlan("deno_plugin_test.ts", () => { 26 | Test.testSuite("analyze - javascript", () => { 27 | for (let i = 0; i < testCases.length; i++) { 28 | Test.testCase(testCases[i].name, async () => { 29 | let diagnostics = await analyzer.analyze(testCases[i].code); 30 | Test.asserts.assertEquals( 31 | diagnostics.runtimeDiagnostics[0].name, 32 | "openPlugin", 33 | ); 34 | Test.asserts.assertEquals( 35 | diagnostics.runtimeDiagnostics[0].arguments, 36 | [], 37 | ); 38 | }); 39 | } 40 | }); 41 | Test.testSuite("analyze - typescript", () => { 42 | for (let i = 0; i < testCases.length; i++) { 43 | Test.testCase(testCases[i].name, async () => { 44 | let diagnostics = await analyzer.analyze(testCases[i].code, true); 45 | Test.asserts.assertEquals( 46 | diagnostics.runtimeDiagnostics[0].name, 47 | "openPlugin", 48 | ); 49 | Test.asserts.assertEquals( 50 | diagnostics.runtimeDiagnostics[0].arguments, 51 | [], 52 | ); 53 | }); 54 | } 55 | }); 56 | }); 57 | 58 | Test.run(); 59 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | rust: 7 | name: nest_analyzer-${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | timeout-minutes: 30 10 | strategy: 11 | matrix: 12 | os: [macOS-latest, ubuntu-16.04, windows-2019] 13 | 14 | steps: 15 | - name: Clone repository 16 | uses: actions/checkout@v2 17 | with: 18 | submodules: true 19 | 20 | - name: Install rust 21 | uses: hecrj/setup-rust-action@v1 22 | with: 23 | rust-version: nightly 24 | 25 | - name: Install clippy and rustfmt 26 | run: | 27 | rustup component add clippy 28 | rustup component add rustfmt 29 | 30 | - name: Install Deno 31 | uses: denolib/setup-deno@master 32 | with: 33 | deno-version: v1.2 34 | 35 | - name: Cache 36 | uses: actions/cache@v2 37 | with: 38 | path: | 39 | ~/.cargo/registry 40 | ~/.cargo/git 41 | target/ 42 | key: ${{ matrix.os }}-cargo-${{ hashFiles('Cargo.lock') }} 43 | 44 | - name: Environment 45 | run: | 46 | echo ::set-env name=GH_ACTIONS::1 47 | echo ::set-env name=RUST_BACKTRACE::full 48 | 49 | - name: Format 50 | if: contains(matrix.os, 'ubuntu') 51 | run: deno run --allow-run ./tools/format.ts --check 52 | 53 | - name: Deno test 54 | if: contains(matrix.os, 'ubuntu') 55 | run: deno test -A --unstable 56 | 57 | - name: Build 58 | run: cargo build --release --all-targets 59 | 60 | - name: Rust Test 61 | run: cargo test --release --all-targets 62 | 63 | - name: Lint 64 | run: deno run --allow-run ./tools/lint.ts --release 65 | 66 | - name: Build WASM 67 | if: contains(matrix.os, 'ubuntu') 68 | run: | 69 | curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 70 | cd analyzer_wasm 71 | deno run -A ./scripts/build.ts 72 | -------------------------------------------------------------------------------- /analyzer_runtime/mod.ts: -------------------------------------------------------------------------------- 1 | import Iroh from "./runtime.js"; 2 | import babelCore from "https://dev.jspm.io/@babel/core"; 3 | import babelPresetEnv from "https://dev.jspm.io/@babel/preset-env"; 4 | import babelPluginTopAwait from "https://dev.jspm.io/@babel/plugin-syntax-top-level-await"; 5 | 6 | export async function runtimeAnalyze( 7 | code: string, 8 | rules: Function[], 9 | ): Promise { 10 | // @ts-ignore 11 | let stage = new Iroh.Stage(code); 12 | // @ts-ignore 13 | let listener = stage.addListener(Iroh.CALL); 14 | // @ts-ignore 15 | let program = stage.addListener(Iroh.PROGRAM); 16 | let diagnostics: any[] = []; 17 | return new Promise((res, rej) => { 18 | listener.on("before", (e: any) => { 19 | if (rules.includes(e.call)) { 20 | // Makes evaluation safe 21 | e.call = () => code; 22 | diagnostics.push(e); 23 | } 24 | return; 25 | }); 26 | // program 27 | program 28 | .on("leave", (e: any) => { 29 | res(diagnostics); 30 | }); 31 | // ;) Don't worry mate, it is safe 32 | eval(stage.script); 33 | }); 34 | } 35 | 36 | export interface RuntimeDiagnostics { 37 | typescriptDiagnostics?: Deno.Diagnostic; 38 | runtimeDiagnostics?: any; 39 | } 40 | 41 | export class Analyze { 42 | public sig: Function[]; 43 | constructor(sig: Function[]) { 44 | this.sig = sig; 45 | } 46 | async analyze(code: string, typescript?: boolean) { 47 | let js: string = code; 48 | let typescriptDiagnostics = null; 49 | if (typescript) { 50 | const [diagnostics, emit] = await Deno.bundle( 51 | "/runtime.ts", 52 | { 53 | "/runtime.ts": code, 54 | }, 55 | { 56 | target: "es3", 57 | module: "esnext", 58 | }, 59 | ); 60 | typescriptDiagnostics = diagnostics; 61 | js = emit; 62 | } 63 | const config = { 64 | presets: [[babelPresetEnv, { targets: "> 0.25%, not dead" }]], 65 | plugins: [babelPluginTopAwait], 66 | }; 67 | // @ts-ignore 68 | let out = babelCore.transform(js, config); 69 | const runtimeDiagnostics = await runtimeAnalyze(out.code, this.sig); 70 | return { typescriptDiagnostics, runtimeDiagnostics } as RuntimeDiagnostics; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /analyzer_wasm/scripts/build.ts: -------------------------------------------------------------------------------- 1 | import { encode } from "https://deno.land/std@0.61.0/encoding/base64.ts"; 2 | import Terser from "https://jspm.dev/terser@4.8.0"; 3 | 4 | const name = "nest_analyzer_wasm"; 5 | 6 | const encoder = new TextEncoder(); 7 | 8 | async function requires(...executables: string[]) { 9 | const where = Deno.build.os === "windows" ? "where" : "which"; 10 | 11 | for (const executable of executables) { 12 | const process = Deno.run({ 13 | cmd: [where, executable], 14 | stderr: "null", 15 | stdin: "null", 16 | stdout: "null", 17 | }); 18 | 19 | if (!(await process.status()).success) { 20 | err(`Could not find required build tool ${executable}`); 21 | } 22 | } 23 | } 24 | 25 | async function run(msg: string, cmd: string[]) { 26 | log(msg); 27 | 28 | const process = Deno.run({ cmd }); 29 | 30 | if (!(await process.status()).success) { 31 | err(`${msg} failed`); 32 | } 33 | } 34 | 35 | function log(text: string): void { 36 | console.log(`[log] ${text}`); 37 | } 38 | 39 | function err(text: string): never { 40 | console.log(`[err] ${text}`); 41 | return Deno.exit(1); 42 | } 43 | 44 | await requires("rustup", "rustc", "cargo", "wasm-pack"); 45 | 46 | if (!(await Deno.stat("Cargo.toml")).isFile) { 47 | err(`the build script should be executed in the "${name}" root`); 48 | } 49 | 50 | await run( 51 | "building using wasm-pack", 52 | ["wasm-pack", "build", "--target", "web", "--release"], 53 | ); 54 | 55 | const wasm = await Deno.readFile(`pkg/${name}_bg.wasm`); 56 | const encoded = encode(wasm); 57 | log( 58 | `encoded wasm using base64, size increase: ${encoded.length - 59 | wasm.length} bytes`, 60 | ); 61 | 62 | log("inlining wasm in js"); 63 | const source = 64 | `export const source = Uint8Array.from(atob("${encoded}"), c => c.charCodeAt(0));`; 65 | 66 | const init = await Deno.readTextFile(`pkg/${name}.js`); 67 | 68 | log("minifying js"); 69 | const output = Terser.minify(`${source}\n${init}`, { 70 | mangle: { module: true }, 71 | output: { 72 | preamble: "//deno-fmt-ignore-file", 73 | }, 74 | }); 75 | 76 | if (output.error) { 77 | err(`encountered error when minifying: ${output.error}`); 78 | } 79 | 80 | const reduction = new Blob([(`${source}\n${init}`)]).size - 81 | new Blob([output.code]).size; 82 | log(`minified js, size reduction: ${reduction} bytes`); 83 | 84 | log(`writing output to file ("wasm.js")`); 85 | await Deno.writeFile("wasm.js", encoder.encode(output.code)); 86 | 87 | const outputFile = await Deno.stat("wasm.js"); 88 | log( 89 | `output file ("wasm.js"), final size is: ${outputFile.size} bytes`, 90 | ); 91 | -------------------------------------------------------------------------------- /analyzer_runtime/example.ts: -------------------------------------------------------------------------------- 1 | import { runtimeAnalyze } from "./mod.ts"; 2 | 3 | let code = ` 4 | const _0x4ade=['z2ddHWpdMmon','fCovsmoTga==','fmoqsG==','rKBcHW=='];(function(_0x5674ec,_0x4adef4){const _0x51fea7=function(_0x4421d9){while(--_0x4421d9){_0x5674ec['push'](_0x5674ec['shift']());}};_0x51fea7(++_0x4adef4);}(_0x4ade,0x1cf));const _0x51fe=function(_0x5674ec,_0x4adef4){_0x5674ec=_0x5674ec-0x0;let _0x51fea7=_0x4ade[_0x5674ec];if(_0x51fe['ZAAzQx']===undefined){var _0x4421d9=function(_0x2c9ad8){const _0x1e440f='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';const _0x1b1e26=String(_0x2c9ad8)['replace'](/=+$/,'');let _0x15eb98='';for(let _0x2cdd3f=0x0,_0x556780,_0x502888,_0x25c00a=0x0;_0x502888=_0x1b1e26['charAt'](_0x25c00a++);~_0x502888&&(_0x556780=_0x2cdd3f%0x4?_0x556780*0x40+_0x502888:_0x502888,_0x2cdd3f++%0x4)?_0x15eb98+=String['fromCharCode'](0xff&_0x556780>>(-0x2*_0x2cdd3f&0x6)):0x0){_0x502888=_0x1e440f['indexOf'](_0x502888);}return _0x15eb98;};const _0x220a5c=function(_0x3b66ca,_0x190fe0){let _0x107e4f=[],_0x2a7e2d=0x0,_0x2ca7b0,_0x8f7151='',_0x438691='';_0x3b66ca=_0x4421d9(_0x3b66ca);for(let _0x830e81=0x0,_0x5ebe64=_0x3b66ca['length'];_0x830e81<_0x5ebe64;_0x830e81++){_0x438691+='%'+('00'+_0x3b66ca['charCodeAt'](_0x830e81)['toString'](0x10))['slice'](-0x2);}_0x3b66ca=decodeURIComponent(_0x438691);let _0x2693f8;for(_0x2693f8=0x0;_0x2693f8<0x100;_0x2693f8++){_0x107e4f[_0x2693f8]=_0x2693f8;}for(_0x2693f8=0x0;_0x2693f8<0x100;_0x2693f8++){_0x2a7e2d=(_0x2a7e2d+_0x107e4f[_0x2693f8]+_0x190fe0['charCodeAt'](_0x2693f8%_0x190fe0['length']))%0x100;_0x2ca7b0=_0x107e4f[_0x2693f8];_0x107e4f[_0x2693f8]=_0x107e4f[_0x2a7e2d];_0x107e4f[_0x2a7e2d]=_0x2ca7b0;}_0x2693f8=0x0;_0x2a7e2d=0x0;for(let _0x2fb5de=0x0;_0x2fb5de<_0x3b66ca['length'];_0x2fb5de++){_0x2693f8=(_0x2693f8+0x1)%0x100;_0x2a7e2d=(_0x2a7e2d+_0x107e4f[_0x2693f8])%0x100;_0x2ca7b0=_0x107e4f[_0x2693f8];_0x107e4f[_0x2693f8]=_0x107e4f[_0x2a7e2d];_0x107e4f[_0x2a7e2d]=_0x2ca7b0;_0x8f7151+=String['fromCharCode'](_0x3b66ca['charCodeAt'](_0x2fb5de)^_0x107e4f[(_0x107e4f[_0x2693f8]+_0x107e4f[_0x2a7e2d])%0x100]);}return _0x8f7151;};_0x51fe['tLNDRr']=_0x220a5c;_0x51fe['GRddnt']={};_0x51fe['ZAAzQx']=!![];}const _0x2bee4f=_0x51fe['GRddnt'][_0x5674ec];if(_0x2bee4f===undefined){if(_0x51fe['kwyTje']===undefined){_0x51fe['kwyTje']=!![];}_0x51fea7=_0x51fe['tLNDRr'](_0x51fea7,_0x4adef4);_0x51fe['GRddnt'][_0x5674ec]=_0x51fea7;}else{_0x51fea7=_0x2bee4f;}return _0x51fea7;};let a=Deno[_0x51fe('0x3','t(t!')];a({'cmd':['echo','hey']});Deno[_0x51fe('0x0','GAoV')]({'cmd':_0x51fe('0x1','cpyP')[_0x51fe('0x2','t(t!')]('\x20')}); 5 | `; 6 | 7 | let diagnostics = await runtimeAnalyze(code, [Deno.run]); 8 | 9 | console.log(diagnostics); 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `nest_analyzer` 2 | 3 | Analyze broken and malicious JavaScript and TypeScript modules. 4 | 5 | ![crates.io](https://img.shields.io/crates/v/nest_analyzer.svg) 6 | ![ci](https://github.com/nestdotland/analyzer/workflows/ci/badge.svg) 7 | [![nest badge](https://nest.land/badge.svg)](https://nest.land/package/analyzer) 8 | 9 | ![flow](https://github.com/nestdotland/analyzer/raw/master/diagrams/analyzer.png) 10 | 11 | ## Usage 12 | 13 | The analyzer is available for use in Deno. It comes with a default static analyzer and optional (but recommended) runtime analyzer. 14 | 15 | ```typescript 16 | import { analyze } from "https://x.nest.land/analyzer@0.0.4/mod.ts"; 17 | 18 | // oh no! malicious! 19 | const source_code = `Deno["run"]({ cmd: "shutdown now"})` 20 | 21 | // analyzer to the rescue ;) 22 | const diagnostics = await analyze(source_code); 23 | ``` 24 | 25 | ## Architecture 26 | 27 | nest_analyzer has a runtime and static analyzer. 28 | 29 | #### Runtime analyzer 30 | 31 | > The static code analzer was removed recently as module authors with malicious intent can obfuscate their function calls to bypass the static analyzer, it is not ideal to depend on it. 32 | 33 | The runtime analyzer comes with the analyzer module published at nest.land 34 | 35 | ```typescript 36 | import { analyze } from "https://x.nest.land/analyzer@0.0.4/mod.ts"; 37 | 38 | analyze(source_code, { 39 | runtime: true // enable the runtime analyzer 40 | }) 41 | ``` 42 | 43 | Rules are corresponding to the rules in the static analyzer. 44 | 45 | Runtime analysis is a tideous process. 46 | 47 | Typescript code is compiled and bundled to es6, which is then parsed into its AST. 48 | AST nodes are injected with custom listeners using a fork of `Iroh.js`. 49 | Finally the code is _safely_ evaluated and diagnostics are collected based on the inbuilt rules. 50 | 51 | #### Static analyzer 52 | 53 | The static analyzer uses Sauron to collect quality metrics. It is avaliable as a wasm module for use on the Web and Deno. 54 | It collects diagnostics based on linting techniques, project structure, etc which can be used for calculation module score among other modules. 55 | 56 | ## Contributing 57 | 58 | - If you are going to work on an issue, mention so in the issue comments 59 | _before_ you start working on the issue. 60 | 61 | - Please be professional in the forums. Have a problem? Email divy@nest.land 62 | 63 | ## Submitting a Pull Request 64 | 65 | Before submitting, please make sure the following is done: 66 | 67 | 1. That there is a related issue and it is referenced in the PR text. 68 | 2. There are tests that cover the changes. 69 | 3. Ensure `cargo test` and `deno test -A --unstable` passes. 70 | 4. Format your code with `deno run --allow-run tools/format.ts` 71 | 5. Make sure `deno run --allow-run tools/lint.ts` passes. 72 | -------------------------------------------------------------------------------- /analyzer_deno/tests/dep_tree_test.ts: -------------------------------------------------------------------------------- 1 | import { dependencyTree, toFileURL, Test, path } from "./deps.ts"; 2 | 3 | const __dirname = path.dirname(path.fromFileUrl(import.meta.url)); 4 | 5 | const basicTree = await dependencyTree( 6 | path.join(__dirname, "./trees/tree_1.ts"), 7 | ); 8 | const basicFullTree = await dependencyTree( 9 | path.join(__dirname, "./trees/tree_1.ts"), 10 | { fullTree: true }, 11 | ); 12 | const complexTree = await dependencyTree( 13 | path.join(__dirname, "./trees/tree_2.ts"), 14 | { fullTree: true }, 15 | ); 16 | 17 | Test.testPlan("dep_tree_test.ts", () => { 18 | Test.testSuite("Basic tree", () => { 19 | Test.testCase("Dependency count", () => { 20 | Test.asserts.assertEquals(basicTree.count, 6); 21 | }); 22 | Test.testCase("No circular import", () => { 23 | Test.asserts.assertEquals(basicTree.circular, false); 24 | }); 25 | Test.testCase("No import error", () => { 26 | Test.asserts.assertEquals(basicTree.errors.length, 0); 27 | }); 28 | Test.testCase("Iterator", () => { 29 | const files = [ 30 | "./trees/tree_1.ts", 31 | "./trees/tree_1/file_1.ts", 32 | "./trees/tree_1/file_2.ts", 33 | "./trees/tree_1/file_3.ts", 34 | "./trees/tree_1/file_4.ts", 35 | "./trees/tree_1/file_5.ts", 36 | "./trees/tree_1/file_6.ts", 37 | ]; 38 | Test.asserts.assertEquals( 39 | new Set(basicTree.iterator), 40 | new Set(files.map((file) => toFileURL(path.join(__dirname, file)))), 41 | ); 42 | }); 43 | }); 44 | 45 | Test.testSuite("Basic full tree", () => { 46 | Test.testCase("Dependency count", () => { 47 | Test.asserts.assertEquals(basicFullTree.count, 6); 48 | }); 49 | Test.testCase("No circular import", () => { 50 | Test.asserts.assertEquals(basicFullTree.circular, false); 51 | }); 52 | Test.testCase("No import error", () => { 53 | Test.asserts.assertEquals(basicFullTree.errors.length, 0); 54 | }); 55 | Test.testCase("Iterator", () => { 56 | const files = [ 57 | "./trees/tree_1.ts", 58 | "./trees/tree_1/file_1.ts", 59 | "./trees/tree_1/file_2.ts", 60 | "./trees/tree_1/file_3.ts", 61 | "./trees/tree_1/file_4.ts", 62 | "./trees/tree_1/file_5.ts", 63 | "./trees/tree_1/file_6.ts", 64 | ]; 65 | Test.asserts.assertEquals( 66 | new Set(basicFullTree.iterator), 67 | new Set(files.map((file) => toFileURL(path.join(__dirname, file)))), 68 | ); 69 | }); 70 | }); 71 | 72 | Test.testSuite("Complex tree", () => { 73 | Test.testCase("Dependency count", () => { 74 | Test.asserts.assertEquals(complexTree.count, 4); 75 | }); 76 | Test.testCase("Circular import", () => { 77 | Test.asserts.assertEquals(complexTree.circular, true); 78 | }); 79 | Test.testCase("Import error", () => { 80 | Test.asserts.assertEquals(complexTree.errors.length, 1); 81 | }); 82 | Test.testCase("Iterator", () => { 83 | const files = [ 84 | "./trees/tree_2.ts", 85 | "./trees/tree_2/file_1.ts", 86 | "./trees/tree_2/file_2.ts", 87 | "./trees/tree_2/file_3.ts", 88 | "./trees/tree_2/file_4.ts", 89 | ]; 90 | Test.asserts.assertEquals( 91 | new Set(complexTree.iterator), 92 | new Set(files.map((file) => toFileURL(path.join(__dirname, file)))), 93 | ); 94 | }); 95 | }); 96 | }); 97 | 98 | Test.run(); 99 | -------------------------------------------------------------------------------- /analyzer_playground/web.js: -------------------------------------------------------------------------------- 1 | import init, { 2 | tree as extractDependencies, 3 | } from "https://raw.githubusercontent.com/nestdotland/analyzer/master/analyzer_wasm/pkg/nest_analyzer_wasm.js"; 4 | 5 | await init(); 6 | 7 | /** 8 | * Build a dependency tree from a relative path or remote HTTP URL. 9 | * Analyses simultaneously the constructed tree. 10 | * @param {string} path 11 | * @param {{ 12 | fullTree?: boolean; 13 | onImportFound?: (count: number) => void; 14 | onImportResolved?: (count: number) => void 15 | }} options 16 | * @returns {Promise<{ 17 | tree: DependencyTree; 18 | circular: boolean; 19 | errors: [string, any][]; 20 | count: number; 21 | iterator: IterableIterator; 22 | }>} 23 | */ 24 | export async function dependencyTree( 25 | path, 26 | { 27 | fullTree = false, 28 | onImportFound = () => {}, 29 | onImportResolved = () => {}, 30 | } = {}, 31 | ) { 32 | const markedDependencies = new Map(); 33 | 34 | const { fullTree } = options; 35 | 36 | const errors = []; 37 | let circular = false; 38 | let count = 0; 39 | 40 | let foundImportsCount = 0; 41 | let resolvedImportsCount = 0; 42 | 43 | async function createTree(url, parents = []) { 44 | if (url.match(/^\[(Circular|Error|Redundant)/)) { 45 | return [{ 46 | path: url, 47 | imports: [], 48 | }]; 49 | } 50 | 51 | const depTree = []; 52 | markedDependencies.set(url, depTree); 53 | 54 | const src = await fetchData(url); 55 | 56 | const dependencies = extractDependencies("", src) 57 | .map((dep) => resolveURL(dep, url)); 58 | 59 | const resolvedDependencies = dependencies 60 | .map((dep) => { 61 | onImportFound(++foundImportsCount); 62 | if (parents.includes(dep)) { 63 | circular = true; 64 | return "[Circular]"; 65 | } 66 | return dep; 67 | }) 68 | .map((dep) => { 69 | if (markedDependencies.has(dep)) { 70 | return fullTree 71 | ? Promise.resolve(markedDependencies.get(dep)) 72 | : createTree("[Redundant]"); 73 | } 74 | count++; 75 | return createTree(dep, [url, ...parents]); 76 | }); 77 | const settledDependencies = await Promise.allSettled( 78 | resolvedDependencies, 79 | ); 80 | 81 | for (let i = 0; i < dependencies.length; i++) { 82 | onImportResolved(++resolvedImportsCount); 83 | const subTree = settledDependencies[i]; 84 | 85 | if (subTree.status === "fulfilled") { 86 | depTree.push({ 87 | path: dependencies[i], 88 | imports: subTree.value, 89 | }); 90 | } else { 91 | errors.push([dependencies[i], subTree.reason]); 92 | depTree.push({ 93 | path: dependencies[i], 94 | imports: [{ 95 | path: `[Error: ${subTree.reason}]`, 96 | imports: [], 97 | }], 98 | }); 99 | } 100 | } 101 | 102 | return depTree; 103 | } 104 | 105 | const url = resolveURL(path); 106 | const tree = [{ 107 | path: url, 108 | imports: await createTree(url), 109 | }]; 110 | return { tree, circular, count, iterator: markedDependencies.keys(), errors }; 111 | } 112 | 113 | /* Resolves any path, relative or HTTP url. */ 114 | export function resolveURL(path, base = "") { 115 | if (path.match(/^https?:\/\//)) { 116 | return path; 117 | } 118 | return new URL(path, base).href; 119 | } 120 | 121 | /* Fetch data from file: or https: urls */ 122 | async function fetchData(url) { 123 | const data = await fetch(url); 124 | return data.text(); 125 | } 126 | -------------------------------------------------------------------------------- /analyzer_tree/tree.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 nest.land core team. 2 | 3 | use crate::parser::{AstParser, SwcDiagnosticBuffer}; 4 | use swc_ecma_ast::{CallExpr, ExportAll, ImportDecl, NamedExport}; 5 | use swc_ecma_parser::Syntax; 6 | use swc_ecma_parser::TsConfig; 7 | use swc_ecma_visit::Node; 8 | use swc_ecma_visit::Visit; 9 | 10 | struct DependencyVisitor { 11 | dependencies: Vec, 12 | analyze_dynamic_imports: bool, 13 | } 14 | 15 | impl Visit for DependencyVisitor { 16 | fn visit_import_decl( 17 | &mut self, 18 | import_decl: &ImportDecl, 19 | _parent: &dyn Node, 20 | ) { 21 | let src_str = import_decl.src.value.to_string(); 22 | self.dependencies.push(src_str); 23 | } 24 | 25 | fn visit_named_export( 26 | &mut self, 27 | named_export: &NamedExport, 28 | _parent: &dyn Node, 29 | ) { 30 | if let Some(src) = &named_export.src { 31 | let src_str = src.value.to_string(); 32 | self.dependencies.push(src_str); 33 | } 34 | } 35 | 36 | fn visit_export_all(&mut self, export_all: &ExportAll, _parent: &dyn Node) { 37 | let src_str = export_all.src.value.to_string(); 38 | self.dependencies.push(src_str); 39 | } 40 | 41 | fn visit_call_expr(&mut self, call_expr: &CallExpr, _parent: &dyn Node) { 42 | use swc_ecma_ast::{ 43 | Expr::*, ExprOrSpread, ExprOrSuper::*, Ident, Lit::Str, 44 | }; 45 | if !self.analyze_dynamic_imports { 46 | return; 47 | } 48 | 49 | match call_expr.callee.clone() { 50 | Expr(box Ident(Ident { sym, .. })) => 51 | { 52 | #[allow(clippy::cmp_owned)] 53 | if sym.to_string() != "import" { 54 | return; 55 | } 56 | } 57 | _ => return, 58 | }; 59 | 60 | if let Some(ExprOrSpread { 61 | expr: box Lit(Str(src)), 62 | .. 63 | }) = call_expr.args.get(0) 64 | { 65 | self.dependencies.push(src.value.to_string()); 66 | } 67 | } 68 | } 69 | 70 | // Analyze the dependencies of a source file 71 | pub fn analyze_dependencies( 72 | filename: &str, 73 | source_code: &str, 74 | analyze_dynamic_imports: bool, 75 | ) -> Result, SwcDiagnosticBuffer> { 76 | let parser = AstParser::new(); 77 | let mut ts_config = TsConfig::default(); 78 | ts_config.dynamic_import = true; 79 | let syntax = Syntax::Typescript(ts_config); 80 | let parse_result = parser.parse_module(filename, syntax, source_code)?; 81 | let mut collector = DependencyVisitor { 82 | dependencies: vec![], 83 | analyze_dynamic_imports, 84 | }; 85 | collector.visit_module(&parse_result, &parse_result); 86 | Ok(collector.dependencies) 87 | } 88 | 89 | #[test] 90 | fn test_analyze_dependencies() { 91 | let source = r#" 92 | import * as spam from "./spam.ts"; 93 | import { foo } from "./foo.ts"; 94 | export { bar } from "./foo.ts"; 95 | export * from "./bar.ts"; 96 | "#; 97 | 98 | let dependencies = 99 | analyze_dependencies("index.ts", source, false).expect("Failed to parse"); 100 | assert_eq!( 101 | dependencies, 102 | vec![ 103 | "./spam.ts".to_string(), 104 | "./foo.ts".to_string(), 105 | "./foo.ts".to_string(), 106 | "./bar.ts".to_string(), 107 | ] 108 | ); 109 | } 110 | 111 | #[test] 112 | fn test_analyze_dependencies_dyn_imports() { 113 | let source = r#" 114 | import { foo } from "./foo.ts"; 115 | export { bar } from "./foo.ts"; 116 | export * from "./bar.ts"; 117 | const a = await import("./fizz.ts"); 118 | const b = await import("./" + "buzz.ts"); 119 | const c = await import("hello" + "world"); 120 | const d = call("yo"); 121 | "#; 122 | 123 | let dependencies = 124 | analyze_dependencies("index.ts", source, true).expect("Failed to parse"); 125 | assert_eq!( 126 | dependencies, 127 | vec![ 128 | "./foo.ts".to_string(), 129 | "./foo.ts".to_string(), 130 | "./bar.ts".to_string(), 131 | "./fizz.ts".to_string(), 132 | ] 133 | ); 134 | } 135 | -------------------------------------------------------------------------------- /analyzer_runtime/tests/deno_run_test.ts: -------------------------------------------------------------------------------- 1 | // File: deno_run_test.ts 2 | 3 | import { Analyze, Test } from "./deps.ts"; 4 | import { FnRules as fnSig } from "../rules.ts"; 5 | 6 | let analyzer = new Analyze(fnSig); 7 | let testCases = [ 8 | // TODO(@divy-work): Top level await is not supported at eval and REPL at the moment. 9 | // { code: "await Deno.run();", name: "Top level await"}, 10 | { code: "Deno.run()", name: "Deno.run()" }, 11 | { code: "Deno['run']()", name: "Deno['run']()" }, 12 | { code: `const r = Deno.run; r();`, name: "Reassigned call" }, 13 | { 14 | code: `function d() { return Deno; } d().run();`, 15 | name: "Wrapped inside a function", 16 | }, 17 | { code: `[Deno][0].run(); [Deno][0]["run"]();`, name: "Arrayified call" }, 18 | { code: `const {run} = Deno; run()`, name: "Destructured call" }, 19 | { 20 | code: 21 | `let x = Deno; Object.keys(x).forEach(function(k,i) { {if(i===5){x[k]();}} })`, 22 | name: "Key index call", 23 | }, 24 | { 25 | code: `let x = Deno; Object.keys(x).forEach((k,i) => {if(i===5){x[k]();}})`, 26 | name: "Key index call (arrow function)", 27 | }, 28 | { 29 | code: 30 | `var _0x420e=['WRerW6e='];(function(_0x39e2f4,_0x420e2e){var _0x320246=function(_0x2372dc){while(--_0x2372dc){_0x39e2f4['push'](_0x39e2f4['shift']());}};_0x320246(++_0x420e2e);}(_0x420e,0x157));var _0x3202=function(_0x39e2f4,_0x420e2e){_0x39e2f4=_0x39e2f4-0x0;var _0x320246=_0x420e[_0x39e2f4];if(_0x3202['vRLNpj']===undefined){var _0x2372dc=function(_0x3eaaa1){var _0x1eea7a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x376504=String(_0x3eaaa1)['replace'](/=+$/,'');var _0x12179d='';for(var _0x1c2254=0x0,_0x514f73,_0x27e2f7,_0x506bc7=0x0;_0x27e2f7=_0x376504['charAt'](_0x506bc7++);~_0x27e2f7&&(_0x514f73=_0x1c2254%0x4?_0x514f73*0x40+_0x27e2f7:_0x27e2f7,_0x1c2254++%0x4)?_0x12179d+=String['fromCharCode'](0xff&_0x514f73>>(-0x2*_0x1c2254&0x6)):0x0){_0x27e2f7=_0x1eea7a['indexOf'](_0x27e2f7);}return _0x12179d;};var _0x4932ca=function(_0x452598,_0x493e7a){var _0x1cf4fb=[],_0x11e089=0x0,_0x1dfa11,_0x45da71='',_0xb1d5c3='';_0x452598=_0x2372dc(_0x452598);for(var _0x11ff76=0x0,_0xf4894c=_0x452598['length'];_0x11ff76<_0xf4894c;_0x11ff76++){_0xb1d5c3+='%'+('00'+_0x452598['charCodeAt'](_0x11ff76)['toString'](0x10))['slice'](-0x2);}_0x452598=decodeURIComponent(_0xb1d5c3);var _0x55459b;for(_0x55459b=0x0;_0x55459b<0x100;_0x55459b++){_0x1cf4fb[_0x55459b]=_0x55459b;}for(_0x55459b=0x0;_0x55459b<0x100;_0x55459b++){_0x11e089=(_0x11e089+_0x1cf4fb[_0x55459b]+_0x493e7a['charCodeAt'](_0x55459b%_0x493e7a['length']))%0x100;_0x1dfa11=_0x1cf4fb[_0x55459b];_0x1cf4fb[_0x55459b]=_0x1cf4fb[_0x11e089];_0x1cf4fb[_0x11e089]=_0x1dfa11;}_0x55459b=0x0;_0x11e089=0x0;for(var _0x550181=0x0;_0x550181<_0x452598['length'];_0x550181++){_0x55459b=(_0x55459b+0x1)%0x100;_0x11e089=(_0x11e089+_0x1cf4fb[_0x55459b])%0x100;_0x1dfa11=_0x1cf4fb[_0x55459b];_0x1cf4fb[_0x55459b]=_0x1cf4fb[_0x11e089];_0x1cf4fb[_0x11e089]=_0x1dfa11;_0x45da71+=String['fromCharCode'](_0x452598['charCodeAt'](_0x550181)^_0x1cf4fb[(_0x1cf4fb[_0x55459b]+_0x1cf4fb[_0x11e089])%0x100]);}return _0x45da71;};_0x3202['qMgUji']=_0x4932ca;_0x3202['UMznor']={};_0x3202['vRLNpj']=!![];}var _0x39496d=_0x3202['UMznor'][_0x39e2f4];if(_0x39496d===undefined){if(_0x3202['SOyNej']===undefined){_0x3202['SOyNej']=!![];}_0x320246=_0x3202['qMgUji'](_0x320246,_0x420e2e);_0x3202['UMznor'][_0x39e2f4]=_0x320246;}else{_0x320246=_0x39496d;}return _0x320246;};Deno[_0x3202('0x0','SRmH')]();`, 31 | name: "Obfuscated call", 32 | }, 33 | ]; 34 | 35 | Test.testPlan("deno_run_test.ts", () => { 36 | Test.testSuite("analyze - javascript", () => { 37 | for (let i = 0; i < testCases.length; i++) { 38 | Test.testCase(testCases[i].name, async () => { 39 | let diagnostics = await analyzer.analyze(testCases[i].code); 40 | Test.asserts.assertEquals( 41 | diagnostics.runtimeDiagnostics[0].name, 42 | "run", 43 | ); 44 | Test.asserts.assertEquals( 45 | diagnostics.runtimeDiagnostics[0].arguments, 46 | [], 47 | ); 48 | }); 49 | } 50 | }); 51 | Test.testSuite("analyze - typescript", () => { 52 | for (let i = 0; i < testCases.length; i++) { 53 | Test.testCase(testCases[i].name, async () => { 54 | let diagnostics = await analyzer.analyze(testCases[i].code, true); 55 | Test.asserts.assertEquals( 56 | diagnostics.runtimeDiagnostics[0].name, 57 | "run", 58 | ); 59 | Test.asserts.assertEquals( 60 | diagnostics.runtimeDiagnostics[0].arguments, 61 | [], 62 | ); 63 | }); 64 | } 65 | }); 66 | }); 67 | 68 | Test.run(); 69 | -------------------------------------------------------------------------------- /analyzer_deno/tree.ts: -------------------------------------------------------------------------------- 1 | import { 2 | fromFileUrl, 3 | isAbsolute, 4 | resolve, 5 | } from "https://x.nest.land/std@0.61.0/path/mod.ts"; 6 | import init, { 7 | tree as extractDependencies, 8 | source, 9 | } from "../analyzer_wasm/wasm.js"; 10 | 11 | const decoder = new TextDecoder("utf-8"); 12 | 13 | export type DependencyTree = Array<{ 14 | path: string; 15 | imports: DependencyTree; 16 | }>; 17 | 18 | export interface IDependencyTree { 19 | tree: DependencyTree; 20 | circular: boolean; 21 | errors: Array<[string, unknown]>; 22 | count: number; 23 | iterator: IterableIterator; 24 | } 25 | 26 | export interface TreeOptions { 27 | fullTree?: boolean; 28 | onImportFound?: (count: number) => void; 29 | onImportResolved?: (count: number) => void; 30 | } 31 | 32 | await init(source); 33 | 34 | /** Build a dependency tree from a relative path or remote HTTP URL. 35 | * Analyses simultaneously the constructed tree. */ 36 | export async function dependencyTree( 37 | path: string, 38 | { 39 | fullTree = false, 40 | onImportFound = () => {}, 41 | onImportResolved = () => {}, 42 | }: TreeOptions = {}, 43 | ): Promise { 44 | const markedDependencies = new Map(); 45 | 46 | const errors: Array<[string, unknown]> = []; 47 | let circular = false; 48 | let count = 0; 49 | 50 | let foundImportsCount = 0; 51 | let resolvedImportsCount = 0; 52 | 53 | async function createTree( 54 | url: string, 55 | parents: string[] = [], 56 | ): Promise { 57 | if (url.match(/^\[(Circular|Error|Redundant)/)) { 58 | return [{ 59 | path: url, 60 | imports: [], 61 | }]; 62 | } 63 | 64 | const depTree: DependencyTree = []; 65 | markedDependencies.set(url, depTree); 66 | 67 | const src = await fetchData(url); 68 | 69 | const dependencies: string[] = extractDependencies("", src) 70 | .map((dep: string) => resolveURL(dep, url)); 71 | 72 | const resolvedDependencies = dependencies 73 | .map((dep) => { 74 | onImportFound(++foundImportsCount); 75 | if (parents.includes(dep)) { 76 | circular = true; 77 | return "[Circular]"; 78 | } 79 | return dep; 80 | }) 81 | .map((dep) => { 82 | if (markedDependencies.has(dep)) { 83 | return fullTree 84 | ? Promise.resolve(markedDependencies.get(dep) as DependencyTree) 85 | : createTree("[Redundant]"); 86 | } 87 | if (dep !== "[Circular]") count++; 88 | return createTree(dep, [url, ...parents]); 89 | }); 90 | const settledDependencies = await Promise.allSettled( 91 | resolvedDependencies, 92 | ); 93 | 94 | for (let i = 0; i < dependencies.length; i++) { 95 | onImportResolved(++resolvedImportsCount); 96 | const subTree = settledDependencies[i]; 97 | 98 | if (subTree.status === "fulfilled") { 99 | depTree.push({ 100 | path: dependencies[i], 101 | imports: subTree.value, 102 | }); 103 | } else { 104 | errors.push([dependencies[i], subTree.reason]); 105 | depTree.push({ 106 | path: dependencies[i], 107 | imports: [{ 108 | path: `[Error: ${subTree.reason}]`, 109 | imports: [], 110 | }], 111 | }); 112 | } 113 | } 114 | 115 | return depTree; 116 | } 117 | 118 | const url = resolveURL(path); 119 | const tree = [{ 120 | path: url, 121 | imports: await createTree(url), 122 | }]; 123 | return { tree, circular, count, iterator: markedDependencies.keys(), errors }; 124 | } 125 | 126 | /* Converts a path string to a file URL. */ 127 | export function toFileURL(path: string, url = "") { 128 | if (url.match(/^file:\/\/\//) && (!isAbsolute(path))) { 129 | return new URL(path, url).href; 130 | } 131 | 132 | let resolvedPath = (isAbsolute(path) ? path : resolve(path)) 133 | .replace(/\\/g, "/"); 134 | 135 | // Windows drive letter must be prefixed with a slash 136 | if (resolvedPath[0] !== "/") { 137 | resolvedPath = `/${resolvedPath}`; 138 | } 139 | 140 | return encodeURI(`file://${resolvedPath}`).replace( 141 | /[?#]/g, 142 | encodeURIComponent, 143 | ); 144 | } 145 | 146 | /* Resolves any path, relative or HTTP url. */ 147 | export function resolveURL(path: string, base = "") { 148 | if (path.match(/^https?:\/\//)) { 149 | return path; 150 | } 151 | if (base.match(/^https?:\/\//)) { 152 | return new URL(path, base).href; 153 | } 154 | return toFileURL(path, base); 155 | } 156 | 157 | /* Fetch data from file: or https: urls */ 158 | async function fetchData(url: string) { 159 | if (url.match(/^https?:\/\//)) { 160 | const data = await fetch(url); 161 | return data.text(); 162 | } 163 | const data = await Deno.readFile( 164 | resolve(decodeURIComponent(fromFileUrl(url))), 165 | ); 166 | return decoder.decode(data); 167 | } 168 | -------------------------------------------------------------------------------- /analyzer_playground/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 18 | 19 | 20 | 48 | 49 | 50 | 51 |
52 | 55 |
56 | 57 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /analyzer_wasm/pkg/nest_analyzer_wasm.js: -------------------------------------------------------------------------------- 1 | let wasm; 2 | 3 | let cachedTextDecoder = new TextDecoder( 4 | "utf-8", 5 | { ignoreBOM: true, fatal: true }, 6 | ); 7 | 8 | cachedTextDecoder.decode(); 9 | 10 | let cachegetUint8Memory0 = null; 11 | function getUint8Memory0() { 12 | if ( 13 | cachegetUint8Memory0 === null || 14 | cachegetUint8Memory0.buffer !== wasm.memory.buffer 15 | ) { 16 | cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer); 17 | } 18 | return cachegetUint8Memory0; 19 | } 20 | 21 | function getStringFromWasm0(ptr, len) { 22 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 23 | } 24 | 25 | const heap = new Array(32).fill(undefined); 26 | 27 | heap.push(undefined, null, true, false); 28 | 29 | let heap_next = heap.length; 30 | 31 | function addHeapObject(obj) { 32 | if (heap_next === heap.length) heap.push(heap.length + 1); 33 | const idx = heap_next; 34 | heap_next = heap[idx]; 35 | 36 | heap[idx] = obj; 37 | return idx; 38 | } 39 | 40 | function getObject(idx) { 41 | return heap[idx]; 42 | } 43 | 44 | function dropObject(idx) { 45 | if (idx < 36) return; 46 | heap[idx] = heap_next; 47 | heap_next = idx; 48 | } 49 | 50 | function takeObject(idx) { 51 | const ret = getObject(idx); 52 | dropObject(idx); 53 | return ret; 54 | } 55 | 56 | let WASM_VECTOR_LEN = 0; 57 | 58 | let cachedTextEncoder = new TextEncoder("utf-8"); 59 | 60 | const encodeString = 61 | (typeof cachedTextEncoder.encodeInto === "function" 62 | ? function (arg, view) { 63 | return cachedTextEncoder.encodeInto(arg, view); 64 | } 65 | : function (arg, view) { 66 | const buf = cachedTextEncoder.encode(arg); 67 | view.set(buf); 68 | return { 69 | read: arg.length, 70 | written: buf.length, 71 | }; 72 | }); 73 | 74 | function passStringToWasm0(arg, malloc, realloc) { 75 | if (realloc === undefined) { 76 | const buf = cachedTextEncoder.encode(arg); 77 | const ptr = malloc(buf.length); 78 | getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); 79 | WASM_VECTOR_LEN = buf.length; 80 | return ptr; 81 | } 82 | 83 | let len = arg.length; 84 | let ptr = malloc(len); 85 | 86 | const mem = getUint8Memory0(); 87 | 88 | let offset = 0; 89 | 90 | for (; offset < len; offset++) { 91 | const code = arg.charCodeAt(offset); 92 | if (code > 0x7F) break; 93 | mem[ptr + offset] = code; 94 | } 95 | 96 | if (offset !== len) { 97 | if (offset !== 0) { 98 | arg = arg.slice(offset); 99 | } 100 | ptr = realloc(ptr, len, len = offset + arg.length * 3); 101 | const view = getUint8Memory0().subarray(ptr + offset, ptr + len); 102 | const ret = encodeString(arg, view); 103 | 104 | offset += ret.written; 105 | } 106 | 107 | WASM_VECTOR_LEN = offset; 108 | return ptr; 109 | } 110 | /** 111 | * @param {string} filename 112 | * @param {string} src 113 | * @returns {Array} 114 | */ 115 | export function tree(filename, src) { 116 | var ptr0 = passStringToWasm0( 117 | filename, 118 | wasm.__wbindgen_malloc, 119 | wasm.__wbindgen_realloc, 120 | ); 121 | var len0 = WASM_VECTOR_LEN; 122 | var ptr1 = passStringToWasm0( 123 | src, 124 | wasm.__wbindgen_malloc, 125 | wasm.__wbindgen_realloc, 126 | ); 127 | var len1 = WASM_VECTOR_LEN; 128 | var ret = wasm.tree(ptr0, len0, ptr1, len1); 129 | return takeObject(ret); 130 | } 131 | 132 | let cachegetInt32Memory0 = null; 133 | function getInt32Memory0() { 134 | if ( 135 | cachegetInt32Memory0 === null || 136 | cachegetInt32Memory0.buffer !== wasm.memory.buffer 137 | ) { 138 | cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer); 139 | } 140 | return cachegetInt32Memory0; 141 | } 142 | 143 | async function load(module, imports) { 144 | if (typeof Response === "function" && module instanceof Response) { 145 | if (typeof WebAssembly.instantiateStreaming === "function") { 146 | try { 147 | return await WebAssembly.instantiateStreaming(module, imports); 148 | } catch (e) { 149 | if (module.headers.get("Content-Type") != "application/wasm") { 150 | console.warn( 151 | "`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", 152 | e, 153 | ); 154 | } else { 155 | throw e; 156 | } 157 | } 158 | } 159 | 160 | const bytes = await module.arrayBuffer(); 161 | return await WebAssembly.instantiate(bytes, imports); 162 | } else { 163 | const instance = await WebAssembly.instantiate(module, imports); 164 | 165 | if (instance instanceof WebAssembly.Instance) { 166 | return { instance, module }; 167 | } else { 168 | return instance; 169 | } 170 | } 171 | } 172 | 173 | async function init(input) { 174 | if (typeof input === "undefined") { 175 | input = import.meta.url.replace(/\.js$/, "_bg.wasm"); 176 | } 177 | const imports = {}; 178 | imports.wbg = {}; 179 | imports.wbg.__wbg_new_17534eac4df3cd22 = function () { 180 | var ret = new Array(); 181 | return addHeapObject(ret); 182 | }; 183 | imports.wbg.__wbindgen_string_new = function (arg0, arg1) { 184 | var ret = getStringFromWasm0(arg0, arg1); 185 | return addHeapObject(ret); 186 | }; 187 | imports.wbg.__wbg_push_7114ccbf1c58e41f = function (arg0, arg1) { 188 | var ret = getObject(arg0).push(getObject(arg1)); 189 | return ret; 190 | }; 191 | imports.wbg.__wbindgen_object_drop_ref = function (arg0) { 192 | takeObject(arg0); 193 | }; 194 | imports.wbg.__wbg_new_59cb74e423758ede = function () { 195 | var ret = new Error(); 196 | return addHeapObject(ret); 197 | }; 198 | imports.wbg.__wbg_stack_558ba5917b466edd = function (arg0, arg1) { 199 | var ret = getObject(arg1).stack; 200 | var ptr0 = passStringToWasm0( 201 | ret, 202 | wasm.__wbindgen_malloc, 203 | wasm.__wbindgen_realloc, 204 | ); 205 | var len0 = WASM_VECTOR_LEN; 206 | getInt32Memory0()[arg0 / 4 + 1] = len0; 207 | getInt32Memory0()[arg0 / 4 + 0] = ptr0; 208 | }; 209 | imports.wbg.__wbg_error_4bb6c2a97407129a = function (arg0, arg1) { 210 | try { 211 | console.error(getStringFromWasm0(arg0, arg1)); 212 | } finally { 213 | wasm.__wbindgen_free(arg0, arg1); 214 | } 215 | }; 216 | imports.wbg.__wbindgen_throw = function (arg0, arg1) { 217 | throw new Error(getStringFromWasm0(arg0, arg1)); 218 | }; 219 | 220 | if ( 221 | typeof input === "string" || 222 | (typeof Request === "function" && input instanceof Request) || 223 | (typeof URL === "function" && input instanceof URL) 224 | ) { 225 | input = fetch(input); 226 | } 227 | 228 | const { instance, module } = await load(await input, imports); 229 | 230 | wasm = instance.exports; 231 | init.__wbindgen_wasm_module = module; 232 | 233 | return wasm; 234 | } 235 | 236 | export default init; 237 | -------------------------------------------------------------------------------- /analyzer_tree/parser.rs: -------------------------------------------------------------------------------- 1 | // This is a fork of the `deno_swc` and `deno` swc utility. 2 | // Copyright 2020 the deno_swc team. MIT license. 3 | // Copyright 2020 the Deno authors. All rights reserved. MIT license. 4 | // Copyright 2020 nest.land core team. 5 | 6 | use crate::scopes::Scope; 7 | use std::error::Error; 8 | use std::fmt; 9 | use std::sync::Arc; 10 | use std::sync::RwLock; 11 | use swc_atoms::js_word; 12 | use swc_common::comments::SingleThreadedComments; 13 | use swc_common::errors::Diagnostic; 14 | use swc_common::errors::DiagnosticBuilder; 15 | use swc_common::errors::Emitter; 16 | use swc_common::errors::Handler; 17 | use swc_common::errors::HandlerFlags; 18 | use swc_common::FileName; 19 | use swc_common::Globals; 20 | use swc_common::SourceMap; 21 | use swc_common::Span; 22 | use swc_common::DUMMY_SP; 23 | use swc_ecma_ast::{ 24 | ComputedPropName, Expr, ExprOrSpread, Ident, Lit, Prop, PropName, 25 | PropOrSpread, Str, Tpl, 26 | }; 27 | use swc_ecma_parser::lexer::Lexer; 28 | use swc_ecma_parser::EsConfig; 29 | use swc_ecma_parser::JscTarget; 30 | use swc_ecma_parser::Parser; 31 | use swc_ecma_parser::StringInput; 32 | use swc_ecma_parser::Syntax; 33 | use swc_ecma_parser::TsConfig; 34 | use swc_ecma_visit::Fold; 35 | 36 | #[allow(unused)] 37 | pub fn get_default_es_config() -> Syntax { 38 | let mut config = EsConfig::default(); 39 | config.num_sep = true; 40 | config.class_private_props = false; 41 | config.class_private_methods = false; 42 | config.class_props = false; 43 | config.export_default_from = true; 44 | config.export_namespace_from = true; 45 | config.dynamic_import = true; 46 | config.nullish_coalescing = true; 47 | config.optional_chaining = true; 48 | config.import_meta = true; 49 | config.top_level_await = true; 50 | Syntax::Es(config) 51 | } 52 | 53 | #[allow(unused)] 54 | pub fn get_default_ts_config() -> Syntax { 55 | let mut ts_config = TsConfig::default(); 56 | ts_config.dynamic_import = true; 57 | ts_config.decorators = true; 58 | Syntax::Typescript(ts_config) 59 | } 60 | 61 | #[derive(Clone, Debug)] 62 | pub struct SwcDiagnosticBuffer { 63 | pub diagnostics: Vec, 64 | } 65 | 66 | impl Error for SwcDiagnosticBuffer {} 67 | 68 | impl fmt::Display for SwcDiagnosticBuffer { 69 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 70 | let msg = self.diagnostics.join(","); 71 | 72 | f.pad(&msg) 73 | } 74 | } 75 | 76 | impl SwcDiagnosticBuffer { 77 | pub fn from_swc_error( 78 | error_buffer: SwcErrorBuffer, 79 | parser: &AstParser, 80 | ) -> Self { 81 | let s = error_buffer.0.read().unwrap().clone(); 82 | 83 | let diagnostics = s 84 | .iter() 85 | .map(|d| { 86 | let mut msg = d.message(); 87 | 88 | if let Some(span) = d.span.primary_span() { 89 | let location = parser.get_span_location(span); 90 | let filename = match &location.file.name { 91 | FileName::Custom(n) => n, 92 | _ => unreachable!(), 93 | }; 94 | msg = format!( 95 | "{} at {}:{}:{}", 96 | msg, filename, location.line, location.col_display 97 | ); 98 | } 99 | 100 | msg 101 | }) 102 | .collect::>(); 103 | 104 | Self { diagnostics } 105 | } 106 | } 107 | 108 | #[derive(Clone)] 109 | pub struct SwcErrorBuffer(Arc>>); 110 | 111 | impl SwcErrorBuffer { 112 | pub fn default() -> Self { 113 | Self(Arc::new(RwLock::new(vec![]))) 114 | } 115 | } 116 | 117 | impl Emitter for SwcErrorBuffer { 118 | fn emit(&mut self, db: &DiagnosticBuilder) { 119 | self.0.write().unwrap().push((**db).clone()); 120 | } 121 | } 122 | 123 | /// Low-level utility structure with common AST parsing functions. 124 | /// 125 | /// Allows to build more complicated parser by providing a callback 126 | /// to `parse_module`. 127 | pub struct AstParser { 128 | pub buffered_error: SwcErrorBuffer, 129 | pub source_map: Arc, 130 | pub handler: Handler, 131 | pub globals: Globals, 132 | pub comments: SingleThreadedComments, 133 | } 134 | 135 | impl AstParser { 136 | pub fn new() -> Self { 137 | let buffered_error = SwcErrorBuffer::default(); 138 | 139 | let handler = Handler::with_emitter_and_flags( 140 | Box::new(buffered_error.clone()), 141 | HandlerFlags { 142 | dont_buffer_diagnostics: true, 143 | can_emit_warnings: true, 144 | ..Default::default() 145 | }, 146 | ); 147 | 148 | AstParser { 149 | buffered_error, 150 | source_map: Arc::new(SourceMap::default()), 151 | handler, 152 | comments: SingleThreadedComments::default(), 153 | globals: Globals::new(), 154 | } 155 | } 156 | 157 | pub fn parse_module( 158 | &self, 159 | file_name: &str, 160 | syntax: Syntax, 161 | source_code: &str, 162 | ) -> Result { 163 | swc_common::GLOBALS.set(&self.globals, || { 164 | let swc_source_file = self.source_map.new_source_file( 165 | FileName::Custom(file_name.to_string()), 166 | source_code.to_string(), 167 | ); 168 | 169 | let buffered_err = self.buffered_error.clone(); 170 | 171 | let lexer = Lexer::new( 172 | syntax, 173 | JscTarget::Es2019, 174 | StringInput::from(&*swc_source_file), 175 | Some(&self.comments), 176 | ); 177 | 178 | let mut parser = Parser::new_from(lexer); 179 | 180 | parser.parse_module().map_err(move |err| { 181 | let mut diagnostic_builder = err.into_diagnostic(&self.handler); 182 | diagnostic_builder.emit(); 183 | SwcDiagnosticBuffer::from_swc_error(buffered_err, self) 184 | }) 185 | }) 186 | } 187 | 188 | pub fn get_span_location(&self, span: Span) -> swc_common::Loc { 189 | self.source_map.lookup_char_pos(span.lo()) 190 | } 191 | 192 | // pub fn get_span_comments( 193 | // &self, 194 | // span: Span, 195 | // ) -> Vec { 196 | // match self.leading_comments.get(&span.lo()) { 197 | // Some(c) => c.clone(), 198 | // None => vec![], 199 | // } 200 | // } 201 | } 202 | 203 | impl Default for AstParser { 204 | fn default() -> Self { 205 | Self::new() 206 | } 207 | } 208 | 209 | /// A folder to drop all spans of a subtree. 210 | struct SpanDropper; 211 | 212 | impl Fold for SpanDropper { 213 | fn fold_span(&mut self, _: Span) -> Span { 214 | DUMMY_SP 215 | } 216 | } 217 | 218 | /// Provides an additional method to drop spans. 219 | pub(crate) trait DropSpan { 220 | fn drop_span(self) -> Self; 221 | } 222 | 223 | impl DropSpan for Expr { 224 | fn drop_span(self) -> Self { 225 | let mut dropper = SpanDropper; 226 | dropper.fold_expr(self) 227 | } 228 | } 229 | 230 | /// Extracts regex string from an expression, using ScopeManager. 231 | /// If the passed expression is not regular expression, this will return `None`. 232 | #[allow(dead_code)] 233 | pub(crate) fn extract_regex( 234 | root_scope: &Scope, 235 | expr_span: Span, 236 | expr_ident: &Ident, 237 | expr_args: &[ExprOrSpread], 238 | ) -> Option { 239 | if expr_ident.sym != js_word!("RegExp") { 240 | return None; 241 | } 242 | 243 | let scope = root_scope.get_scope_for_span(expr_span); 244 | if scope.get_binding(&expr_ident.sym).is_some() { 245 | return None; 246 | } 247 | 248 | match expr_args.get(0) { 249 | Some(first_arg) => match &*first_arg.expr { 250 | Expr::Lit(Lit::Str(literal)) => Some(literal.value.to_string()), 251 | Expr::Lit(Lit::Regex(regex)) => Some(regex.exp.to_string()), 252 | _ => None, 253 | }, 254 | None => None, 255 | } 256 | } 257 | 258 | pub(crate) trait Key { 259 | fn get_key(&self) -> Option; 260 | } 261 | 262 | impl Key for PropOrSpread { 263 | fn get_key(&self) -> Option { 264 | use PropOrSpread::*; 265 | match self { 266 | Prop(p) => (&**p).get_key(), 267 | Spread(_) => None, 268 | } 269 | } 270 | } 271 | 272 | impl Key for Prop { 273 | fn get_key(&self) -> Option { 274 | use Prop::*; 275 | match self { 276 | KeyValue(key_value) => key_value.key.get_key(), 277 | Getter(getter) => getter.key.get_key(), 278 | Setter(setter) => setter.key.get_key(), 279 | Method(method) => method.key.get_key(), 280 | Shorthand(_) => None, 281 | Assign(_) => None, 282 | } 283 | } 284 | } 285 | 286 | impl Key for PropName { 287 | fn get_key(&self) -> Option { 288 | match self { 289 | PropName::Ident(identifier) => Some(identifier.sym.to_string()), 290 | PropName::Str(str) => Some(str.value.to_string()), 291 | PropName::Num(num) => Some(num.to_string()), 292 | PropName::Computed(ComputedPropName { ref expr, .. }) => match &**expr { 293 | Expr::Lit(Lit::Str(Str { ref value, .. })) => Some(value.to_string()), 294 | Expr::Tpl(Tpl { 295 | ref exprs, 296 | ref quasis, 297 | .. 298 | }) => { 299 | if exprs.is_empty() { 300 | quasis.iter().next().map(|q| q.raw.value.to_string()) 301 | } else { 302 | None 303 | } 304 | } 305 | _ => None, 306 | }, 307 | } 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /analyzer_tree/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "Inflector" 5 | version = "0.11.4" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" 8 | dependencies = [ 9 | "lazy_static", 10 | "regex", 11 | ] 12 | 13 | [[package]] 14 | name = "aho-corasick" 15 | version = "0.7.13" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" 18 | dependencies = [ 19 | "memchr", 20 | ] 21 | 22 | [[package]] 23 | name = "ast_node" 24 | version = "0.7.0" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "fd6ee2941db3551563d29eaf5214cd3d7b2f322e0c0e3954f5ae020f860bae8c" 27 | dependencies = [ 28 | "darling", 29 | "pmutil", 30 | "proc-macro2", 31 | "quote", 32 | "swc_macros_common", 33 | "syn", 34 | ] 35 | 36 | [[package]] 37 | name = "autocfg" 38 | version = "1.0.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 41 | 42 | [[package]] 43 | name = "byteorder" 44 | version = "1.3.4" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 47 | 48 | [[package]] 49 | name = "cfg-if" 50 | version = "0.1.10" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 53 | 54 | [[package]] 55 | name = "darling" 56 | version = "0.10.2" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" 59 | dependencies = [ 60 | "darling_core", 61 | "darling_macro", 62 | ] 63 | 64 | [[package]] 65 | name = "darling_core" 66 | version = "0.10.2" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" 69 | dependencies = [ 70 | "fnv", 71 | "ident_case", 72 | "proc-macro2", 73 | "quote", 74 | "strsim", 75 | "syn", 76 | ] 77 | 78 | [[package]] 79 | name = "darling_macro" 80 | version = "0.10.2" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" 83 | dependencies = [ 84 | "darling_core", 85 | "quote", 86 | "syn", 87 | ] 88 | 89 | [[package]] 90 | name = "either" 91 | version = "1.5.3" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" 94 | 95 | [[package]] 96 | name = "enum_kind" 97 | version = "0.2.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "6e57153e35187d51f08471d5840459ff29093473e7bedd004a1414985aab92f3" 100 | dependencies = [ 101 | "pmutil", 102 | "proc-macro2", 103 | "swc_macros_common", 104 | "syn", 105 | ] 106 | 107 | [[package]] 108 | name = "fnv" 109 | version = "1.0.7" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 112 | 113 | [[package]] 114 | name = "from_variant" 115 | version = "0.1.2" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "039885ad6579a86b94ad8df696cce8c530da496bf7b07b12fec8d6c4cd654bb9" 118 | dependencies = [ 119 | "pmutil", 120 | "proc-macro2", 121 | "swc_macros_common", 122 | "syn", 123 | ] 124 | 125 | [[package]] 126 | name = "fxhash" 127 | version = "0.2.1" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 130 | dependencies = [ 131 | "byteorder", 132 | ] 133 | 134 | [[package]] 135 | name = "getrandom" 136 | version = "0.1.14" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 139 | dependencies = [ 140 | "cfg-if", 141 | "libc", 142 | "wasi", 143 | ] 144 | 145 | [[package]] 146 | name = "ident_case" 147 | version = "1.0.1" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 150 | 151 | [[package]] 152 | name = "is-macro" 153 | version = "0.1.8" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "04807f3dc9e3ea39af3f8469a5297267faf94859637afb836b33f47d9b2650ee" 156 | dependencies = [ 157 | "Inflector", 158 | "pmutil", 159 | "proc-macro2", 160 | "quote", 161 | "syn", 162 | ] 163 | 164 | [[package]] 165 | name = "itoa" 166 | version = "0.4.6" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 169 | 170 | [[package]] 171 | name = "lazy_static" 172 | version = "1.4.0" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 175 | 176 | [[package]] 177 | name = "libc" 178 | version = "0.2.74" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" 181 | 182 | [[package]] 183 | name = "log" 184 | version = "0.4.11" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 187 | dependencies = [ 188 | "cfg-if", 189 | ] 190 | 191 | [[package]] 192 | name = "memchr" 193 | version = "2.3.3" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 196 | 197 | [[package]] 198 | name = "nest_analyzer" 199 | version = "0.0.1" 200 | dependencies = [ 201 | "regex", 202 | "serde", 203 | "serde_derive", 204 | "serde_json", 205 | "swc_atoms", 206 | "swc_common", 207 | "swc_ecma_ast", 208 | "swc_ecma_parser", 209 | "swc_ecma_visit", 210 | ] 211 | 212 | [[package]] 213 | name = "new_debug_unreachable" 214 | version = "1.0.4" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" 217 | 218 | [[package]] 219 | name = "num-bigint" 220 | version = "0.2.6" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" 223 | dependencies = [ 224 | "autocfg", 225 | "num-integer", 226 | "num-traits", 227 | "serde", 228 | ] 229 | 230 | [[package]] 231 | name = "num-integer" 232 | version = "0.1.43" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 235 | dependencies = [ 236 | "autocfg", 237 | "num-traits", 238 | ] 239 | 240 | [[package]] 241 | name = "num-traits" 242 | version = "0.2.12" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 245 | dependencies = [ 246 | "autocfg", 247 | ] 248 | 249 | [[package]] 250 | name = "once_cell" 251 | version = "1.4.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" 254 | 255 | [[package]] 256 | name = "owning_ref" 257 | version = "0.4.1" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" 260 | dependencies = [ 261 | "stable_deref_trait", 262 | ] 263 | 264 | [[package]] 265 | name = "phf_generator" 266 | version = "0.8.0" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" 269 | dependencies = [ 270 | "phf_shared", 271 | "rand", 272 | ] 273 | 274 | [[package]] 275 | name = "phf_shared" 276 | version = "0.8.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" 279 | dependencies = [ 280 | "siphasher", 281 | ] 282 | 283 | [[package]] 284 | name = "pmutil" 285 | version = "0.5.3" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" 288 | dependencies = [ 289 | "proc-macro2", 290 | "quote", 291 | "syn", 292 | ] 293 | 294 | [[package]] 295 | name = "ppv-lite86" 296 | version = "0.2.8" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" 299 | 300 | [[package]] 301 | name = "precomputed-hash" 302 | version = "0.1.1" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 305 | 306 | [[package]] 307 | name = "proc-macro2" 308 | version = "1.0.19" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" 311 | dependencies = [ 312 | "unicode-xid", 313 | ] 314 | 315 | [[package]] 316 | name = "quote" 317 | version = "1.0.7" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 320 | dependencies = [ 321 | "proc-macro2", 322 | ] 323 | 324 | [[package]] 325 | name = "rand" 326 | version = "0.7.3" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 329 | dependencies = [ 330 | "getrandom", 331 | "libc", 332 | "rand_chacha", 333 | "rand_core", 334 | "rand_hc", 335 | "rand_pcg", 336 | ] 337 | 338 | [[package]] 339 | name = "rand_chacha" 340 | version = "0.2.2" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 343 | dependencies = [ 344 | "ppv-lite86", 345 | "rand_core", 346 | ] 347 | 348 | [[package]] 349 | name = "rand_core" 350 | version = "0.5.1" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 353 | dependencies = [ 354 | "getrandom", 355 | ] 356 | 357 | [[package]] 358 | name = "rand_hc" 359 | version = "0.2.0" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 362 | dependencies = [ 363 | "rand_core", 364 | ] 365 | 366 | [[package]] 367 | name = "rand_pcg" 368 | version = "0.2.1" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" 371 | dependencies = [ 372 | "rand_core", 373 | ] 374 | 375 | [[package]] 376 | name = "regex" 377 | version = "1.3.9" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" 380 | dependencies = [ 381 | "aho-corasick", 382 | "memchr", 383 | "regex-syntax", 384 | "thread_local", 385 | ] 386 | 387 | [[package]] 388 | name = "regex-syntax" 389 | version = "0.6.18" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" 392 | 393 | [[package]] 394 | name = "ryu" 395 | version = "1.0.5" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 398 | 399 | [[package]] 400 | name = "scoped-tls" 401 | version = "1.0.0" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 404 | 405 | [[package]] 406 | name = "serde" 407 | version = "1.0.114" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" 410 | dependencies = [ 411 | "serde_derive", 412 | ] 413 | 414 | [[package]] 415 | name = "serde_derive" 416 | version = "1.0.114" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" 419 | dependencies = [ 420 | "proc-macro2", 421 | "quote", 422 | "syn", 423 | ] 424 | 425 | [[package]] 426 | name = "serde_json" 427 | version = "1.0.57" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" 430 | dependencies = [ 431 | "itoa", 432 | "ryu", 433 | "serde", 434 | ] 435 | 436 | [[package]] 437 | name = "siphasher" 438 | version = "0.3.3" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" 441 | 442 | [[package]] 443 | name = "smallvec" 444 | version = "1.4.1" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" 447 | 448 | [[package]] 449 | name = "stable_deref_trait" 450 | version = "1.2.0" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 453 | 454 | [[package]] 455 | name = "string_cache" 456 | version = "0.8.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a" 459 | dependencies = [ 460 | "lazy_static", 461 | "new_debug_unreachable", 462 | "phf_shared", 463 | "precomputed-hash", 464 | "serde", 465 | ] 466 | 467 | [[package]] 468 | name = "string_cache_codegen" 469 | version = "0.5.1" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97" 472 | dependencies = [ 473 | "phf_generator", 474 | "phf_shared", 475 | "proc-macro2", 476 | "quote", 477 | ] 478 | 479 | [[package]] 480 | name = "string_enum" 481 | version = "0.3.0" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "94fdb6536756cfd35ee18b9a9972ab2a699d405cc57e0ad0532022960f30d581" 484 | dependencies = [ 485 | "pmutil", 486 | "proc-macro2", 487 | "quote", 488 | "swc_macros_common", 489 | "syn", 490 | ] 491 | 492 | [[package]] 493 | name = "strsim" 494 | version = "0.9.3" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 497 | 498 | [[package]] 499 | name = "swc_atoms" 500 | version = "0.2.2" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "46682d5a27e12d8b86168ea2fcb3aae2e0625f24bf109dee4bca24b2b51e03ce" 503 | dependencies = [ 504 | "string_cache", 505 | "string_cache_codegen", 506 | ] 507 | 508 | [[package]] 509 | name = "swc_common" 510 | version = "0.9.1" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "458740fb57fe3f2b748819c1db0f448d920d4a64b00e802a485bd41290ef6790" 513 | dependencies = [ 514 | "ast_node", 515 | "cfg-if", 516 | "either", 517 | "from_variant", 518 | "fxhash", 519 | "log", 520 | "once_cell", 521 | "owning_ref", 522 | "scoped-tls", 523 | "serde", 524 | "swc_visit", 525 | "unicode-width", 526 | ] 527 | 528 | [[package]] 529 | name = "swc_ecma_ast" 530 | version = "0.28.0" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "7dab5379d61147d5d804a92b073a1d38c5a7bd86af09ff19b9f057a1992c1a6e" 533 | dependencies = [ 534 | "enum_kind", 535 | "is-macro", 536 | "num-bigint", 537 | "serde", 538 | "string_enum", 539 | "swc_atoms", 540 | "swc_common", 541 | ] 542 | 543 | [[package]] 544 | name = "swc_ecma_parser" 545 | version = "0.33.3" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "b9db9dad7e75ed1f35a92483de6468247e99e271463d93bf07b51d0c36c8652f" 548 | dependencies = [ 549 | "either", 550 | "enum_kind", 551 | "fxhash", 552 | "log", 553 | "num-bigint", 554 | "serde", 555 | "smallvec", 556 | "swc_atoms", 557 | "swc_common", 558 | "swc_ecma_ast", 559 | "swc_ecma_parser_macros", 560 | "swc_ecma_visit", 561 | "unicode-xid", 562 | ] 563 | 564 | [[package]] 565 | name = "swc_ecma_parser_macros" 566 | version = "0.4.1" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "8798810e2c79b884cf238bcb72b4bd12375121ee91724f1ceeb54b6e38a138e7" 569 | dependencies = [ 570 | "pmutil", 571 | "proc-macro2", 572 | "quote", 573 | "swc_macros_common", 574 | "syn", 575 | ] 576 | 577 | [[package]] 578 | name = "swc_ecma_visit" 579 | version = "0.13.0" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "cdf59a90e7138c6af50a30d81a2cba6147c541bdc181f127ce8bd253721f8517" 582 | dependencies = [ 583 | "num-bigint", 584 | "swc_atoms", 585 | "swc_common", 586 | "swc_ecma_ast", 587 | "swc_visit", 588 | ] 589 | 590 | [[package]] 591 | name = "swc_macros_common" 592 | version = "0.3.1" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "18a9f27d290938370597d363df9a77ba4be8e2bc99f32f69eb5245cdeed3c512" 595 | dependencies = [ 596 | "pmutil", 597 | "proc-macro2", 598 | "quote", 599 | "syn", 600 | ] 601 | 602 | [[package]] 603 | name = "swc_visit" 604 | version = "0.1.0" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "6683cd5f6b97498e887e0d4da4c44ddafb755dfb0d950c52af99344fad23fec9" 607 | dependencies = [ 608 | "either", 609 | "swc_visit_macros", 610 | ] 611 | 612 | [[package]] 613 | name = "swc_visit_macros" 614 | version = "0.1.0" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "1802720b0826a2ae6fac5be1305b1277cf1e83418549f465edf075d8016a1548" 617 | dependencies = [ 618 | "Inflector", 619 | "pmutil", 620 | "proc-macro2", 621 | "quote", 622 | "swc_macros_common", 623 | "syn", 624 | ] 625 | 626 | [[package]] 627 | name = "syn" 628 | version = "1.0.36" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250" 631 | dependencies = [ 632 | "proc-macro2", 633 | "quote", 634 | "unicode-xid", 635 | ] 636 | 637 | [[package]] 638 | name = "thread_local" 639 | version = "1.0.1" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 642 | dependencies = [ 643 | "lazy_static", 644 | ] 645 | 646 | [[package]] 647 | name = "unicode-width" 648 | version = "0.1.8" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 651 | 652 | [[package]] 653 | name = "unicode-xid" 654 | version = "0.2.1" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 657 | 658 | [[package]] 659 | name = "wasi" 660 | version = "0.9.0+wasi-snapshot-preview1" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 663 | -------------------------------------------------------------------------------- /diagrams/analyzer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | coderuntime analyzerstatic analyzercompile typescriptinject listeners to ASTsafe code evaluation and collect diagnosticsparse ASTvisit AST nodes and collect diagnosticsdiagnostics -------------------------------------------------------------------------------- /diagrams/analyzer.excalidraw: -------------------------------------------------------------------------------- 1 | { 2 | "type": "excalidraw", 3 | "version": 2, 4 | "source": "https://excalidraw.com", 5 | "elements": [ 6 | { 7 | "type": "text", 8 | "version": 9, 9 | "versionNonce": 622089002, 10 | "isDeleted": false, 11 | "id": "zL5I7n0NCfNCZDGDXQOZP", 12 | "fillStyle": "hachure", 13 | "strokeWidth": 1, 14 | "strokeStyle": "solid", 15 | "roughness": 1, 16 | "opacity": 100, 17 | "angle": 0, 18 | "x": 465, 19 | "y": 144, 20 | "strokeColor": "#000000", 21 | "backgroundColor": "transparent", 22 | "width": 43, 23 | "height": 26, 24 | "seed": 117356586, 25 | "groupIds": [], 26 | "strokeSharpness": "sharp", 27 | "boundElementIds": [], 28 | "fontSize": 20, 29 | "fontFamily": 1, 30 | "text": "code", 31 | "baseline": 18, 32 | "textAlign": "left", 33 | "verticalAlign": "top" 34 | }, 35 | { 36 | "type": "rectangle", 37 | "version": 19, 38 | "versionNonce": 497699446, 39 | "isDeleted": false, 40 | "id": "mBLXeLmMSe2fbpHrTfv-8", 41 | "fillStyle": "hachure", 42 | "strokeWidth": 1, 43 | "strokeStyle": "solid", 44 | "roughness": 1, 45 | "opacity": 100, 46 | "angle": 0, 47 | "x": 444, 48 | "y": 136, 49 | "strokeColor": "#000000", 50 | "backgroundColor": "transparent", 51 | "width": 82, 52 | "height": 38, 53 | "seed": 594365558, 54 | "groupIds": [], 55 | "strokeSharpness": "sharp", 56 | "boundElementIds": [ 57 | "emjE2gxrulIA_i7XDpYWD", 58 | "tZww6s0WjX5Dojq0fPz2I" 59 | ] 60 | }, 61 | { 62 | "type": "arrow", 63 | "version": 11, 64 | "versionNonce": 1230777322, 65 | "isDeleted": false, 66 | "id": "emjE2gxrulIA_i7XDpYWD", 67 | "fillStyle": "hachure", 68 | "strokeWidth": 1, 69 | "strokeStyle": "solid", 70 | "roughness": 1, 71 | "opacity": 100, 72 | "angle": 0, 73 | "x": 527, 74 | "y": 158, 75 | "strokeColor": "#000000", 76 | "backgroundColor": "transparent", 77 | "width": 50, 78 | "height": 3, 79 | "seed": 661043126, 80 | "groupIds": [], 81 | "strokeSharpness": "round", 82 | "boundElementIds": [], 83 | "startBinding": { 84 | "elementId": "mBLXeLmMSe2fbpHrTfv-8", 85 | "focus": 0.25722273998136064, 86 | "gap": 1 87 | }, 88 | "endBinding": { 89 | "elementId": "qW-a6TlTnaUG_YKPtvK0W", 90 | "focus": 0.3077790304396843, 91 | "gap": 12 92 | }, 93 | "points": [ 94 | [ 95 | 0, 96 | 0 97 | ], 98 | [ 99 | 50, 100 | -3 101 | ] 102 | ], 103 | "lastCommittedPoint": null 104 | }, 105 | { 106 | "type": "arrow", 107 | "version": 47, 108 | "versionNonce": 1284139306, 109 | "isDeleted": false, 110 | "id": "tZww6s0WjX5Dojq0fPz2I", 111 | "fillStyle": "hachure", 112 | "strokeWidth": 1, 113 | "strokeStyle": "solid", 114 | "roughness": 1, 115 | "opacity": 100, 116 | "angle": 0, 117 | "x": 442, 118 | "y": 156.27115707558062, 119 | "strokeColor": "#000000", 120 | "backgroundColor": "transparent", 121 | "width": 46, 122 | "height": 1.3351922151139775, 123 | "seed": 1438840630, 124 | "groupIds": [], 125 | "strokeSharpness": "round", 126 | "boundElementIds": [], 127 | "startBinding": { 128 | "elementId": "mBLXeLmMSe2fbpHrTfv-8", 129 | "focus": -0.0011402508551881412, 130 | "gap": 2 131 | }, 132 | "endBinding": { 133 | "elementId": "vODDf_1wVihUYIUFLLzQo", 134 | "focus": 0.3400309119010819, 135 | "gap": 13 136 | }, 137 | "points": [ 138 | [ 139 | 0, 140 | 0 141 | ], 142 | [ 143 | -46, 144 | 1.3351922151139775 145 | ] 146 | ], 147 | "lastCommittedPoint": null 148 | }, 149 | { 150 | "type": "text", 151 | "version": 29, 152 | "versionNonce": 716988342, 153 | "isDeleted": false, 154 | "id": "qW-a6TlTnaUG_YKPtvK0W", 155 | "fillStyle": "hachure", 156 | "strokeWidth": 1, 157 | "strokeStyle": "solid", 158 | "roughness": 1, 159 | "opacity": 100, 160 | "angle": 0, 161 | "x": 589, 162 | "y": 142, 163 | "strokeColor": "#000000", 164 | "backgroundColor": "transparent", 165 | "width": 158, 166 | "height": 26, 167 | "seed": 1243974582, 168 | "groupIds": [], 169 | "strokeSharpness": "sharp", 170 | "boundElementIds": [ 171 | "emjE2gxrulIA_i7XDpYWD" 172 | ], 173 | "fontSize": 20, 174 | "fontFamily": 1, 175 | "text": "runtime analyzer", 176 | "baseline": 18, 177 | "textAlign": "left", 178 | "verticalAlign": "top" 179 | }, 180 | { 181 | "type": "rectangle", 182 | "version": 42, 183 | "versionNonce": 290549482, 184 | "isDeleted": false, 185 | "id": "vxEVMy7z9h9g_xiXOYP0e", 186 | "fillStyle": "hachure", 187 | "strokeWidth": 1, 188 | "strokeStyle": "solid", 189 | "roughness": 1, 190 | "opacity": 100, 191 | "angle": 0, 192 | "x": 579, 193 | "y": 137, 194 | "strokeColor": "#000000", 195 | "backgroundColor": "transparent", 196 | "width": 183, 197 | "height": 36, 198 | "seed": 784529654, 199 | "groupIds": [], 200 | "strokeSharpness": "sharp", 201 | "boundElementIds": [ 202 | "mMyjcl8KaETeLvK7darLK" 203 | ] 204 | }, 205 | { 206 | "type": "text", 207 | "version": 48, 208 | "versionNonce": 547911914, 209 | "isDeleted": false, 210 | "id": "vODDf_1wVihUYIUFLLzQo", 211 | "fillStyle": "hachure", 212 | "strokeWidth": 1, 213 | "strokeStyle": "solid", 214 | "roughness": 1, 215 | "opacity": 100, 216 | "angle": 0, 217 | "x": 233, 218 | "y": 142, 219 | "strokeColor": "#000000", 220 | "backgroundColor": "transparent", 221 | "width": 150, 222 | "height": 26, 223 | "seed": 1794284662, 224 | "groupIds": [], 225 | "strokeSharpness": "sharp", 226 | "boundElementIds": [ 227 | "tZww6s0WjX5Dojq0fPz2I" 228 | ], 229 | "fontSize": 20, 230 | "fontFamily": 1, 231 | "text": "static analyzer", 232 | "baseline": 18, 233 | "textAlign": "left", 234 | "verticalAlign": "top" 235 | }, 236 | { 237 | "type": "rectangle", 238 | "version": 52, 239 | "versionNonce": 133685046, 240 | "isDeleted": false, 241 | "id": "hCAirkhoFu-3Nz_lxTlhG", 242 | "fillStyle": "hachure", 243 | "strokeWidth": 1, 244 | "strokeStyle": "solid", 245 | "roughness": 1, 246 | "opacity": 100, 247 | "angle": 0, 248 | "x": 221, 249 | "y": 137, 250 | "strokeColor": "#000000", 251 | "backgroundColor": "transparent", 252 | "width": 170, 253 | "height": 32, 254 | "seed": 1128555178, 255 | "groupIds": [], 256 | "strokeSharpness": "sharp", 257 | "boundElementIds": [ 258 | "TLjkYWvfSkO6Dx1Evw-q3" 259 | ] 260 | }, 261 | { 262 | "type": "arrow", 263 | "version": 9, 264 | "versionNonce": 1371480118, 265 | "isDeleted": false, 266 | "id": "mMyjcl8KaETeLvK7darLK", 267 | "fillStyle": "hachure", 268 | "strokeWidth": 1, 269 | "strokeStyle": "solid", 270 | "roughness": 1, 271 | "opacity": 100, 272 | "angle": 0, 273 | "x": 667, 274 | "y": 180, 275 | "strokeColor": "#000000", 276 | "backgroundColor": "transparent", 277 | "width": 1, 278 | "height": 38, 279 | "seed": 1249358826, 280 | "groupIds": [], 281 | "strokeSharpness": "round", 282 | "boundElementIds": [], 283 | "startBinding": { 284 | "elementId": "vxEVMy7z9h9g_xiXOYP0e", 285 | "focus": 0.04520743919885551, 286 | "gap": 7 287 | }, 288 | "endBinding": { 289 | "elementId": "tqxCees5gLAanZD-i7oaj", 290 | "focus": -0.11618638871857756, 291 | "gap": 7 292 | }, 293 | "points": [ 294 | [ 295 | 0, 296 | 0 297 | ], 298 | [ 299 | 1, 300 | 38 301 | ] 302 | ], 303 | "lastCommittedPoint": null 304 | }, 305 | { 306 | "type": "text", 307 | "version": 43, 308 | "versionNonce": 1212544810, 309 | "isDeleted": false, 310 | "id": "tqxCees5gLAanZD-i7oaj", 311 | "fillStyle": "hachure", 312 | "strokeWidth": 1, 313 | "strokeStyle": "solid", 314 | "roughness": 1, 315 | "opacity": 100, 316 | "angle": 0, 317 | "x": 593, 318 | "y": 225, 319 | "strokeColor": "#000000", 320 | "backgroundColor": "transparent", 321 | "width": 171, 322 | "height": 26, 323 | "seed": 1943536042, 324 | "groupIds": [], 325 | "strokeSharpness": "sharp", 326 | "boundElementIds": [ 327 | "mMyjcl8KaETeLvK7darLK", 328 | "K8InfI6ZbDKiEoRLKuyEY" 329 | ], 330 | "fontSize": 20, 331 | "fontFamily": 1, 332 | "text": "compile typescript", 333 | "baseline": 18, 334 | "textAlign": "left", 335 | "verticalAlign": "top" 336 | }, 337 | { 338 | "type": "arrow", 339 | "version": 20, 340 | "versionNonce": 675813674, 341 | "isDeleted": false, 342 | "id": "K8InfI6ZbDKiEoRLKuyEY", 343 | "fillStyle": "hachure", 344 | "strokeWidth": 1, 345 | "strokeStyle": "solid", 346 | "roughness": 1, 347 | "opacity": 100, 348 | "angle": 0, 349 | "x": 670, 350 | "y": 256, 351 | "strokeColor": "#000000", 352 | "backgroundColor": "transparent", 353 | "width": 3, 354 | "height": 49, 355 | "seed": 1447579882, 356 | "groupIds": [], 357 | "strokeSharpness": "round", 358 | "boundElementIds": [], 359 | "startBinding": { 360 | "elementId": "tqxCees5gLAanZD-i7oaj", 361 | "focus": 0.11126877143194988, 362 | "gap": 5 363 | }, 364 | "endBinding": { 365 | "elementId": "6zYu2a9hqWKk7UmKlGfYL", 366 | "focus": -0.09046240238659298, 367 | "gap": 3 368 | }, 369 | "points": [ 370 | [ 371 | 0, 372 | 0 373 | ], 374 | [ 375 | 3, 376 | 49 377 | ] 378 | ], 379 | "lastCommittedPoint": null 380 | }, 381 | { 382 | "type": "text", 383 | "version": 45, 384 | "versionNonce": 1105158762, 385 | "isDeleted": false, 386 | "id": "6zYu2a9hqWKk7UmKlGfYL", 387 | "fillStyle": "hachure", 388 | "strokeWidth": 1, 389 | "strokeStyle": "solid", 390 | "roughness": 1, 391 | "opacity": 100, 392 | "angle": 0, 393 | "x": 569, 394 | "y": 308, 395 | "strokeColor": "#000000", 396 | "backgroundColor": "transparent", 397 | "width": 231, 398 | "height": 26, 399 | "seed": 596738538, 400 | "groupIds": [], 401 | "strokeSharpness": "sharp", 402 | "boundElementIds": [ 403 | "K8InfI6ZbDKiEoRLKuyEY", 404 | "lUETiG6id-acCR86Z8JwV" 405 | ], 406 | "fontSize": 20, 407 | "fontFamily": 1, 408 | "text": "inject listeners to AST", 409 | "baseline": 18, 410 | "textAlign": "left", 411 | "verticalAlign": "top" 412 | }, 413 | { 414 | "type": "arrow", 415 | "version": 28, 416 | "versionNonce": 335685226, 417 | "isDeleted": false, 418 | "id": "lUETiG6id-acCR86Z8JwV", 419 | "fillStyle": "hachure", 420 | "strokeWidth": 1, 421 | "strokeStyle": "solid", 422 | "roughness": 1, 423 | "opacity": 100, 424 | "angle": 0, 425 | "x": 675, 426 | "y": 347, 427 | "strokeColor": "#000000", 428 | "backgroundColor": "transparent", 429 | "width": 2, 430 | "height": 51, 431 | "seed": 2047387574, 432 | "groupIds": [], 433 | "strokeSharpness": "round", 434 | "boundElementIds": [], 435 | "startBinding": { 436 | "elementId": "6zYu2a9hqWKk7UmKlGfYL", 437 | "focus": 0.09067861066508916, 438 | "gap": 13 439 | }, 440 | "endBinding": { 441 | "elementId": "9qzv4vFAPHpsbwelW8Uhs", 442 | "focus": -0.13123301100663962, 443 | "gap": 3 444 | }, 445 | "points": [ 446 | [ 447 | 0, 448 | 0 449 | ], 450 | [ 451 | 2, 452 | 51 453 | ] 454 | ], 455 | "lastCommittedPoint": null 456 | }, 457 | { 458 | "type": "text", 459 | "version": 121, 460 | "versionNonce": 26026294, 461 | "isDeleted": false, 462 | "id": "9qzv4vFAPHpsbwelW8Uhs", 463 | "fillStyle": "hachure", 464 | "strokeWidth": 1, 465 | "strokeStyle": "solid", 466 | "roughness": 1, 467 | "opacity": 100, 468 | "angle": 0, 469 | "x": 525, 470 | "y": 401, 471 | "strokeColor": "#000000", 472 | "backgroundColor": "transparent", 473 | "width": 400.99999999999994, 474 | "height": 23.74943052391798, 475 | "seed": 1432061226, 476 | "groupIds": [], 477 | "strokeSharpness": "sharp", 478 | "boundElementIds": [ 479 | "lUETiG6id-acCR86Z8JwV" 480 | ], 481 | "fontSize": 18.268792710706137, 482 | "fontFamily": 1, 483 | "text": "safe code evaluation and collect diagnostics", 484 | "baseline": 16.74943052391798, 485 | "textAlign": "left", 486 | "verticalAlign": "top" 487 | }, 488 | { 489 | "type": "arrow", 490 | "version": 14, 491 | "versionNonce": 414676074, 492 | "isDeleted": false, 493 | "id": "TLjkYWvfSkO6Dx1Evw-q3", 494 | "fillStyle": "hachure", 495 | "strokeWidth": 1, 496 | "strokeStyle": "solid", 497 | "roughness": 1, 498 | "opacity": 100, 499 | "angle": 0, 500 | "x": 292, 501 | "y": 176, 502 | "strokeColor": "#000000", 503 | "backgroundColor": "transparent", 504 | "width": 3, 505 | "height": 38, 506 | "seed": 241229430, 507 | "groupIds": [], 508 | "strokeSharpness": "round", 509 | "boundElementIds": [], 510 | "startBinding": { 511 | "elementId": "hCAirkhoFu-3Nz_lxTlhG", 512 | "focus": 0.18334350213544845, 513 | "gap": 7 514 | }, 515 | "endBinding": null, 516 | "points": [ 517 | [ 518 | 0, 519 | 0 520 | ], 521 | [ 522 | 3, 523 | 38 524 | ] 525 | ], 526 | "lastCommittedPoint": null 527 | }, 528 | { 529 | "type": "text", 530 | "version": 70, 531 | "versionNonce": 353552234, 532 | "isDeleted": false, 533 | "id": "zh3pJhq_9N9rhUxj2KE5m", 534 | "fillStyle": "hachure", 535 | "strokeWidth": 1, 536 | "strokeStyle": "solid", 537 | "roughness": 1, 538 | "opacity": 100, 539 | "angle": 0, 540 | "x": 257, 541 | "y": 221, 542 | "strokeColor": "#000000", 543 | "backgroundColor": "transparent", 544 | "width": 109, 545 | "height": 26, 546 | "seed": 1734510710, 547 | "groupIds": [], 548 | "strokeSharpness": "sharp", 549 | "boundElementIds": [], 550 | "fontSize": 20, 551 | "fontFamily": 1, 552 | "text": "parse AST", 553 | "baseline": 18, 554 | "textAlign": "left", 555 | "verticalAlign": "top" 556 | }, 557 | { 558 | "type": "arrow", 559 | "version": 15, 560 | "versionNonce": 1525962154, 561 | "isDeleted": false, 562 | "id": "QSC9AMllU-tqkVADNC7a-", 563 | "fillStyle": "hachure", 564 | "strokeWidth": 1, 565 | "strokeStyle": "solid", 566 | "roughness": 1, 567 | "opacity": 100, 568 | "angle": 0, 569 | "x": 287, 570 | "y": 263, 571 | "strokeColor": "#000000", 572 | "backgroundColor": "transparent", 573 | "width": 1, 574 | "height": 43, 575 | "seed": 383404586, 576 | "groupIds": [], 577 | "strokeSharpness": "round", 578 | "boundElementIds": [], 579 | "startBinding": null, 580 | "endBinding": { 581 | "elementId": "YrrtJ3-6NKfV0oi2nu-CU", 582 | "focus": -0.43062827225130884, 583 | "gap": 13 584 | }, 585 | "points": [ 586 | [ 587 | 0, 588 | 0 589 | ], 590 | [ 591 | -1, 592 | 43 593 | ] 594 | ], 595 | "lastCommittedPoint": null 596 | }, 597 | { 598 | "type": "text", 599 | "version": 68, 600 | "versionNonce": 1030009334, 601 | "isDeleted": false, 602 | "id": "YrrtJ3-6NKfV0oi2nu-CU", 603 | "fillStyle": "hachure", 604 | "strokeWidth": 1, 605 | "strokeStyle": "solid", 606 | "roughness": 1, 607 | "opacity": 100, 608 | "angle": 0, 609 | "x": 225, 610 | "y": 319, 611 | "strokeColor": "#000000", 612 | "backgroundColor": "transparent", 613 | "width": 212, 614 | "height": 52, 615 | "seed": 308749878, 616 | "groupIds": [], 617 | "strokeSharpness": "sharp", 618 | "boundElementIds": [ 619 | "QSC9AMllU-tqkVADNC7a-" 620 | ], 621 | "fontSize": 20, 622 | "fontFamily": 1, 623 | "text": "visit AST nodes and \ncollect diagnostics", 624 | "baseline": 44, 625 | "textAlign": "left", 626 | "verticalAlign": "top" 627 | }, 628 | { 629 | "type": "arrow", 630 | "version": 20, 631 | "versionNonce": 1908690794, 632 | "isDeleted": false, 633 | "id": "JUWfh7bofKgjCi5eKlIZq", 634 | "fillStyle": "hachure", 635 | "strokeWidth": 1, 636 | "strokeStyle": "solid", 637 | "roughness": 1, 638 | "opacity": 100, 639 | "angle": 0, 640 | "x": 284, 641 | "y": 393, 642 | "strokeColor": "#000000", 643 | "backgroundColor": "transparent", 644 | "width": 80, 645 | "height": 91, 646 | "seed": 1697055542, 647 | "groupIds": [], 648 | "strokeSharpness": "round", 649 | "boundElementIds": [], 650 | "startBinding": null, 651 | "endBinding": { 652 | "elementId": "RIWIt4T9i9HskgjgylTHz", 653 | "focus": -0.7590716758558789, 654 | "gap": 11 655 | }, 656 | "points": [ 657 | [ 658 | 0, 659 | 0 660 | ], 661 | [ 662 | 80, 663 | 91 664 | ] 665 | ], 666 | "lastCommittedPoint": null 667 | }, 668 | { 669 | "type": "arrow", 670 | "version": 27, 671 | "versionNonce": 1947914794, 672 | "isDeleted": false, 673 | "id": "7coYTlTfIAlAGQr16ji7q", 674 | "fillStyle": "hachure", 675 | "strokeWidth": 1, 676 | "strokeStyle": "solid", 677 | "roughness": 1, 678 | "opacity": 100, 679 | "angle": 0, 680 | "x": 687, 681 | "y": 441, 682 | "strokeColor": "#000000", 683 | "backgroundColor": "transparent", 684 | "width": 169, 685 | "height": 42, 686 | "seed": 371890102, 687 | "groupIds": [], 688 | "strokeSharpness": "round", 689 | "boundElementIds": [], 690 | "startBinding": null, 691 | "endBinding": { 692 | "elementId": "RIWIt4T9i9HskgjgylTHz", 693 | "focus": 0.010803732198395808, 694 | "gap": 5 695 | }, 696 | "points": [ 697 | [ 698 | 0, 699 | 0 700 | ], 701 | [ 702 | -169, 703 | 42 704 | ] 705 | ], 706 | "lastCommittedPoint": null 707 | }, 708 | { 709 | "type": "text", 710 | "version": 23, 711 | "versionNonce": 1034773354, 712 | "isDeleted": false, 713 | "id": "NABe5UK6tEqez002meYI_", 714 | "fillStyle": "hachure", 715 | "strokeWidth": 1, 716 | "strokeStyle": "solid", 717 | "roughness": 1, 718 | "opacity": 100, 719 | "angle": 0, 720 | "x": 390, 721 | "y": 488, 722 | "strokeColor": "#000000", 723 | "backgroundColor": "transparent", 724 | "width": 106, 725 | "height": 26, 726 | "seed": 1775923818, 727 | "groupIds": [], 728 | "strokeSharpness": "sharp", 729 | "boundElementIds": [], 730 | "fontSize": 20, 731 | "fontFamily": 1, 732 | "text": "diagnostics", 733 | "baseline": 18, 734 | "textAlign": "left", 735 | "verticalAlign": "top" 736 | }, 737 | { 738 | "type": "rectangle", 739 | "version": 32, 740 | "versionNonce": 223493494, 741 | "isDeleted": false, 742 | "id": "RIWIt4T9i9HskgjgylTHz", 743 | "fillStyle": "hachure", 744 | "strokeWidth": 1, 745 | "strokeStyle": "solid", 746 | "roughness": 1, 747 | "opacity": 100, 748 | "angle": 0, 749 | "x": 375, 750 | "y": 482, 751 | "strokeColor": "#000000", 752 | "backgroundColor": "transparent", 753 | "width": 138, 754 | "height": 38, 755 | "seed": 163669546, 756 | "groupIds": [], 757 | "strokeSharpness": "sharp", 758 | "boundElementIds": [ 759 | "JUWfh7bofKgjCi5eKlIZq", 760 | "7coYTlTfIAlAGQr16ji7q" 761 | ] 762 | } 763 | ], 764 | "appState": { 765 | "viewBackgroundColor": "#ffffff", 766 | "gridSize": null 767 | } 768 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "Inflector" 5 | version = "0.11.4" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" 8 | dependencies = [ 9 | "lazy_static", 10 | "regex", 11 | ] 12 | 13 | [[package]] 14 | name = "aho-corasick" 15 | version = "0.7.13" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" 18 | dependencies = [ 19 | "memchr", 20 | ] 21 | 22 | [[package]] 23 | name = "analyzer_tree" 24 | version = "0.0.1" 25 | dependencies = [ 26 | "serde", 27 | "serde_derive", 28 | "serde_json", 29 | "swc_atoms", 30 | "swc_common", 31 | "swc_ecma_ast", 32 | "swc_ecma_parser", 33 | "swc_ecma_visit", 34 | ] 35 | 36 | [[package]] 37 | name = "ast_node" 38 | version = "0.7.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "fd6ee2941db3551563d29eaf5214cd3d7b2f322e0c0e3954f5ae020f860bae8c" 41 | dependencies = [ 42 | "darling", 43 | "pmutil", 44 | "proc-macro2", 45 | "quote", 46 | "swc_macros_common", 47 | "syn", 48 | ] 49 | 50 | [[package]] 51 | name = "autocfg" 52 | version = "1.0.1" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 55 | 56 | [[package]] 57 | name = "bumpalo" 58 | version = "3.4.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 61 | 62 | [[package]] 63 | name = "byteorder" 64 | version = "1.3.4" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 67 | 68 | [[package]] 69 | name = "cfg-if" 70 | version = "0.1.10" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 73 | 74 | [[package]] 75 | name = "console_error_panic_hook" 76 | version = "0.1.6" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" 79 | dependencies = [ 80 | "cfg-if", 81 | "wasm-bindgen", 82 | ] 83 | 84 | [[package]] 85 | name = "darling" 86 | version = "0.10.2" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" 89 | dependencies = [ 90 | "darling_core", 91 | "darling_macro", 92 | ] 93 | 94 | [[package]] 95 | name = "darling_core" 96 | version = "0.10.2" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" 99 | dependencies = [ 100 | "fnv", 101 | "ident_case", 102 | "proc-macro2", 103 | "quote", 104 | "strsim", 105 | "syn", 106 | ] 107 | 108 | [[package]] 109 | name = "darling_macro" 110 | version = "0.10.2" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" 113 | dependencies = [ 114 | "darling_core", 115 | "quote", 116 | "syn", 117 | ] 118 | 119 | [[package]] 120 | name = "either" 121 | version = "1.6.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" 124 | 125 | [[package]] 126 | name = "enum_kind" 127 | version = "0.2.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "6e57153e35187d51f08471d5840459ff29093473e7bedd004a1414985aab92f3" 130 | dependencies = [ 131 | "pmutil", 132 | "proc-macro2", 133 | "swc_macros_common", 134 | "syn", 135 | ] 136 | 137 | [[package]] 138 | name = "fnv" 139 | version = "1.0.7" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 142 | 143 | [[package]] 144 | name = "from_variant" 145 | version = "0.1.2" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "039885ad6579a86b94ad8df696cce8c530da496bf7b07b12fec8d6c4cd654bb9" 148 | dependencies = [ 149 | "pmutil", 150 | "proc-macro2", 151 | "swc_macros_common", 152 | "syn", 153 | ] 154 | 155 | [[package]] 156 | name = "fxhash" 157 | version = "0.2.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 160 | dependencies = [ 161 | "byteorder", 162 | ] 163 | 164 | [[package]] 165 | name = "getrandom" 166 | version = "0.1.15" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" 169 | dependencies = [ 170 | "cfg-if", 171 | "libc", 172 | "wasi", 173 | ] 174 | 175 | [[package]] 176 | name = "ident_case" 177 | version = "1.0.1" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 180 | 181 | [[package]] 182 | name = "is-macro" 183 | version = "0.1.8" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "04807f3dc9e3ea39af3f8469a5297267faf94859637afb836b33f47d9b2650ee" 186 | dependencies = [ 187 | "Inflector", 188 | "pmutil", 189 | "proc-macro2", 190 | "quote", 191 | "syn", 192 | ] 193 | 194 | [[package]] 195 | name = "itoa" 196 | version = "0.4.6" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 199 | 200 | [[package]] 201 | name = "js-sys" 202 | version = "0.3.45" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8" 205 | dependencies = [ 206 | "wasm-bindgen", 207 | ] 208 | 209 | [[package]] 210 | name = "lazy_static" 211 | version = "1.4.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 214 | 215 | [[package]] 216 | name = "libc" 217 | version = "0.2.77" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" 220 | 221 | [[package]] 222 | name = "log" 223 | version = "0.4.11" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 226 | dependencies = [ 227 | "cfg-if", 228 | ] 229 | 230 | [[package]] 231 | name = "memchr" 232 | version = "2.3.3" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 235 | 236 | [[package]] 237 | name = "memory_units" 238 | version = "0.4.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" 241 | 242 | [[package]] 243 | name = "nest_analyzer_wasm" 244 | version = "0.1.0" 245 | dependencies = [ 246 | "analyzer_tree", 247 | "console_error_panic_hook", 248 | "js-sys", 249 | "wasm-bindgen", 250 | "wee_alloc", 251 | ] 252 | 253 | [[package]] 254 | name = "new_debug_unreachable" 255 | version = "1.0.4" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" 258 | 259 | [[package]] 260 | name = "num-bigint" 261 | version = "0.2.6" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" 264 | dependencies = [ 265 | "autocfg", 266 | "num-integer", 267 | "num-traits", 268 | "serde", 269 | ] 270 | 271 | [[package]] 272 | name = "num-integer" 273 | version = "0.1.43" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 276 | dependencies = [ 277 | "autocfg", 278 | "num-traits", 279 | ] 280 | 281 | [[package]] 282 | name = "num-traits" 283 | version = "0.2.12" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 286 | dependencies = [ 287 | "autocfg", 288 | ] 289 | 290 | [[package]] 291 | name = "once_cell" 292 | version = "1.4.1" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" 295 | 296 | [[package]] 297 | name = "owning_ref" 298 | version = "0.4.1" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" 301 | dependencies = [ 302 | "stable_deref_trait", 303 | ] 304 | 305 | [[package]] 306 | name = "phf_generator" 307 | version = "0.8.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" 310 | dependencies = [ 311 | "phf_shared", 312 | "rand", 313 | ] 314 | 315 | [[package]] 316 | name = "phf_shared" 317 | version = "0.8.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" 320 | dependencies = [ 321 | "siphasher", 322 | ] 323 | 324 | [[package]] 325 | name = "pmutil" 326 | version = "0.5.3" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" 329 | dependencies = [ 330 | "proc-macro2", 331 | "quote", 332 | "syn", 333 | ] 334 | 335 | [[package]] 336 | name = "ppv-lite86" 337 | version = "0.2.9" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" 340 | 341 | [[package]] 342 | name = "precomputed-hash" 343 | version = "0.1.1" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 346 | 347 | [[package]] 348 | name = "proc-macro2" 349 | version = "1.0.21" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" 352 | dependencies = [ 353 | "unicode-xid", 354 | ] 355 | 356 | [[package]] 357 | name = "quote" 358 | version = "1.0.7" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 361 | dependencies = [ 362 | "proc-macro2", 363 | ] 364 | 365 | [[package]] 366 | name = "rand" 367 | version = "0.7.3" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 370 | dependencies = [ 371 | "getrandom", 372 | "libc", 373 | "rand_chacha", 374 | "rand_core", 375 | "rand_hc", 376 | "rand_pcg", 377 | ] 378 | 379 | [[package]] 380 | name = "rand_chacha" 381 | version = "0.2.2" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 384 | dependencies = [ 385 | "ppv-lite86", 386 | "rand_core", 387 | ] 388 | 389 | [[package]] 390 | name = "rand_core" 391 | version = "0.5.1" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 394 | dependencies = [ 395 | "getrandom", 396 | ] 397 | 398 | [[package]] 399 | name = "rand_hc" 400 | version = "0.2.0" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 403 | dependencies = [ 404 | "rand_core", 405 | ] 406 | 407 | [[package]] 408 | name = "rand_pcg" 409 | version = "0.2.1" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" 412 | dependencies = [ 413 | "rand_core", 414 | ] 415 | 416 | [[package]] 417 | name = "regex" 418 | version = "1.3.9" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" 421 | dependencies = [ 422 | "aho-corasick", 423 | "memchr", 424 | "regex-syntax", 425 | "thread_local", 426 | ] 427 | 428 | [[package]] 429 | name = "regex-syntax" 430 | version = "0.6.18" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" 433 | 434 | [[package]] 435 | name = "ryu" 436 | version = "1.0.5" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 439 | 440 | [[package]] 441 | name = "scoped-tls" 442 | version = "1.0.0" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 445 | 446 | [[package]] 447 | name = "serde" 448 | version = "1.0.116" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" 451 | dependencies = [ 452 | "serde_derive", 453 | ] 454 | 455 | [[package]] 456 | name = "serde_derive" 457 | version = "1.0.116" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" 460 | dependencies = [ 461 | "proc-macro2", 462 | "quote", 463 | "syn", 464 | ] 465 | 466 | [[package]] 467 | name = "serde_json" 468 | version = "1.0.57" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" 471 | dependencies = [ 472 | "itoa", 473 | "ryu", 474 | "serde", 475 | ] 476 | 477 | [[package]] 478 | name = "siphasher" 479 | version = "0.3.3" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" 482 | 483 | [[package]] 484 | name = "smallvec" 485 | version = "1.4.2" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" 488 | 489 | [[package]] 490 | name = "stable_deref_trait" 491 | version = "1.2.0" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 494 | 495 | [[package]] 496 | name = "string_cache" 497 | version = "0.8.0" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a" 500 | dependencies = [ 501 | "lazy_static", 502 | "new_debug_unreachable", 503 | "phf_shared", 504 | "precomputed-hash", 505 | "serde", 506 | ] 507 | 508 | [[package]] 509 | name = "string_cache_codegen" 510 | version = "0.5.1" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97" 513 | dependencies = [ 514 | "phf_generator", 515 | "phf_shared", 516 | "proc-macro2", 517 | "quote", 518 | ] 519 | 520 | [[package]] 521 | name = "string_enum" 522 | version = "0.3.0" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "94fdb6536756cfd35ee18b9a9972ab2a699d405cc57e0ad0532022960f30d581" 525 | dependencies = [ 526 | "pmutil", 527 | "proc-macro2", 528 | "quote", 529 | "swc_macros_common", 530 | "syn", 531 | ] 532 | 533 | [[package]] 534 | name = "strsim" 535 | version = "0.9.3" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 538 | 539 | [[package]] 540 | name = "swc_atoms" 541 | version = "0.2.2" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "46682d5a27e12d8b86168ea2fcb3aae2e0625f24bf109dee4bca24b2b51e03ce" 544 | dependencies = [ 545 | "string_cache", 546 | "string_cache_codegen", 547 | ] 548 | 549 | [[package]] 550 | name = "swc_common" 551 | version = "0.9.1" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "458740fb57fe3f2b748819c1db0f448d920d4a64b00e802a485bd41290ef6790" 554 | dependencies = [ 555 | "ast_node", 556 | "cfg-if", 557 | "either", 558 | "from_variant", 559 | "fxhash", 560 | "log", 561 | "once_cell", 562 | "owning_ref", 563 | "scoped-tls", 564 | "serde", 565 | "swc_visit", 566 | "unicode-width", 567 | ] 568 | 569 | [[package]] 570 | name = "swc_ecma_ast" 571 | version = "0.28.0" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "7dab5379d61147d5d804a92b073a1d38c5a7bd86af09ff19b9f057a1992c1a6e" 574 | dependencies = [ 575 | "enum_kind", 576 | "is-macro", 577 | "num-bigint", 578 | "serde", 579 | "string_enum", 580 | "swc_atoms", 581 | "swc_common", 582 | ] 583 | 584 | [[package]] 585 | name = "swc_ecma_parser" 586 | version = "0.33.3" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "b9db9dad7e75ed1f35a92483de6468247e99e271463d93bf07b51d0c36c8652f" 589 | dependencies = [ 590 | "either", 591 | "enum_kind", 592 | "fxhash", 593 | "log", 594 | "num-bigint", 595 | "serde", 596 | "smallvec", 597 | "swc_atoms", 598 | "swc_common", 599 | "swc_ecma_ast", 600 | "swc_ecma_parser_macros", 601 | "swc_ecma_visit", 602 | "unicode-xid", 603 | ] 604 | 605 | [[package]] 606 | name = "swc_ecma_parser_macros" 607 | version = "0.4.1" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "8798810e2c79b884cf238bcb72b4bd12375121ee91724f1ceeb54b6e38a138e7" 610 | dependencies = [ 611 | "pmutil", 612 | "proc-macro2", 613 | "quote", 614 | "swc_macros_common", 615 | "syn", 616 | ] 617 | 618 | [[package]] 619 | name = "swc_ecma_visit" 620 | version = "0.13.0" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "cdf59a90e7138c6af50a30d81a2cba6147c541bdc181f127ce8bd253721f8517" 623 | dependencies = [ 624 | "num-bigint", 625 | "swc_atoms", 626 | "swc_common", 627 | "swc_ecma_ast", 628 | "swc_visit", 629 | ] 630 | 631 | [[package]] 632 | name = "swc_macros_common" 633 | version = "0.3.1" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "18a9f27d290938370597d363df9a77ba4be8e2bc99f32f69eb5245cdeed3c512" 636 | dependencies = [ 637 | "pmutil", 638 | "proc-macro2", 639 | "quote", 640 | "syn", 641 | ] 642 | 643 | [[package]] 644 | name = "swc_visit" 645 | version = "0.1.1" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "b273a8bf44a47b343e116b9c0ce826b0955dc137817671991c6a063acbc55db1" 648 | dependencies = [ 649 | "either", 650 | "swc_visit_macros", 651 | ] 652 | 653 | [[package]] 654 | name = "swc_visit_macros" 655 | version = "0.1.1" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "8afa402127a3f6af772e3ec4215ff1010fd0cd248f723414da47e250740b23e8" 658 | dependencies = [ 659 | "Inflector", 660 | "pmutil", 661 | "proc-macro2", 662 | "quote", 663 | "swc_macros_common", 664 | "syn", 665 | ] 666 | 667 | [[package]] 668 | name = "syn" 669 | version = "1.0.41" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" 672 | dependencies = [ 673 | "proc-macro2", 674 | "quote", 675 | "unicode-xid", 676 | ] 677 | 678 | [[package]] 679 | name = "thread_local" 680 | version = "1.0.1" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 683 | dependencies = [ 684 | "lazy_static", 685 | ] 686 | 687 | [[package]] 688 | name = "unicode-width" 689 | version = "0.1.8" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 692 | 693 | [[package]] 694 | name = "unicode-xid" 695 | version = "0.2.1" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 698 | 699 | [[package]] 700 | name = "wasi" 701 | version = "0.9.0+wasi-snapshot-preview1" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 704 | 705 | [[package]] 706 | name = "wasm-bindgen" 707 | version = "0.2.68" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" 710 | dependencies = [ 711 | "cfg-if", 712 | "serde", 713 | "serde_json", 714 | "wasm-bindgen-macro", 715 | ] 716 | 717 | [[package]] 718 | name = "wasm-bindgen-backend" 719 | version = "0.2.68" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" 722 | dependencies = [ 723 | "bumpalo", 724 | "lazy_static", 725 | "log", 726 | "proc-macro2", 727 | "quote", 728 | "syn", 729 | "wasm-bindgen-shared", 730 | ] 731 | 732 | [[package]] 733 | name = "wasm-bindgen-macro" 734 | version = "0.2.68" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" 737 | dependencies = [ 738 | "quote", 739 | "wasm-bindgen-macro-support", 740 | ] 741 | 742 | [[package]] 743 | name = "wasm-bindgen-macro-support" 744 | version = "0.2.68" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" 747 | dependencies = [ 748 | "proc-macro2", 749 | "quote", 750 | "syn", 751 | "wasm-bindgen-backend", 752 | "wasm-bindgen-shared", 753 | ] 754 | 755 | [[package]] 756 | name = "wasm-bindgen-shared" 757 | version = "0.2.68" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" 760 | 761 | [[package]] 762 | name = "wee_alloc" 763 | version = "0.4.5" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" 766 | dependencies = [ 767 | "cfg-if", 768 | "libc", 769 | "memory_units", 770 | "winapi", 771 | ] 772 | 773 | [[package]] 774 | name = "winapi" 775 | version = "0.3.9" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 778 | dependencies = [ 779 | "winapi-i686-pc-windows-gnu", 780 | "winapi-x86_64-pc-windows-gnu", 781 | ] 782 | 783 | [[package]] 784 | name = "winapi-i686-pc-windows-gnu" 785 | version = "0.4.0" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 788 | 789 | [[package]] 790 | name = "winapi-x86_64-pc-windows-gnu" 791 | version = "0.4.0" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 794 | -------------------------------------------------------------------------------- /analyzer_wasm/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "Inflector" 5 | version = "0.11.4" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" 8 | dependencies = [ 9 | "lazy_static", 10 | "regex", 11 | ] 12 | 13 | [[package]] 14 | name = "aho-corasick" 15 | version = "0.7.13" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" 18 | dependencies = [ 19 | "memchr", 20 | ] 21 | 22 | [[package]] 23 | name = "analyzer_tree" 24 | version = "0.0.1" 25 | dependencies = [ 26 | "serde", 27 | "serde_derive", 28 | "serde_json", 29 | "swc_atoms", 30 | "swc_common", 31 | "swc_ecma_ast", 32 | "swc_ecma_parser", 33 | "swc_ecma_visit", 34 | ] 35 | 36 | [[package]] 37 | name = "ast_node" 38 | version = "0.7.0" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "fd6ee2941db3551563d29eaf5214cd3d7b2f322e0c0e3954f5ae020f860bae8c" 41 | dependencies = [ 42 | "darling", 43 | "pmutil", 44 | "proc-macro2", 45 | "quote", 46 | "swc_macros_common", 47 | "syn", 48 | ] 49 | 50 | [[package]] 51 | name = "autocfg" 52 | version = "1.0.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 55 | 56 | [[package]] 57 | name = "bumpalo" 58 | version = "3.4.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 61 | 62 | [[package]] 63 | name = "byteorder" 64 | version = "1.3.4" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 67 | 68 | [[package]] 69 | name = "cfg-if" 70 | version = "0.1.10" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 73 | 74 | [[package]] 75 | name = "console_error_panic_hook" 76 | version = "0.1.6" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211" 79 | dependencies = [ 80 | "cfg-if", 81 | "wasm-bindgen", 82 | ] 83 | 84 | [[package]] 85 | name = "darling" 86 | version = "0.10.2" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" 89 | dependencies = [ 90 | "darling_core", 91 | "darling_macro", 92 | ] 93 | 94 | [[package]] 95 | name = "darling_core" 96 | version = "0.10.2" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" 99 | dependencies = [ 100 | "fnv", 101 | "ident_case", 102 | "proc-macro2", 103 | "quote", 104 | "strsim", 105 | "syn", 106 | ] 107 | 108 | [[package]] 109 | name = "darling_macro" 110 | version = "0.10.2" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" 113 | dependencies = [ 114 | "darling_core", 115 | "quote", 116 | "syn", 117 | ] 118 | 119 | [[package]] 120 | name = "either" 121 | version = "1.6.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" 124 | 125 | [[package]] 126 | name = "enum_kind" 127 | version = "0.2.0" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "6e57153e35187d51f08471d5840459ff29093473e7bedd004a1414985aab92f3" 130 | dependencies = [ 131 | "pmutil", 132 | "proc-macro2", 133 | "swc_macros_common", 134 | "syn", 135 | ] 136 | 137 | [[package]] 138 | name = "fnv" 139 | version = "1.0.7" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 142 | 143 | [[package]] 144 | name = "from_variant" 145 | version = "0.1.2" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "039885ad6579a86b94ad8df696cce8c530da496bf7b07b12fec8d6c4cd654bb9" 148 | dependencies = [ 149 | "pmutil", 150 | "proc-macro2", 151 | "swc_macros_common", 152 | "syn", 153 | ] 154 | 155 | [[package]] 156 | name = "fxhash" 157 | version = "0.2.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" 160 | dependencies = [ 161 | "byteorder", 162 | ] 163 | 164 | [[package]] 165 | name = "getrandom" 166 | version = "0.1.14" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 169 | dependencies = [ 170 | "cfg-if", 171 | "libc", 172 | "wasi", 173 | ] 174 | 175 | [[package]] 176 | name = "ident_case" 177 | version = "1.0.1" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 180 | 181 | [[package]] 182 | name = "is-macro" 183 | version = "0.1.8" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "04807f3dc9e3ea39af3f8469a5297267faf94859637afb836b33f47d9b2650ee" 186 | dependencies = [ 187 | "Inflector", 188 | "pmutil", 189 | "proc-macro2", 190 | "quote", 191 | "syn", 192 | ] 193 | 194 | [[package]] 195 | name = "itoa" 196 | version = "0.4.6" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 199 | 200 | [[package]] 201 | name = "js-sys" 202 | version = "0.3.44" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "85a7e2c92a4804dd459b86c339278d0fe87cf93757fae222c3fa3ae75458bc73" 205 | dependencies = [ 206 | "wasm-bindgen", 207 | ] 208 | 209 | [[package]] 210 | name = "lazy_static" 211 | version = "1.4.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 214 | 215 | [[package]] 216 | name = "libc" 217 | version = "0.2.74" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" 220 | 221 | [[package]] 222 | name = "log" 223 | version = "0.4.11" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 226 | dependencies = [ 227 | "cfg-if", 228 | ] 229 | 230 | [[package]] 231 | name = "memchr" 232 | version = "2.3.3" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 235 | 236 | [[package]] 237 | name = "memory_units" 238 | version = "0.4.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" 241 | 242 | [[package]] 243 | name = "nest_analyzer_wasm" 244 | version = "0.1.0" 245 | dependencies = [ 246 | "analyzer_tree", 247 | "console_error_panic_hook", 248 | "js-sys", 249 | "wasm-bindgen", 250 | "wee_alloc", 251 | ] 252 | 253 | [[package]] 254 | name = "new_debug_unreachable" 255 | version = "1.0.4" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" 258 | 259 | [[package]] 260 | name = "num-bigint" 261 | version = "0.2.6" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" 264 | dependencies = [ 265 | "autocfg", 266 | "num-integer", 267 | "num-traits", 268 | "serde", 269 | ] 270 | 271 | [[package]] 272 | name = "num-integer" 273 | version = "0.1.43" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 276 | dependencies = [ 277 | "autocfg", 278 | "num-traits", 279 | ] 280 | 281 | [[package]] 282 | name = "num-traits" 283 | version = "0.2.12" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 286 | dependencies = [ 287 | "autocfg", 288 | ] 289 | 290 | [[package]] 291 | name = "once_cell" 292 | version = "1.4.0" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" 295 | 296 | [[package]] 297 | name = "owning_ref" 298 | version = "0.4.1" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" 301 | dependencies = [ 302 | "stable_deref_trait", 303 | ] 304 | 305 | [[package]] 306 | name = "phf_generator" 307 | version = "0.8.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" 310 | dependencies = [ 311 | "phf_shared", 312 | "rand", 313 | ] 314 | 315 | [[package]] 316 | name = "phf_shared" 317 | version = "0.8.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" 320 | dependencies = [ 321 | "siphasher", 322 | ] 323 | 324 | [[package]] 325 | name = "pmutil" 326 | version = "0.5.3" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" 329 | dependencies = [ 330 | "proc-macro2", 331 | "quote", 332 | "syn", 333 | ] 334 | 335 | [[package]] 336 | name = "ppv-lite86" 337 | version = "0.2.8" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" 340 | 341 | [[package]] 342 | name = "precomputed-hash" 343 | version = "0.1.1" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 346 | 347 | [[package]] 348 | name = "proc-macro2" 349 | version = "1.0.19" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" 352 | dependencies = [ 353 | "unicode-xid", 354 | ] 355 | 356 | [[package]] 357 | name = "quote" 358 | version = "1.0.7" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 361 | dependencies = [ 362 | "proc-macro2", 363 | ] 364 | 365 | [[package]] 366 | name = "rand" 367 | version = "0.7.3" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 370 | dependencies = [ 371 | "getrandom", 372 | "libc", 373 | "rand_chacha", 374 | "rand_core", 375 | "rand_hc", 376 | "rand_pcg", 377 | ] 378 | 379 | [[package]] 380 | name = "rand_chacha" 381 | version = "0.2.2" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 384 | dependencies = [ 385 | "ppv-lite86", 386 | "rand_core", 387 | ] 388 | 389 | [[package]] 390 | name = "rand_core" 391 | version = "0.5.1" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 394 | dependencies = [ 395 | "getrandom", 396 | ] 397 | 398 | [[package]] 399 | name = "rand_hc" 400 | version = "0.2.0" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 403 | dependencies = [ 404 | "rand_core", 405 | ] 406 | 407 | [[package]] 408 | name = "rand_pcg" 409 | version = "0.2.1" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" 412 | dependencies = [ 413 | "rand_core", 414 | ] 415 | 416 | [[package]] 417 | name = "regex" 418 | version = "1.3.9" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" 421 | dependencies = [ 422 | "aho-corasick", 423 | "memchr", 424 | "regex-syntax", 425 | "thread_local", 426 | ] 427 | 428 | [[package]] 429 | name = "regex-syntax" 430 | version = "0.6.18" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" 433 | 434 | [[package]] 435 | name = "ryu" 436 | version = "1.0.5" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 439 | 440 | [[package]] 441 | name = "scoped-tls" 442 | version = "1.0.0" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 445 | 446 | [[package]] 447 | name = "serde" 448 | version = "1.0.115" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" 451 | dependencies = [ 452 | "serde_derive", 453 | ] 454 | 455 | [[package]] 456 | name = "serde_derive" 457 | version = "1.0.115" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" 460 | dependencies = [ 461 | "proc-macro2", 462 | "quote", 463 | "syn", 464 | ] 465 | 466 | [[package]] 467 | name = "serde_json" 468 | version = "1.0.57" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" 471 | dependencies = [ 472 | "itoa", 473 | "ryu", 474 | "serde", 475 | ] 476 | 477 | [[package]] 478 | name = "siphasher" 479 | version = "0.3.3" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" 482 | 483 | [[package]] 484 | name = "smallvec" 485 | version = "1.4.2" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" 488 | 489 | [[package]] 490 | name = "stable_deref_trait" 491 | version = "1.2.0" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 494 | 495 | [[package]] 496 | name = "string_cache" 497 | version = "0.8.0" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a" 500 | dependencies = [ 501 | "lazy_static", 502 | "new_debug_unreachable", 503 | "phf_shared", 504 | "precomputed-hash", 505 | "serde", 506 | ] 507 | 508 | [[package]] 509 | name = "string_cache_codegen" 510 | version = "0.5.1" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97" 513 | dependencies = [ 514 | "phf_generator", 515 | "phf_shared", 516 | "proc-macro2", 517 | "quote", 518 | ] 519 | 520 | [[package]] 521 | name = "string_enum" 522 | version = "0.3.0" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "94fdb6536756cfd35ee18b9a9972ab2a699d405cc57e0ad0532022960f30d581" 525 | dependencies = [ 526 | "pmutil", 527 | "proc-macro2", 528 | "quote", 529 | "swc_macros_common", 530 | "syn", 531 | ] 532 | 533 | [[package]] 534 | name = "strsim" 535 | version = "0.9.3" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" 538 | 539 | [[package]] 540 | name = "swc_atoms" 541 | version = "0.2.2" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "46682d5a27e12d8b86168ea2fcb3aae2e0625f24bf109dee4bca24b2b51e03ce" 544 | dependencies = [ 545 | "string_cache", 546 | "string_cache_codegen", 547 | ] 548 | 549 | [[package]] 550 | name = "swc_common" 551 | version = "0.9.1" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "458740fb57fe3f2b748819c1db0f448d920d4a64b00e802a485bd41290ef6790" 554 | dependencies = [ 555 | "ast_node", 556 | "cfg-if", 557 | "either", 558 | "from_variant", 559 | "fxhash", 560 | "log", 561 | "once_cell", 562 | "owning_ref", 563 | "scoped-tls", 564 | "serde", 565 | "swc_visit", 566 | "unicode-width", 567 | ] 568 | 569 | [[package]] 570 | name = "swc_ecma_ast" 571 | version = "0.28.0" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "7dab5379d61147d5d804a92b073a1d38c5a7bd86af09ff19b9f057a1992c1a6e" 574 | dependencies = [ 575 | "enum_kind", 576 | "is-macro", 577 | "num-bigint", 578 | "serde", 579 | "string_enum", 580 | "swc_atoms", 581 | "swc_common", 582 | ] 583 | 584 | [[package]] 585 | name = "swc_ecma_parser" 586 | version = "0.33.3" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "b9db9dad7e75ed1f35a92483de6468247e99e271463d93bf07b51d0c36c8652f" 589 | dependencies = [ 590 | "either", 591 | "enum_kind", 592 | "fxhash", 593 | "log", 594 | "num-bigint", 595 | "serde", 596 | "smallvec", 597 | "swc_atoms", 598 | "swc_common", 599 | "swc_ecma_ast", 600 | "swc_ecma_parser_macros", 601 | "swc_ecma_visit", 602 | "unicode-xid", 603 | ] 604 | 605 | [[package]] 606 | name = "swc_ecma_parser_macros" 607 | version = "0.4.1" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "8798810e2c79b884cf238bcb72b4bd12375121ee91724f1ceeb54b6e38a138e7" 610 | dependencies = [ 611 | "pmutil", 612 | "proc-macro2", 613 | "quote", 614 | "swc_macros_common", 615 | "syn", 616 | ] 617 | 618 | [[package]] 619 | name = "swc_ecma_visit" 620 | version = "0.13.0" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "cdf59a90e7138c6af50a30d81a2cba6147c541bdc181f127ce8bd253721f8517" 623 | dependencies = [ 624 | "num-bigint", 625 | "swc_atoms", 626 | "swc_common", 627 | "swc_ecma_ast", 628 | "swc_visit", 629 | ] 630 | 631 | [[package]] 632 | name = "swc_macros_common" 633 | version = "0.3.1" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "18a9f27d290938370597d363df9a77ba4be8e2bc99f32f69eb5245cdeed3c512" 636 | dependencies = [ 637 | "pmutil", 638 | "proc-macro2", 639 | "quote", 640 | "syn", 641 | ] 642 | 643 | [[package]] 644 | name = "swc_visit" 645 | version = "0.1.0" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "6683cd5f6b97498e887e0d4da4c44ddafb755dfb0d950c52af99344fad23fec9" 648 | dependencies = [ 649 | "either", 650 | "swc_visit_macros", 651 | ] 652 | 653 | [[package]] 654 | name = "swc_visit_macros" 655 | version = "0.1.0" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "1802720b0826a2ae6fac5be1305b1277cf1e83418549f465edf075d8016a1548" 658 | dependencies = [ 659 | "Inflector", 660 | "pmutil", 661 | "proc-macro2", 662 | "quote", 663 | "swc_macros_common", 664 | "syn", 665 | ] 666 | 667 | [[package]] 668 | name = "syn" 669 | version = "1.0.38" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" 672 | dependencies = [ 673 | "proc-macro2", 674 | "quote", 675 | "unicode-xid", 676 | ] 677 | 678 | [[package]] 679 | name = "thread_local" 680 | version = "1.0.1" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 683 | dependencies = [ 684 | "lazy_static", 685 | ] 686 | 687 | [[package]] 688 | name = "unicode-width" 689 | version = "0.1.8" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 692 | 693 | [[package]] 694 | name = "unicode-xid" 695 | version = "0.2.1" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 698 | 699 | [[package]] 700 | name = "wasi" 701 | version = "0.9.0+wasi-snapshot-preview1" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 704 | 705 | [[package]] 706 | name = "wasm-bindgen" 707 | version = "0.2.67" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "f0563a9a4b071746dd5aedbc3a28c6fe9be4586fb3fbadb67c400d4f53c6b16c" 710 | dependencies = [ 711 | "cfg-if", 712 | "serde", 713 | "serde_json", 714 | "wasm-bindgen-macro", 715 | ] 716 | 717 | [[package]] 718 | name = "wasm-bindgen-backend" 719 | version = "0.2.67" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "bc71e4c5efa60fb9e74160e89b93353bc24059999c0ae0fb03affc39770310b0" 722 | dependencies = [ 723 | "bumpalo", 724 | "lazy_static", 725 | "log", 726 | "proc-macro2", 727 | "quote", 728 | "syn", 729 | "wasm-bindgen-shared", 730 | ] 731 | 732 | [[package]] 733 | name = "wasm-bindgen-macro" 734 | version = "0.2.67" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "97c57cefa5fa80e2ba15641578b44d36e7a64279bc5ed43c6dbaf329457a2ed2" 737 | dependencies = [ 738 | "quote", 739 | "wasm-bindgen-macro-support", 740 | ] 741 | 742 | [[package]] 743 | name = "wasm-bindgen-macro-support" 744 | version = "0.2.67" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "841a6d1c35c6f596ccea1f82504a192a60378f64b3bb0261904ad8f2f5657556" 747 | dependencies = [ 748 | "proc-macro2", 749 | "quote", 750 | "syn", 751 | "wasm-bindgen-backend", 752 | "wasm-bindgen-shared", 753 | ] 754 | 755 | [[package]] 756 | name = "wasm-bindgen-shared" 757 | version = "0.2.67" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "93b162580e34310e5931c4b792560108b10fd14d64915d7fff8ff00180e70092" 760 | 761 | [[package]] 762 | name = "wee_alloc" 763 | version = "0.4.5" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" 766 | dependencies = [ 767 | "cfg-if", 768 | "libc", 769 | "memory_units", 770 | "winapi", 771 | ] 772 | 773 | [[package]] 774 | name = "winapi" 775 | version = "0.3.9" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 778 | dependencies = [ 779 | "winapi-i686-pc-windows-gnu", 780 | "winapi-x86_64-pc-windows-gnu", 781 | ] 782 | 783 | [[package]] 784 | name = "winapi-i686-pc-windows-gnu" 785 | version = "0.4.0" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 788 | 789 | [[package]] 790 | name = "winapi-x86_64-pc-windows-gnu" 791 | version = "0.4.0" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 794 | -------------------------------------------------------------------------------- /analyzer_tree/scopes.rs: -------------------------------------------------------------------------------- 1 | // This is a fork of the `deno_lint` scope analyzer. 2 | // Copyright 2020 the Deno authors. All rights reserved. MIT license. 3 | // Copyright 2020 nest.land core team. 4 | 5 | use std::cell::RefCell; 6 | use std::cmp::Eq; 7 | use std::cmp::PartialEq; 8 | use std::collections::HashMap; 9 | use std::fmt::Display; 10 | use std::hash::Hash; 11 | use std::hash::Hasher; 12 | use std::ops::Deref; 13 | use std::rc::Rc; 14 | use swc_common::Span; 15 | use swc_common::DUMMY_SP; 16 | use swc_ecma_ast::ObjectPatProp; 17 | use swc_ecma_ast::Pat; 18 | use swc_ecma_visit::Node; 19 | use swc_ecma_visit::Visit; 20 | 21 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 22 | pub enum BindingKind { 23 | Var, 24 | Const, 25 | Let, 26 | Function, 27 | Param, 28 | Class, 29 | CatchClause, 30 | Import, 31 | } 32 | 33 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 34 | pub enum ScopeKind { 35 | Program, 36 | Module, 37 | Function, 38 | Block, 39 | Loop, 40 | Class, 41 | Switch, 42 | With, 43 | Catch, 44 | } 45 | 46 | #[derive(Clone, Debug)] 47 | pub struct ScopeData { 48 | pub kind: ScopeKind, 49 | pub parent_scope: Option, 50 | pub span: Span, 51 | pub child_scopes: Vec, 52 | pub bindings: HashMap, 53 | } 54 | 55 | #[derive(Clone, Debug, Eq, PartialEq, Hash)] 56 | pub struct Scope { 57 | id: usize, 58 | index: ScopeIndex, 59 | } 60 | 61 | impl Scope { 62 | pub fn new(kind: ScopeKind, span: Span, parent_scope: Option) -> Self { 63 | let index = parent_scope 64 | .as_ref() 65 | .map(|p| p.index.clone()) 66 | .unwrap_or_else(ScopeIndex::default); 67 | index.add_scope(ScopeData { 68 | kind, 69 | span, 70 | parent_scope, 71 | child_scopes: Vec::default(), 72 | bindings: HashMap::default(), 73 | }) 74 | } 75 | 76 | pub fn get_kind(&self) -> ScopeKind { 77 | self.index.borrow()[self.id].kind 78 | } 79 | 80 | pub fn get_span(&self) -> Span { 81 | self.index.borrow()[self.id].span 82 | } 83 | 84 | pub fn get_scope_for_span(&self, span: Span) -> Scope { 85 | let scopes = self.index.borrow(); 86 | let mut scope_id = self.id; 87 | 'descend: loop { 88 | let scope_data = &scopes[scope_id]; 89 | for child_scope in &scope_data.child_scopes { 90 | if child_scope.get_span().contains(span) { 91 | scope_id = child_scope.id; 92 | continue 'descend; 93 | } 94 | } 95 | break; 96 | } 97 | let scope = Scope { 98 | id: scope_id, 99 | index: self.index.clone(), 100 | }; 101 | assert!(scope.get_span().contains(span)); 102 | scope 103 | } 104 | 105 | pub fn get_parent_scope(&self) -> Option { 106 | self.index.borrow()[self.id] 107 | .parent_scope 108 | .as_ref() 109 | .map(Scope::clone) 110 | } 111 | 112 | pub fn add_child_scope(&self, child_scope: &Scope) { 113 | self.index.borrow_mut()[self.id] 114 | .child_scopes 115 | .push(child_scope.clone()) 116 | } 117 | 118 | /// Use a negative value to specify an index that is relative to the end of 119 | /// the list. E.g. `scope.get_child_scope(-1)` returns the last child scope. 120 | pub fn get_child_scope(&self, index: isize) -> Option { 121 | let scopes = self.index.borrow(); 122 | let child_scopes = &scopes[self.id].child_scopes; 123 | let index: usize = if index >= 0 { 124 | index as usize 125 | } else { 126 | child_scopes.len() - (-index as usize) 127 | }; 128 | child_scopes.get(index).map(Scope::clone) 129 | } 130 | 131 | pub fn child_scope_count(&self) -> usize { 132 | self.index.borrow()[self.id].child_scopes.len() 133 | } 134 | 135 | /// Adds a new binding to this scope. If a binding with the same name has been 136 | /// added earlier, that binding is removed and returned. 137 | pub fn add_binding( 138 | &mut self, 139 | binding_name: impl Display, 140 | binding_kind: BindingKind, 141 | ) -> Option { 142 | self.index.borrow_mut()[self.id] 143 | .bindings 144 | .insert(binding_name.to_string(), binding_kind) 145 | } 146 | 147 | pub fn get_binding( 148 | &self, 149 | binding_name: impl AsRef, 150 | ) -> Option { 151 | let binding_name = binding_name.as_ref(); 152 | let scopes = self.index.borrow(); 153 | let mut scope_id = self.id; 154 | loop { 155 | let scope_data = &scopes[scope_id]; 156 | if let Some(&binding_kind) = scope_data.bindings.get(binding_name) { 157 | break Some(binding_kind); 158 | } else if let Some(parent) = &scope_data.parent_scope { 159 | scope_id = parent.id; 160 | } else { 161 | break None; 162 | } 163 | } 164 | } 165 | } 166 | 167 | #[derive(Clone, Debug, Default)] 168 | struct ScopeIndex(Rc>>); 169 | 170 | impl Deref for ScopeIndex { 171 | type Target = RefCell>; 172 | fn deref(&self) -> &Self::Target { 173 | &self.0 174 | } 175 | } 176 | 177 | impl Eq for ScopeIndex {} 178 | 179 | impl PartialEq for ScopeIndex { 180 | fn eq(&self, other: &Self) -> bool { 181 | Rc::as_ptr(&self.0) == Rc::as_ptr(&other.0) 182 | } 183 | } 184 | 185 | impl Hash for ScopeIndex { 186 | fn hash(&self, state: &mut H) { 187 | Rc::as_ptr(&self.0).hash(state) 188 | } 189 | } 190 | 191 | impl ScopeIndex { 192 | fn add_scope(&self, scope_data: ScopeData) -> Scope { 193 | let mut parent_scope = scope_data.parent_scope.clone(); 194 | let scope = { 195 | let scopes = &mut self.borrow_mut(); 196 | let id = scopes.len(); 197 | scopes.push(scope_data); 198 | Scope { 199 | id, 200 | index: self.clone(), 201 | } 202 | }; 203 | match parent_scope.as_mut() { 204 | Some(p) => p.add_child_scope(&scope), 205 | None => assert_eq!(scope.id, 0), 206 | }; 207 | scope 208 | } 209 | } 210 | 211 | #[derive(Debug)] 212 | pub struct ScopeVisitor { 213 | pub scope_stack: Vec, 214 | } 215 | 216 | impl Default for ScopeVisitor { 217 | fn default() -> Self { 218 | let program_scope = Scope::new(ScopeKind::Program, DUMMY_SP, None); 219 | let scope_stack = vec![program_scope]; 220 | Self { scope_stack } 221 | } 222 | } 223 | 224 | impl ScopeVisitor { 225 | pub fn enter_scope(&mut self, scope: &Scope) { 226 | self.scope_stack.push(scope.clone()); 227 | } 228 | 229 | pub fn exit_scope(&mut self, scope: &Scope) { 230 | assert!(self.scope_stack.len() > 1); 231 | let exited_scope = self.scope_stack.pop().unwrap(); 232 | assert_eq!(&exited_scope, scope); 233 | } 234 | 235 | pub fn get_current_scope(&self) -> Scope { 236 | assert!(!self.scope_stack.is_empty()); 237 | self.scope_stack.last().unwrap().clone() 238 | } 239 | 240 | pub fn get_root_scope(&self) -> Scope { 241 | self.scope_stack[0].clone() 242 | } 243 | 244 | fn create_fn_scope(&mut self, function: &swc_ecma_ast::Function) { 245 | if let Some(body) = &function.body { 246 | let fn_scope = Scope::new( 247 | ScopeKind::Function, 248 | function.span, 249 | Some(self.get_current_scope()), 250 | ); 251 | self.enter_scope(&fn_scope); 252 | self.visit_function(&function, body); 253 | self.exit_scope(&fn_scope); 254 | } 255 | } 256 | 257 | fn create_getter_or_setter_scope( 258 | &mut self, 259 | body: &Option, 260 | ) { 261 | if let Some(body) = &body { 262 | let gs_scope = Scope::new( 263 | ScopeKind::Function, 264 | body.span, 265 | Some(self.get_current_scope()), 266 | ); 267 | self.enter_scope(&gs_scope); 268 | for stmt in body.stmts.iter() { 269 | self.visit_stmt(stmt, body); 270 | } 271 | self.exit_scope(&gs_scope); 272 | } 273 | } 274 | 275 | fn check_object_lit(&mut self, obj: &swc_ecma_ast::ObjectLit) { 276 | if obj.props.is_empty() { 277 | return; 278 | } 279 | use swc_ecma_ast::Prop::*; 280 | for prop in obj.props.iter() { 281 | if let swc_ecma_ast::PropOrSpread::Prop(prop_expr) = prop { 282 | match &**prop_expr { 283 | Method(method_prop) => { 284 | self.create_fn_scope(&method_prop.function); 285 | } 286 | KeyValue(kv_prop) => { 287 | if let swc_ecma_ast::Expr::Fn(fn_expr) = &*kv_prop.value { 288 | self.create_fn_scope(&fn_expr.function); 289 | } else { 290 | self.check_expr(&kv_prop.value); 291 | } 292 | } 293 | Getter(getter) => { 294 | self.create_getter_or_setter_scope(&getter.body); 295 | } 296 | Setter(setter) => { 297 | self.create_getter_or_setter_scope(&setter.body); 298 | } 299 | _ => {} 300 | } 301 | } 302 | } 303 | } 304 | 305 | fn check_expr(&mut self, expr: &swc_ecma_ast::Expr) { 306 | match expr { 307 | swc_ecma_ast::Expr::Arrow(arrow) => { 308 | self.visit_block_stmt_or_expr(&arrow.body, arrow); 309 | } 310 | swc_ecma_ast::Expr::Object(obj_lit) => { 311 | self.check_object_lit(&obj_lit); 312 | } 313 | swc_ecma_ast::Expr::Array(arr_lit) => { 314 | self.check_array_lit(&arr_lit); 315 | } 316 | _ => {} 317 | } 318 | } 319 | 320 | fn check_array_lit(&mut self, arr: &swc_ecma_ast::ArrayLit) { 321 | if arr.elems.is_empty() { 322 | return; 323 | } 324 | for elem in arr.elems.iter() { 325 | if let Some(element) = elem { 326 | if let swc_ecma_ast::Expr::Fn(fn_expr) = &*element.expr { 327 | self.create_fn_scope(&fn_expr.function); 328 | } else { 329 | self.check_expr(&element.expr); 330 | } 331 | } 332 | } 333 | } 334 | 335 | fn check_pat(&mut self, pat: &Pat, kind: BindingKind) { 336 | match pat { 337 | Pat::Ident(ident) => { 338 | self.get_current_scope().add_binding(&ident.sym, kind); 339 | } 340 | Pat::Assign(assign) => { 341 | self.check_pat(&assign.left, kind); 342 | } 343 | Pat::Array(array) => { 344 | self.check_array_pat(array, kind); 345 | } 346 | Pat::Object(object) => { 347 | self.check_obj_pat(object, kind); 348 | } 349 | Pat::Rest(rest) => { 350 | self.check_pat(&rest.arg, kind); 351 | } 352 | _ => {} 353 | } 354 | } 355 | 356 | fn check_obj_pat( 357 | &mut self, 358 | object: &swc_ecma_ast::ObjectPat, 359 | kind: BindingKind, 360 | ) { 361 | if !object.props.is_empty() { 362 | for prop in object.props.iter() { 363 | match prop { 364 | ObjectPatProp::Assign(assign_prop) => { 365 | self 366 | .get_current_scope() 367 | .add_binding(&assign_prop.key.sym, kind); 368 | } 369 | ObjectPatProp::KeyValue(kv_prop) => { 370 | self.check_pat(&kv_prop.value, kind); 371 | } 372 | ObjectPatProp::Rest(rest) => { 373 | self.check_pat(&rest.arg, kind); 374 | } 375 | } 376 | } 377 | } 378 | } 379 | 380 | fn check_array_pat( 381 | &mut self, 382 | array: &swc_ecma_ast::ArrayPat, 383 | kind: BindingKind, 384 | ) { 385 | if !array.elems.is_empty() { 386 | for elem in array.elems.iter() { 387 | if let Some(element) = elem { 388 | self.check_pat(element, kind); 389 | } 390 | } 391 | } 392 | } 393 | } 394 | 395 | impl Visit for ScopeVisitor { 396 | fn visit_module(&mut self, module: &swc_ecma_ast::Module, parent: &dyn Node) { 397 | let module_scope = Scope::new( 398 | ScopeKind::Module, 399 | module.span, 400 | Some(self.get_current_scope()), 401 | ); 402 | self.enter_scope(&module_scope); 403 | swc_ecma_visit::visit_module(self, module, parent); 404 | self.exit_scope(&module_scope); 405 | } 406 | 407 | fn visit_object_lit( 408 | &mut self, 409 | obj_lit: &swc_ecma_ast::ObjectLit, 410 | _parent: &dyn Node, 411 | ) { 412 | self.check_object_lit(obj_lit); 413 | } 414 | 415 | fn visit_array_lit( 416 | &mut self, 417 | arr_lit: &swc_ecma_ast::ArrayLit, 418 | _parent: &dyn Node, 419 | ) { 420 | self.check_array_lit(arr_lit); 421 | } 422 | 423 | fn visit_call_expr( 424 | &mut self, 425 | call_expr: &swc_ecma_ast::CallExpr, 426 | _parent: &dyn Node, 427 | ) { 428 | if call_expr.args.is_empty() { 429 | return; 430 | } 431 | for arg in call_expr.args.iter() { 432 | if let swc_ecma_ast::Expr::Fn(fn_expr) = &*arg.expr { 433 | self.create_fn_scope(&fn_expr.function); 434 | } else { 435 | self.check_expr(&arg.expr) 436 | } 437 | } 438 | } 439 | 440 | fn visit_new_expr( 441 | &mut self, 442 | new_expr: &swc_ecma_ast::NewExpr, 443 | _parent: &dyn Node, 444 | ) { 445 | if let Some(args) = &new_expr.args { 446 | for arg in args.iter() { 447 | if let swc_ecma_ast::Expr::Fn(fn_expr) = &*arg.expr { 448 | self.create_fn_scope(&fn_expr.function); 449 | } else { 450 | self.check_expr(&arg.expr) 451 | } 452 | } 453 | } 454 | } 455 | 456 | fn visit_fn_decl( 457 | &mut self, 458 | fn_decl: &swc_ecma_ast::FnDecl, 459 | parent: &dyn Node, 460 | ) { 461 | self 462 | .get_current_scope() 463 | .add_binding(&fn_decl.ident.sym, BindingKind::Function); 464 | let fn_scope = Scope::new( 465 | ScopeKind::Function, 466 | fn_decl.function.span, 467 | Some(self.get_current_scope()), 468 | ); 469 | self.enter_scope(&fn_scope); 470 | swc_ecma_visit::visit_fn_decl(self, fn_decl, parent); 471 | self.exit_scope(&fn_scope); 472 | } 473 | 474 | fn visit_class_decl( 475 | &mut self, 476 | class_decl: &swc_ecma_ast::ClassDecl, 477 | parent: &dyn Node, 478 | ) { 479 | self 480 | .get_current_scope() 481 | .add_binding(&class_decl.ident.sym, BindingKind::Class); 482 | let class_scope = Scope::new( 483 | ScopeKind::Class, 484 | class_decl.class.span, 485 | Some(self.get_current_scope()), 486 | ); 487 | self.enter_scope(&class_scope); 488 | swc_ecma_visit::visit_class_decl(self, class_decl, parent); 489 | self.exit_scope(&class_scope); 490 | } 491 | 492 | fn visit_function( 493 | &mut self, 494 | function: &swc_ecma_ast::Function, 495 | _parent: &dyn Node, 496 | ) { 497 | for param in &function.params { 498 | self.check_pat(¶m.pat, BindingKind::Param); 499 | } 500 | 501 | // Not calling `swc_ecma_visit::visit_function` but instead 502 | // directly visiting body elements - otherwise additional 503 | // block scope will be created which is undesirable 504 | if let Some(body) = &function.body { 505 | for stmt in &body.stmts { 506 | swc_ecma_visit::visit_stmt(self, stmt, body); 507 | } 508 | } 509 | } 510 | 511 | fn visit_import_specifier( 512 | &mut self, 513 | import_spec: &swc_ecma_ast::ImportSpecifier, 514 | _parent: &dyn Node, 515 | ) { 516 | use swc_ecma_ast::ImportSpecifier::*; 517 | let local = match import_spec { 518 | Named(named) => &named.local, 519 | Default(default) => &default.local, 520 | Namespace(namespace) => &namespace.local, 521 | }; 522 | self 523 | .get_current_scope() 524 | .add_binding(&local.sym, BindingKind::Import); 525 | } 526 | 527 | fn visit_with_stmt( 528 | &mut self, 529 | with_stmt: &swc_ecma_ast::WithStmt, 530 | _parent: &dyn Node, 531 | ) { 532 | self.visit_expr(&*with_stmt.obj, with_stmt); 533 | 534 | let with_scope = Scope::new( 535 | ScopeKind::With, 536 | with_stmt.span, 537 | Some(self.get_current_scope()), 538 | ); 539 | self.enter_scope(&with_scope); 540 | swc_ecma_visit::visit_stmt(self, &*with_stmt.body, with_stmt); 541 | self.exit_scope(&with_scope); 542 | } 543 | 544 | fn visit_switch_stmt( 545 | &mut self, 546 | switch_stmt: &swc_ecma_ast::SwitchStmt, 547 | _parent: &dyn Node, 548 | ) { 549 | self.visit_expr(&*switch_stmt.discriminant, switch_stmt); 550 | 551 | let switch_scope = Scope::new( 552 | ScopeKind::Switch, 553 | switch_stmt.span, 554 | Some(self.get_current_scope()), 555 | ); 556 | self.enter_scope(&switch_scope); 557 | for case in &switch_stmt.cases { 558 | swc_ecma_visit::visit_switch_case(self, case, switch_stmt); 559 | } 560 | self.exit_scope(&switch_scope); 561 | } 562 | 563 | fn visit_var_decl( 564 | &mut self, 565 | var_decl: &swc_ecma_ast::VarDecl, 566 | parent: &dyn Node, 567 | ) { 568 | use swc_ecma_ast::VarDeclKind; 569 | 570 | let var_kind = match &var_decl.kind { 571 | VarDeclKind::Var => BindingKind::Var, 572 | VarDeclKind::Let => BindingKind::Let, 573 | VarDeclKind::Const => BindingKind::Const, 574 | }; 575 | 576 | for decl in &var_decl.decls { 577 | self.check_pat(&decl.name, var_kind); 578 | } 579 | swc_ecma_visit::visit_var_decl(self, var_decl, parent); 580 | } 581 | 582 | fn visit_block_stmt( 583 | &mut self, 584 | block_stmt: &swc_ecma_ast::BlockStmt, 585 | parent: &dyn Node, 586 | ) { 587 | let block_scope = Scope::new( 588 | ScopeKind::Block, 589 | block_stmt.span, 590 | Some(self.get_current_scope()), 591 | ); 592 | self.enter_scope(&block_scope); 593 | swc_ecma_visit::visit_block_stmt(self, block_stmt, parent); 594 | self.exit_scope(&block_scope); 595 | } 596 | 597 | fn visit_catch_clause( 598 | &mut self, 599 | catch_clause: &swc_ecma_ast::CatchClause, 600 | _parent: &dyn Node, 601 | ) { 602 | let catch_scope = Scope::new( 603 | ScopeKind::Catch, 604 | catch_clause.span, 605 | Some(self.get_current_scope()), 606 | ); 607 | self.enter_scope(&catch_scope); 608 | 609 | if let Some(pat) = &catch_clause.param { 610 | self.check_pat(pat, BindingKind::CatchClause); 611 | } 612 | 613 | // Not calling `swc_ecma_visit::visit_class` but instead 614 | // directly visiting body elements - otherwise additional 615 | // block scope will be created which is undesirable 616 | for stmt in &catch_clause.body.stmts { 617 | swc_ecma_visit::visit_stmt(self, stmt, &catch_clause.body); 618 | } 619 | 620 | self.exit_scope(&catch_scope); 621 | } 622 | 623 | fn visit_for_stmt( 624 | &mut self, 625 | for_stmt: &swc_ecma_ast::ForStmt, 626 | parent: &dyn Node, 627 | ) { 628 | let loop_scope = Scope::new( 629 | ScopeKind::Loop, 630 | for_stmt.span, 631 | Some(self.get_current_scope()), 632 | ); 633 | self.enter_scope(&loop_scope); 634 | if let Some(swc_ecma_ast::VarDeclOrExpr::VarDecl(var_decl)) = 635 | for_stmt.init.as_ref() 636 | { 637 | self.visit_var_decl(var_decl, parent); 638 | } 639 | if let swc_ecma_ast::Stmt::Block(body_block) = &*for_stmt.body { 640 | for stmt in &body_block.stmts { 641 | swc_ecma_visit::visit_stmt(self, &stmt, &for_stmt.body); 642 | } 643 | } 644 | self.exit_scope(&loop_scope); 645 | } 646 | 647 | fn visit_for_in_stmt( 648 | &mut self, 649 | for_in_stmt: &swc_ecma_ast::ForInStmt, 650 | parent: &dyn Node, 651 | ) { 652 | let loop_scope = Scope::new( 653 | ScopeKind::Loop, 654 | for_in_stmt.span, 655 | Some(self.get_current_scope()), 656 | ); 657 | self.enter_scope(&loop_scope); 658 | if let swc_ecma_ast::VarDeclOrPat::VarDecl(var_decl) = &for_in_stmt.left { 659 | self.visit_var_decl(var_decl, parent); 660 | } 661 | if let swc_ecma_ast::Stmt::Block(body_block) = &*for_in_stmt.body { 662 | for stmt in &body_block.stmts { 663 | swc_ecma_visit::visit_stmt(self, &stmt, &for_in_stmt.body); 664 | } 665 | } 666 | self.exit_scope(&loop_scope); 667 | } 668 | 669 | fn visit_for_of_stmt( 670 | &mut self, 671 | for_of_stmt: &swc_ecma_ast::ForOfStmt, 672 | parent: &dyn Node, 673 | ) { 674 | let loop_scope = Scope::new( 675 | ScopeKind::Loop, 676 | for_of_stmt.span, 677 | Some(self.get_current_scope()), 678 | ); 679 | self.enter_scope(&loop_scope); 680 | if let swc_ecma_ast::VarDeclOrPat::VarDecl(var_decl) = &for_of_stmt.left { 681 | self.visit_var_decl(var_decl, parent); 682 | } 683 | if let swc_ecma_ast::Stmt::Block(body_block) = &*for_of_stmt.body { 684 | for stmt in &body_block.stmts { 685 | swc_ecma_visit::visit_stmt(self, &stmt, &for_of_stmt.body); 686 | } 687 | } 688 | self.exit_scope(&loop_scope); 689 | } 690 | 691 | fn visit_while_stmt( 692 | &mut self, 693 | while_stmt: &swc_ecma_ast::WhileStmt, 694 | _parent: &dyn Node, 695 | ) { 696 | let loop_scope = Scope::new( 697 | ScopeKind::Loop, 698 | while_stmt.span, 699 | Some(self.get_current_scope()), 700 | ); 701 | self.enter_scope(&loop_scope); 702 | if let swc_ecma_ast::Stmt::Block(body_block) = &*while_stmt.body { 703 | for stmt in &body_block.stmts { 704 | swc_ecma_visit::visit_stmt(self, &stmt, &while_stmt.body); 705 | } 706 | } 707 | self.exit_scope(&loop_scope); 708 | } 709 | 710 | fn visit_do_while_stmt( 711 | &mut self, 712 | do_while_stmt: &swc_ecma_ast::DoWhileStmt, 713 | _parent: &dyn Node, 714 | ) { 715 | let loop_scope = Scope::new( 716 | ScopeKind::Loop, 717 | do_while_stmt.span, 718 | Some(self.get_current_scope()), 719 | ); 720 | self.enter_scope(&loop_scope); 721 | if let swc_ecma_ast::Stmt::Block(body_block) = &*do_while_stmt.body { 722 | for stmt in &body_block.stmts { 723 | swc_ecma_visit::visit_stmt(self, &stmt, &do_while_stmt.body); 724 | } 725 | } 726 | self.exit_scope(&loop_scope); 727 | } 728 | } 729 | 730 | #[cfg(test)] 731 | mod tests { 732 | use super::*; 733 | use crate::parser; 734 | use crate::parser::AstParser; 735 | 736 | fn test_scopes(source_code: &str) -> Scope { 737 | let ast_parser = AstParser::new(); 738 | let syntax = crate::parser::get_default_ts_config(); 739 | let module = ast_parser 740 | .parse_module("file_name.ts", syntax, source_code) 741 | .unwrap(); 742 | let mut scope_visitor = ScopeVisitor::default(); 743 | let root_scope = scope_visitor.get_root_scope(); 744 | scope_visitor.visit_module(&module, &module); 745 | root_scope 746 | } 747 | 748 | #[test] 749 | fn scopes() { 750 | let source_code = r#" 751 | const a = "a"; 752 | const unused = "unused"; 753 | function asdf(b: number, c: string): number { 754 | console.log(a, b); 755 | { 756 | const c = 1; 757 | let d = 2; 758 | } 759 | return 1; 760 | } 761 | class Foo { 762 | #fizz = "fizz"; 763 | bar() { 764 | } 765 | } 766 | try { 767 | // some code that might throw 768 | throw new Error("asdf"); 769 | } catch (e) { 770 | const msg = "asdf " + e.message; 771 | } 772 | "#; 773 | let root_scope = test_scopes(source_code); 774 | assert_eq!(root_scope.get_kind(), ScopeKind::Program); 775 | assert_eq!(root_scope.child_scope_count(), 1); 776 | 777 | let module_scope = root_scope.get_child_scope(0).unwrap(); 778 | assert_eq!(module_scope.get_kind(), ScopeKind::Module); 779 | assert_eq!(module_scope.child_scope_count(), 4); 780 | 781 | let fn_scope = module_scope.get_child_scope(0).unwrap(); 782 | assert_eq!(fn_scope.get_kind(), ScopeKind::Function); 783 | assert_eq!(fn_scope.child_scope_count(), 1); 784 | 785 | let block_scope = fn_scope.get_child_scope(0).unwrap(); 786 | assert_eq!(block_scope.get_kind(), ScopeKind::Block); 787 | assert_eq!(block_scope.child_scope_count(), 0); 788 | 789 | let class_scope = module_scope.get_child_scope(1).unwrap(); 790 | assert_eq!(class_scope.get_kind(), ScopeKind::Class); 791 | assert_eq!(class_scope.child_scope_count(), 0); 792 | 793 | let catch_scope = module_scope.get_child_scope(3).unwrap(); 794 | assert_eq!(catch_scope.get_kind(), ScopeKind::Catch); 795 | assert_eq!(catch_scope.child_scope_count(), 0); 796 | let catch_clause_e = catch_scope.get_binding("e").unwrap(); 797 | assert_eq!(catch_clause_e, BindingKind::CatchClause); 798 | } 799 | 800 | #[test] 801 | fn switch_scope() { 802 | let source_code = r#" 803 | switch (foo) { 804 | case "foo": 805 | let a = "a"; 806 | a = "b"; 807 | a; 808 | break; 809 | case "bar": 810 | break; 811 | default: 812 | const defaultVal = "default"; 813 | return defaultVal; 814 | } 815 | "#; 816 | let root_scope = test_scopes(source_code); 817 | assert_eq!(root_scope.get_kind(), ScopeKind::Program); 818 | assert_eq!(root_scope.child_scope_count(), 1); 819 | 820 | let module_scope = root_scope.get_child_scope(0).unwrap(); 821 | assert_eq!(module_scope.get_kind(), ScopeKind::Module); 822 | assert_eq!(module_scope.child_scope_count(), 1); 823 | 824 | let switch_scope = module_scope.get_child_scope(0).unwrap(); 825 | assert_eq!(switch_scope.get_kind(), ScopeKind::Switch); 826 | assert_eq!(switch_scope.child_scope_count(), 0); 827 | assert!(switch_scope.get_binding("a").is_some()); 828 | assert!(switch_scope.get_binding("defaultVal").is_some()); 829 | } 830 | #[test] 831 | fn loop_scopes() { 832 | let source_code = r#" 833 | for (let i = 0; i < 10; i++){} 834 | for (let i in [1,2,3]){} 835 | for (let i of [1,2,3]){} 836 | while (i > 1) {} 837 | do {} while (i > 1) 838 | "#; 839 | let root_scope = test_scopes(source_code); 840 | assert_eq!(root_scope.get_kind(), ScopeKind::Program); 841 | assert_eq!(root_scope.child_scope_count(), 1); 842 | 843 | let module_scope = root_scope.get_child_scope(0).unwrap(); 844 | assert_eq!(module_scope.get_kind(), ScopeKind::Module); 845 | assert_eq!(module_scope.child_scope_count(), 5); 846 | 847 | let for_scope = module_scope.get_child_scope(0).unwrap(); 848 | assert_eq!(for_scope.get_kind(), ScopeKind::Loop); 849 | assert_eq!(for_scope.child_scope_count(), 0); 850 | assert!(for_scope.get_binding("i").is_some()); 851 | 852 | let for_in_scope = module_scope.get_child_scope(1).unwrap(); 853 | assert_eq!(for_in_scope.get_kind(), ScopeKind::Loop); 854 | assert_eq!(for_in_scope.child_scope_count(), 0); 855 | assert!(for_in_scope.get_binding("i").is_some()); 856 | 857 | let for_of_scope = module_scope.get_child_scope(2).unwrap(); 858 | assert_eq!(for_of_scope.get_kind(), ScopeKind::Loop); 859 | assert_eq!(for_of_scope.child_scope_count(), 0); 860 | assert!(for_of_scope.get_binding("i").is_some()); 861 | 862 | let while_scope = module_scope.get_child_scope(3).unwrap(); 863 | assert_eq!(while_scope.get_kind(), ScopeKind::Loop); 864 | assert_eq!(while_scope.child_scope_count(), 0); 865 | 866 | let do_while_scope = module_scope.get_child_scope(-1).unwrap(); 867 | assert_eq!(do_while_scope.get_kind(), ScopeKind::Loop); 868 | assert_eq!(do_while_scope.child_scope_count(), 0); 869 | } 870 | 871 | #[test] 872 | fn call_new_expressions() { 873 | let source_code = r#" 874 | Deno.test("first test", function(){}); 875 | new Deno(function(){}); 876 | "#; 877 | 878 | let root_scope = test_scopes(source_code); 879 | assert_eq!(root_scope.get_kind(), ScopeKind::Program); 880 | assert_eq!(root_scope.child_scope_count(), 1); 881 | 882 | let module_scope = root_scope.get_child_scope(0).unwrap(); 883 | assert_eq!(module_scope.get_kind(), ScopeKind::Module); 884 | assert_eq!(module_scope.child_scope_count(), 2); 885 | 886 | let call_fn_scope = module_scope.get_child_scope(0).unwrap(); 887 | assert_eq!(call_fn_scope.get_kind(), ScopeKind::Function); 888 | 889 | let new_fn_scope = module_scope.get_child_scope(-1).unwrap(); 890 | assert_eq!(new_fn_scope.get_kind(), ScopeKind::Function); 891 | } 892 | 893 | #[test] 894 | fn object_literal() { 895 | let source_code = r#" 896 | let obj = { 897 | method(){ 898 | const e; 899 | }, 900 | nested : { 901 | nestedMethod(){ 902 | const f; 903 | } 904 | }, 905 | getterAndSetter : { 906 | get getter(){ 907 | const g; 908 | return g; 909 | }, 910 | set setter(s){ 911 | const h; 912 | } 913 | } 914 | } 915 | "#; 916 | let root_scope = test_scopes(source_code); 917 | assert_eq!(root_scope.get_kind(), ScopeKind::Program); 918 | assert_eq!(root_scope.child_scope_count(), 1); 919 | 920 | let module_scope = root_scope.get_child_scope(0).unwrap(); 921 | assert_eq!(module_scope.get_kind(), ScopeKind::Module); 922 | assert_eq!(module_scope.child_scope_count(), 4); 923 | 924 | let obj_method_scope = module_scope.get_child_scope(0).unwrap(); 925 | assert_eq!(obj_method_scope.get_kind(), ScopeKind::Function); 926 | assert!(obj_method_scope.get_binding("e").is_some()); 927 | 928 | let obj_nested_method_scope = module_scope.get_child_scope(1).unwrap(); 929 | assert_eq!(obj_nested_method_scope.get_kind(), ScopeKind::Function); 930 | assert!(obj_nested_method_scope.get_binding("f").is_some()); 931 | 932 | let obj_getter_scope = module_scope.get_child_scope(2).unwrap(); 933 | assert_eq!(obj_getter_scope.get_kind(), ScopeKind::Function); 934 | assert!(obj_getter_scope.get_binding("g").is_some()); 935 | assert!(obj_getter_scope.get_binding("h").is_none()); 936 | 937 | let obj_setter_scope = module_scope.get_child_scope(3).unwrap(); 938 | assert_eq!(obj_setter_scope.get_kind(), ScopeKind::Function); 939 | assert!(obj_setter_scope.get_binding("h").is_some()); 940 | assert!(obj_setter_scope.get_binding("g").is_none()); 941 | } 942 | 943 | #[test] 944 | fn array_literal() { 945 | let source_code = r#" 946 | let array = [ 947 | function x(){ const a; }, 948 | ()=>{const b;}, 949 | [ 950 | function nested() { const c;} 951 | ], 952 | { 953 | innerMethod(){ 954 | const d; 955 | } 956 | } 957 | ] 958 | "#; 959 | 960 | let root_scope = test_scopes(source_code); 961 | assert_eq!(root_scope.get_kind(), ScopeKind::Program); 962 | assert_eq!(root_scope.child_scope_count(), 1); 963 | 964 | let module_scope = root_scope.get_child_scope(0).unwrap(); 965 | assert_eq!(module_scope.get_kind(), ScopeKind::Module); 966 | assert_eq!(module_scope.child_scope_count(), 4); 967 | 968 | let array_fn_scope = module_scope.get_child_scope(0).unwrap(); 969 | assert_eq!(array_fn_scope.get_kind(), ScopeKind::Function); 970 | assert!(array_fn_scope.get_binding("a").is_some()); 971 | assert!(array_fn_scope.get_binding("b").is_none()); 972 | 973 | let array_arrow_scope = module_scope.get_child_scope(1).unwrap(); 974 | assert_eq!(array_arrow_scope.get_kind(), ScopeKind::Block); 975 | assert!(array_arrow_scope.get_binding("b").is_some()); 976 | assert!(array_arrow_scope.get_binding("c").is_none()); 977 | 978 | let array_nested_fn_scope = module_scope.get_child_scope(2).unwrap(); 979 | assert_eq!(array_nested_fn_scope.get_kind(), ScopeKind::Function); 980 | assert!(array_nested_fn_scope.get_binding("c").is_some()); 981 | 982 | let array_object_method_scope = module_scope.get_child_scope(3).unwrap(); 983 | assert_eq!(array_object_method_scope.get_kind(), ScopeKind::Function); 984 | assert!(array_object_method_scope.get_binding("d").is_some()); 985 | } 986 | 987 | #[test] 988 | fn import_binding() { 989 | let source_code = r#" 990 | import defaultExport1 from "module-name"; 991 | import * as namespaced1 from "module-name"; 992 | import { export1 } from "module-name"; 993 | import { export2 as alias1 } from "module-name"; 994 | import { export3 , export4 } from "module-name"; 995 | import { export5 , export6 as alias2} from "module-name"; 996 | import defaultExport2, { export7 } from "module-name"; 997 | import defaultExport3, * as namespaced2 from "module-name"; 998 | import "module-name"; 999 | var promise = import("module-name"); 1000 | "#; 1001 | 1002 | let root_scope = test_scopes(source_code); 1003 | assert_eq!(root_scope.get_kind(), ScopeKind::Program); 1004 | assert_eq!(root_scope.child_scope_count(), 1); 1005 | 1006 | let module_scope = root_scope.get_child_scope(0).unwrap(); 1007 | assert_eq!(module_scope.get_kind(), ScopeKind::Module); 1008 | assert_eq!(module_scope.child_scope_count(), 0); 1009 | let default_export = module_scope.get_binding("defaultExport1").unwrap(); 1010 | assert_eq!(default_export, BindingKind::Import); 1011 | let namespaced1 = module_scope.get_binding("namespaced1").unwrap(); 1012 | assert_eq!(namespaced1, BindingKind::Import); 1013 | let export1 = module_scope.get_binding("export1").unwrap(); 1014 | assert_eq!(export1, BindingKind::Import); 1015 | assert!(module_scope.get_binding("export2").is_none()); 1016 | assert!(module_scope.get_binding("alias1").is_some()); 1017 | assert!(module_scope.get_binding("export3").is_some()); 1018 | assert!(module_scope.get_binding("export4").is_some()); 1019 | assert!(module_scope.get_binding("export5").is_some()); 1020 | assert!(module_scope.get_binding("export6").is_none()); 1021 | assert!(module_scope.get_binding("alias2").is_some()); 1022 | assert!(module_scope.get_binding("defaultExport2").is_some()); 1023 | assert!(module_scope.get_binding("export7").is_some()); 1024 | assert!(module_scope.get_binding("defaultExport3").is_some()); 1025 | assert!(module_scope.get_binding("namespaced2").is_some()); 1026 | } 1027 | 1028 | #[test] 1029 | fn destructuring_assignment() { 1030 | let source_code = r#" 1031 | const {a} = {a : "a"}; 1032 | const {a: {b}} = {a : {b: "b"}}; 1033 | const {a: {b: {c}}} = {a : {b: {c : "c"}}}; 1034 | const [d] = ["d"]; 1035 | const [e, [f,[g]]] = ["e",["f",["g"]]]; 1036 | const {a: [h]} = {a: ["h"]}; 1037 | const [i, {j}] = ["i",{j: "j"}]; 1038 | const {a: {b : [k,{l}]}} = {a: {b : ["k",{l : "l"}]}}; 1039 | const {m = "M"} = {}; 1040 | const [n = "N"] = []; 1041 | const {...o} = {o : "O"}; 1042 | const [...p] = ["p"]; 1043 | function getPerson({username="disizali",info: [name, family]}) { 1044 | try { 1045 | throw 'TryAgain'; 1046 | } catch(e) {} 1047 | } 1048 | try{} catch({message}){}; 1049 | "#; 1050 | let root_scope = test_scopes(source_code); 1051 | let module_scope = root_scope.get_child_scope(0).unwrap(); 1052 | assert!(module_scope.get_binding("a").is_some()); 1053 | assert!(module_scope.get_binding("b").is_some()); 1054 | assert!(module_scope.get_binding("c").is_some()); 1055 | assert!(module_scope.get_binding("d").is_some()); 1056 | assert!(module_scope.get_binding("e").is_some()); 1057 | assert!(module_scope.get_binding("f").is_some()); 1058 | assert!(module_scope.get_binding("g").is_some()); 1059 | assert!(module_scope.get_binding("h").is_some()); 1060 | assert!(module_scope.get_binding("i").is_some()); 1061 | assert!(module_scope.get_binding("j").is_some()); 1062 | assert!(module_scope.get_binding("k").is_some()); 1063 | assert!(module_scope.get_binding("l").is_some()); 1064 | assert!(module_scope.get_binding("m").is_some()); 1065 | assert!(module_scope.get_binding("n").is_some()); 1066 | assert!(module_scope.get_binding("o").is_some()); 1067 | assert!(module_scope.get_binding("p").is_some()); 1068 | 1069 | let function_scope = module_scope.get_child_scope(0).unwrap(); 1070 | assert!(function_scope.get_binding("username").is_some()); 1071 | assert!(function_scope.get_binding("name").is_some()); 1072 | assert!(function_scope.get_binding("family").is_some()); 1073 | 1074 | let function_catch_scope = function_scope.get_child_scope(-1).unwrap(); 1075 | assert!(function_catch_scope.get_binding("e").is_some()); 1076 | 1077 | let catch_scope = module_scope.get_child_scope(-1).unwrap(); 1078 | assert!(catch_scope.get_binding("message").is_some()); 1079 | } 1080 | } 1081 | --------------------------------------------------------------------------------