diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5427e2..00e4ee8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,39 +1,76 @@ -name: Continuous Integration - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -permissions: - contents: read - -jobs: - build_and_lint: - name: Build & Lint Check - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - - uses: actions/checkout@v7 - - - name: Install pnpm - uses: pnpm/action-setup@v6 - with: - version: 9.15.0 - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: 24 - cache: pnpm - - - name: Install Dependencies - run: pnpm install --frozen-lockfile - - - name: Run Linter - run: pnpm run lint - - - name: Compile and Build Webview - run: pnpm run compile +name: Continuous Integration + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build_and_lint: + name: Build & Lint Check + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + + uses: actions/checkout@v7 + + - name: Install pnpm + uses: pnpm/action-setup@v6 + with: + version: 9.15.0 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: pnpm + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Run Linter + run: pnpm run lint + + - name: Compile and Build Webview + run: pnpm run compile + +name: Continuous Integration + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + build_and_lint: + name: Build & Lint Check + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + + uses: actions/checkout@v7 + + - name: Install pnpm + uses: pnpm/action-setup@v6 + with: + version: 9.15.0 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: pnpm + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Run Linter + run: pnpm run lint + + - name: Compile and Build Webview + run: pnpm run compile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d2917f6..a49ce7d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,76 +1,76 @@ -name: Release - -on: - push: - tags: - - 'v*' - -permissions: - contents: write - -jobs: - release: - name: Package & Release - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - - uses: actions/checkout@v7 - - - name: Install pnpm - uses: pnpm/action-setup@v6 - with: - version: 9.15.0 - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: 24 - cache: pnpm - - - name: Install Dependencies - run: pnpm install --frozen-lockfile - - - name: Compile and Build Webview - run: pnpm run compile - - - name: Package Extension - run: npx @vscode/vsce package --no-dependencies - - - name: Extract Release Notes from CHANGELOG.md - id: extract_notes - run: | - VERSION=${GITHUB_REF#refs/tags/v} - echo "Extracting release notes for version $VERSION" - - # Match lines starting from ## [VERSION] or ## VERSION until the next ## header - sed -n "/## \[\{0,1\}$VERSION\]\{0,1\}/,/## /p" CHANGELOG.md | grep -v '## ' > release_notes.md - - if [ ! -s release_notes.md ]; then - echo "No release notes found for version $VERSION in CHANGELOG.md. Using fallback." - echo "Release version $VERSION" > release_notes.md - fi - - - name: Create GitHub Release - uses: softprops/action-gh-release@v3 - with: - body_path: release_notes.md - files: | - *.vsix - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Publish to VS Code Marketplace - env: - VSCE_PAT: ${{ secrets.VSCE_PAT }} - run: | - if [ -n "$VSCE_PAT" ]; then - PACKAGE_FILE=$(ls *.vsix | head -n 1) - if [ -n "$PACKAGE_FILE" ]; then - npx @vscode/vsce publish --pat "$VSCE_PAT" --packagePath "$PACKAGE_FILE" --no-dependencies - else - echo "No VSIX package found; skipping Marketplace publish." - fi - else - echo "VSCE_PAT not set; skipping Marketplace publish." - fi +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + release: + name: Package & Release + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + + uses: actions/checkout@v7 + + - name: Install pnpm + uses: pnpm/action-setup@v6 + with: + version: 9.15.0 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24 + cache: pnpm + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Compile and Build Webview + run: pnpm run compile + + - name: Package Extension + run: npx @vscode/vsce package --no-dependencies + + - name: Extract Release Notes from CHANGELOG.md + id: extract_notes + run: | + VERSION=${GITHUB_REF#refs/tags/v} + echo "Extracting release notes for version $VERSION" + + # Match lines starting from ## [VERSION] or ## VERSION until the next ## header + sed -n "/## \[\{0,1\}$VERSION\]\{0,1\}/,/## /p" CHANGELOG.md | grep -v '## ' > release_notes.md + + if [ ! -s release_notes.md ]; then + echo "No release notes found for version $VERSION in CHANGELOG.md. Using fallback." + echo "Release version $VERSION" > release_notes.md + fi + + - name: Create GitHub Release + uses: softprops/action-gh-release@v3 + with: + body_path: release_notes.md + files: | + *.vsix + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish to VS Code Marketplace + env: + VSCE_PAT: ${{ secrets.VSCE_PAT }} + run: | + if [ -n "$VSCE_PAT" ]; then + PACKAGE_FILE=$(ls *.vsix | head -n 1) + if [ -n "$PACKAGE_FILE" ]; then + npx @vscode/vsce publish --pat "$VSCE_PAT" --packagePath "$PACKAGE_FILE" --no-dependencies + else + echo "No VSIX package found; skipping Marketplace publish." + fi + else + echo "VSCE_PAT not set; skipping Marketplace publish." + fi diff --git a/.gitignore b/.gitignore index 872d5f6..1bb1f9a 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,9 @@ web_modules/ .next out +# Runtime deps copied for VSIX packaging (generated by scripts/copy-deps.mjs) +deps/ + # Nuxt.js build / generate output .nuxt dist diff --git a/.vscodeignore b/.vscodeignore index 7d3e5c7..7b3481f 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,11 +1,36 @@ .vscode/** .vscode-test/** src/** +scripts/** .gitignore .yarnrc +.npmrc vsc-extension-quickstart.md **/tsconfig.json **/eslint.config.mjs -**/*.map -**/*.ts **/.vscode-test.* + +# Exclude ALL node_modules — runtime deps are in deps/ +node_modules/** + +# Strip unnecessary files from deps/ to keep the VSIX small +deps/**/*.map +deps/**/binding.gyp +deps/**/Makefile +deps/**/*.cc +deps/**/*.c +deps/**/*.h +deps/**/*.o +deps/**/*.node +deps/**/*.target.mk +deps/**/build/** +deps/**/prebuilds/** +deps/**/src/** +deps/**/.npmignore +deps/**/.eslintrc* +deps/**/.travis.yml +deps/**/test/** +deps/**/tests/** +deps/**/examples/** +deps/**/docs/** +deps/**/doc/** \ No newline at end of file diff --git a/codepulse-complexity-1.0.0.vsix b/codepulse-complexity-1.0.0.vsix deleted file mode 100644 index ca1d29f..0000000 Binary files a/codepulse-complexity-1.0.0.vsix and /dev/null differ diff --git a/codepulse-complexity-3.0.1.vsix b/codepulse-complexity-3.0.1.vsix new file mode 100644 index 0000000..82d5d6b Binary files /dev/null and b/codepulse-complexity-3.0.1.vsix differ diff --git a/esbuild.config.mjs b/esbuild.config.mjs new file mode 100644 index 0000000..68fa46d --- /dev/null +++ b/esbuild.config.mjs @@ -0,0 +1,103 @@ +/** + * @file esbuild.config.mjs + * + * @version 2.0.0 + * @author BleckWolf25 + * @license MIT + * + * @summary esbuild configuration for bundling the VS Code extension. + * + * @description + * Bundles the extension code, but keeps runtime dependencies (web-tree-sitter, + * tree-sitter languages, sql.js, jspdf) as external modules since they need + * their WASM files to be available at runtime. + * + * Runtime dependencies are shipped in the `deps/` directory (not node_modules) + * because pnpm's symlinked node_modules are incompatible with vsce packaging. + * The depsRewrite plugin rewrites bare imports of these packages to resolve + * from `../deps/` relative to the output file in `out/`. + * + * @since 09/07/2026 + * @updated 09/07/2026 + */ +// ---------- IMPORTS +import esbuild from 'esbuild'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +// ---------- CONFIGURATION +const isProduction = process.argv.includes('--production'); + +/** + * Runtime dependencies that live in deps/ instead of node_modules. + * These are marked external and their imports are rewritten to ../deps/. + */ +const RUNTIME_DEPS = [ + 'web-tree-sitter', + 'tree-sitter-c', + 'tree-sitter-cpp', + 'tree-sitter-go', + 'tree-sitter-java', + 'tree-sitter-javascript', + 'tree-sitter-python', + 'tree-sitter-rust', + 'tree-sitter-typescript', + 'sql.js', +]; + +/** + * esbuild plugin that rewrites bare imports of runtime dependencies + * to resolve from the deps/ directory instead of node_modules/. + * + * At build time, the source code uses normal bare imports (e.g. `import 'web-tree-sitter'`). + * This plugin intercepts those and resolves them to the actual node_modules location + * for type checking, but emits a relative path (../deps/) in the output bundle + * so that at runtime the packaged deps/ directory is used. + */ +const depsRewritePlugin = { + name: 'deps-rewrite', + setup(build) { + // Match any of the runtime dep package names or their subpaths + const filter = new RegExp(`^(${RUNTIME_DEPS.map(d => d.replace('.', '\\.')).join('|')})(/.*)?$`); + + build.onResolve({ filter }, (args) => { + // Resolve to the real node_modules path for correctness during build, + // but mark as external with a rewritten path for the output bundle. + // The output is in out/, so ../deps/ reaches the deps/ directory. + return { + path: `../deps/${args.path}`, + external: true, + }; + }); + }, +}; + +const config = { + entryPoints: ['src/extension.ts'], + bundle: true, + outfile: 'out/extension.cjs', + // vscode is the only truly external dependency (provided by the host) + external: ['vscode'], + plugins: [depsRewritePlugin], + format: 'cjs', + platform: 'node', + target: 'node24', + sourcemap: isProduction ? false : 'inline', + minify: isProduction, + logLevel: 'info', +}; + +// ---------- BUILD +async function build() { + console.log('[CodePulse] Building extension with esbuild...'); + await esbuild.build(config); + console.log('[CodePulse] Extension built successfully'); +} + +// ---------- ERROR HANDLING +build().catch((error) => { + console.error('[CodePulse] Build failed:', error); + process.exit(1); +}); \ No newline at end of file diff --git a/package.json b/package.json index 5100dee..0a1cb13 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "codepulse-complexity", "displayName": "CodePulseComplexity", "description": "Local code complexity tracking and analytics.", - "version": "1.0.0", + "version": "3.0.1", "icon": "icon.png", "author": "João Costa ", "publisher": "extbleckwolf25", @@ -27,7 +27,7 @@ "activationEvents": [ "onStartupFinished" ], - "main": "./out/extension.js", + "main": "./out/extension.cjs", "contributes": { "commands": [ { @@ -102,9 +102,10 @@ } }, "scripts": { - "vscode:prepublish": "pnpm run compile", - "compile": "tsc -p ./ && pnpm exec vite build --config ./src/webview/vite.config.ts", - "watch": "tsc -watch -p ./", + "vscode:prepublish": "pnpm run compile && pnpm install --shamefully-hoist && node scripts/copy-deps.mjs", + "compile": "node esbuild.config.mjs --production && pnpm exec vite build --config ./src/webview/vite.config.ts", + "watch": "node esbuild.config.mjs && pnpm exec vite build --config ./src/webview/vite.config.ts --watch", + "package": "pnpm exec vsce package --no-dependencies", "pretest": "pnpm run compile && pnpm run lint", "lint": "eslint src", "test": "vscode-test" @@ -118,6 +119,7 @@ "@vscode/test-cli": "^0.0.15", "@vscode/test-electron": "^3.0.0", "chart.js": "^4.5.1", + "esbuild": "^0.28.0", "eslint": "^10.6.0", "eslint-config-prettier": "^10.1.8", "prettier": "^3.9.4", @@ -126,9 +128,11 @@ "typescript-eslint": "^8.63.0", "vite": "^8.1.3" }, - "overrides": { - "diff": "^8.0.4", - "serialize-javascript": "^7.0.6" + "pnpm": { + "overrides": { + "diff": "^8.0.4", + "serialize-javascript": "^7.0.6" + } }, "dependencies": { "jspdf": "^4.2.1", @@ -143,4 +147,4 @@ "tree-sitter-typescript": "^0.23.2", "web-tree-sitter": "^0.26.10" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c92d464..3a1e135 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,10 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + diff: ^8.0.4 + serialize-javascript: ^7.0.6 + importers: .: @@ -47,13 +51,13 @@ importers: version: 10.0.1(eslint@10.6.0) '@sveltejs/vite-plugin-svelte': specifier: ^7.2.0 - version: 7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.3(@types/node@24.13.2)) + version: 7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)) '@types/mocha': specifier: ^10.0.10 version: 10.0.10 '@types/node': specifier: 24.x - version: 24.13.2 + version: 24.13.3 '@types/vscode': specifier: ^1.125.0 version: 1.125.0 @@ -66,6 +70,9 @@ importers: chart.js: specifier: ^4.5.1 version: 4.5.1 + esbuild: + specifier: ^0.28.0 + version: 0.28.1 eslint: specifier: ^10.6.0 version: 10.6.0 @@ -86,7 +93,7 @@ importers: version: 8.63.0(eslint@10.6.0)(typescript@6.0.3) vite: specifier: ^8.1.3 - version: 8.1.3(@types/node@24.13.2) + version: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1) packages: @@ -107,6 +114,162 @@ packages: '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -328,8 +491,8 @@ packages: '@types/mocha@10.0.10': resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} - '@types/node@24.13.2': - resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} + '@types/node@24.13.3': + resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} '@types/pako@2.0.4': resolution: {integrity: sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==} @@ -466,8 +629,8 @@ packages: resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} engines: {node: '>= 0.6.0'} - brace-expansion@2.1.1: - resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + brace-expansion@2.1.2: + resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} brace-expansion@5.0.7: resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} @@ -584,8 +747,8 @@ packages: devalue@5.8.1: resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} - diff@7.0.0: - resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} dompurify@3.4.11: @@ -607,6 +770,11 @@ packages: resolution: {integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==} engines: {node: '>=10.13.0'} + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -971,8 +1139,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.5.1: - resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} magic-string@0.30.21: @@ -1104,9 +1272,6 @@ packages: raf@3.4.1: resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -1141,16 +1306,14 @@ packages: safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true - serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + serialize-javascript@7.0.7: + resolution: {integrity: sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==} + engines: {node: '>=20.0.0'} setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -1365,8 +1528,8 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - vite@8.1.3: - resolution: {integrity: sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==} + vite@8.1.4: + resolution: {integrity: sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -1496,6 +1659,84 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.28.1': + optional: true + + '@esbuild/android-arm64@0.28.1': + optional: true + + '@esbuild/android-arm@0.28.1': + optional: true + + '@esbuild/android-x64@0.28.1': + optional: true + + '@esbuild/darwin-arm64@0.28.1': + optional: true + + '@esbuild/darwin-x64@0.28.1': + optional: true + + '@esbuild/freebsd-arm64@0.28.1': + optional: true + + '@esbuild/freebsd-x64@0.28.1': + optional: true + + '@esbuild/linux-arm64@0.28.1': + optional: true + + '@esbuild/linux-arm@0.28.1': + optional: true + + '@esbuild/linux-ia32@0.28.1': + optional: true + + '@esbuild/linux-loong64@0.28.1': + optional: true + + '@esbuild/linux-mips64el@0.28.1': + optional: true + + '@esbuild/linux-ppc64@0.28.1': + optional: true + + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true + + '@esbuild/win32-x64@0.28.1': + optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0)': dependencies: eslint: 10.6.0 @@ -1645,14 +1886,14 @@ snapshots: dependencies: acorn: 8.17.0 - '@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.3(@types/node@24.13.2))': + '@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1))': dependencies: deepmerge: 4.3.1 magic-string: 0.30.21 obug: 2.1.3 svelte: 5.56.4(@typescript-eslint/types@8.63.0) - vite: 8.1.3(@types/node@24.13.2) - vitefu: 1.1.3(vite@8.1.3(@types/node@24.13.2)) + vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1) + vitefu: 1.1.3(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)) '@tybys/wasm-util@0.10.3': dependencies: @@ -1669,7 +1910,7 @@ snapshots: '@types/mocha@10.0.10': {} - '@types/node@24.13.2': + '@types/node@24.13.3': dependencies: undici-types: 7.18.2 @@ -1835,7 +2076,7 @@ snapshots: base64-arraybuffer@1.0.2: optional: true - brace-expansion@2.1.1: + brace-expansion@2.1.2: dependencies: balanced-match: 1.0.2 @@ -1952,7 +2193,7 @@ snapshots: devalue@5.8.1: {} - diff@7.0.0: {} + diff@8.0.4: {} dompurify@3.4.11: optionalDependencies: @@ -1972,6 +2213,35 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} @@ -2314,7 +2584,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.5.1: {} + lru-cache@11.5.2: {} magic-string@0.30.21: dependencies: @@ -2332,7 +2602,7 @@ snapshots: minimatch@9.0.9: dependencies: - brace-expansion: 2.1.1 + brace-expansion: 2.1.2 minipass@7.1.3: {} @@ -2341,7 +2611,7 @@ snapshots: browser-stdout: 1.3.1 chokidar: 4.0.3 debug: 4.4.3(supports-color@8.1.1) - diff: 7.0.0 + diff: 8.0.4 escape-string-regexp: 4.0.0 find-up: 5.0.0 glob: 10.5.0 @@ -2352,7 +2622,7 @@ snapshots: minimatch: 9.0.9 ms: 2.1.3 picocolors: 1.1.1 - serialize-javascript: 6.0.2 + serialize-javascript: 7.0.7 strip-json-comments: 3.1.1 supports-color: 8.1.1 workerpool: 9.3.4 @@ -2422,7 +2692,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.5.1 + lru-cache: 11.5.2 minipass: 7.1.3 performance-now@2.1.0: @@ -2451,10 +2721,6 @@ snapshots: performance-now: 2.1.0 optional: true - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -2505,13 +2771,9 @@ snapshots: safe-buffer@5.1.2: {} - safe-buffer@5.2.1: {} - semver@7.8.5: {} - serialize-javascript@6.0.2: - dependencies: - randombytes: 2.1.0 + serialize-javascript@7.0.7: {} setimmediate@1.0.5: {} @@ -2711,7 +2973,7 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - vite@8.1.3(@types/node@24.13.2): + vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1): dependencies: lightningcss: 1.32.0 picomatch: 4.0.5 @@ -2719,12 +2981,13 @@ snapshots: rolldown: 1.1.5 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 24.13.2 + '@types/node': 24.13.3 + esbuild: 0.28.1 fsevents: 2.3.3 - vitefu@1.1.3(vite@8.1.3(@types/node@24.13.2)): + vitefu@1.1.3(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)): optionalDependencies: - vite: 8.1.3(@types/node@24.13.2) + vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1) web-tree-sitter@0.26.10: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 9d5588c..64e85d2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -12,3 +12,4 @@ allowBuilds: tree-sitter-python: true tree-sitter-rust: true tree-sitter-typescript: true + web-tree-sitter: true diff --git a/scripts/copy-deps.mjs b/scripts/copy-deps.mjs new file mode 100644 index 0000000..42fc773 --- /dev/null +++ b/scripts/copy-deps.mjs @@ -0,0 +1,56 @@ +/** + * @file scripts/copy-deps.mjs + * + * @summary Copies runtime dependencies (dereferencing pnpm symlinks) into deps/ + * so that `vsce package` includes them in the VSIX. + * + * pnpm stores packages in a content-addressable store and symlinks them into + * node_modules/. `vsce package` ignores symlinked directories and the + * .vscodeignore negation pattern (e.g. !node_modules/x/**) does not work + * reliably with pnpm. This script copies the real files into a deps/ + * directory that vsce can include without issues. + */ +import { cpSync, rmSync, mkdirSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const root = resolve(__dirname, '..'); +const nodeModules = resolve(root, 'node_modules'); +const depsDir = resolve(root, 'deps'); + +/** Runtime packages that must ship inside the VSIX. */ +const RUNTIME_DEPS = [ + 'web-tree-sitter', + 'tree-sitter-c', + 'tree-sitter-cpp', + 'tree-sitter-go', + 'tree-sitter-java', + 'tree-sitter-javascript', + 'tree-sitter-python', + 'tree-sitter-rust', + 'tree-sitter-typescript', + 'sql.js', + 'jspdf', +]; + +// Clean and recreate deps/ +rmSync(depsDir, { recursive: true, force: true }); +mkdirSync(depsDir, { recursive: true }); + +console.log('[copy-deps] Copying runtime dependencies to deps/ ...'); + +for (const pkg of RUNTIME_DEPS) { + const src = resolve(nodeModules, pkg); + const dest = resolve(depsDir, pkg); + try { + // dereference: true follows pnpm symlinks and copies real files + cpSync(src, dest, { recursive: true, dereference: true }); + console.log(` ✓ ${pkg}`); + } catch (err) { + console.error(` ✗ ${pkg}: ${err.message}`); + process.exit(1); + } +} + +console.log('[copy-deps] Done.'); diff --git a/src/extension.ts b/src/extension.ts index 60a8ef7..5c32460 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -193,43 +193,53 @@ async function getStorageDir(context: vscode.ExtensionContext): Promise return dbPath; } -// ---------- EXTENSION LIFECYCLE -export async function activate(context: vscode.ExtensionContext) { - console.log('[CodePulse] Activating extension...'); +// ---------- INITIALIZATION HELPER +let initPromise: Promise | undefined; - // 1. Resolve storage directory - const storageDir = await getStorageDir(context); - console.log(`[CodePulse] Storage directory: ${storageDir}`); +async function ensureInitialized(context: vscode.ExtensionContext): Promise { + initPromise ??= (async () => { + const storageDir = await getStorageDir(context); + console.log(`[CodePulse] Storage directory: ${storageDir}`); - // 2. Initialise tree-sitter WASM runtime and language parsers - const tsManager = TreeSitterManager.getInstance(); - await tsManager.initialize(context); + const tsManager = TreeSitterManager.getInstance(); + await tsManager.initialize(context); - // 3. Initialise the SQLite database (load from disk or create) - const db = DatabaseManager.getInstance(); - await db.initialize(storageDir); + const db = DatabaseManager.getInstance(); + await db.initialize(storageDir); + })(); + return initPromise; +} - // 4. Start the event processor (listens to file save/change events) - const eventProcessor = new EventProcessor(); - eventProcessor.activate(); +// ---------- EXTENSION LIFECYCLE +export function activate(context: vscode.ExtensionContext) { + console.log('[CodePulse] Activating extension...'); - // 5. Register commands + // 1. Register commands synchronously FIRST so they are never missing context.subscriptions.push( - vscode.commands.registerCommand('codepulse.openDashboard', () => { - showDashboard(context); + vscode.commands.registerCommand('codepulse.openDashboard', async () => { + try { + await ensureInitialized(context); + showDashboard(context); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + void vscode.window.showErrorMessage(`[CodePulse] Failed to open dashboard: ${msg}`); + } }), - vscode.commands.registerCommand('codepulse.exportJson', () => { + vscode.commands.registerCommand('codepulse.exportJson', async () => { + await ensureInitialized(context); void exportJson(); }), - vscode.commands.registerCommand('codepulse.exportCsv', () => { + vscode.commands.registerCommand('codepulse.exportCsv', async () => { + await ensureInitialized(context); void exportCsv(); }), - vscode.commands.registerCommand('codepulse.purgeLogs', () => { + vscode.commands.registerCommand('codepulse.purgeLogs', async () => { + await ensureInitialized(context); void purgeLogs(); }), ); - // 6. Register CodeLens and Hover providers + // 2. Register CodeLens and Hover providers synchronously const selector: vscode.DocumentSelector = [ 'typescript', 'javascript', @@ -246,16 +256,23 @@ export async function activate(context: vscode.ExtensionContext) { vscode.languages.registerHoverProvider(selector, new CodePulseHoverProvider()), ); - // 7. Push dispose handlers for clean deactivation + // 3. Start background initialization and event processor + const eventProcessor = new EventProcessor(); context.subscriptions.push({ dispose(): void { eventProcessor.deactivate(); }, }); - console.log('[CodePulse] Extension activated successfully'); - - showDashboard(context); + void ensureInitialized(context) + .then(() => { + eventProcessor.activate(); + console.log('[CodePulse] Extension activated successfully'); + showDashboard(context); + }) + .catch((err: unknown) => { + console.error('[CodePulse] Extension initialization failed:', err); + }); } export async function deactivate(): Promise { diff --git a/src/parser/eventProcessor.ts b/src/parser/eventProcessor.ts index 3793570..36518c7 100644 --- a/src/parser/eventProcessor.ts +++ b/src/parser/eventProcessor.ts @@ -233,6 +233,9 @@ export class EventProcessor { for (const fileUri of fileUris) { try { + if (!fs.existsSync(fileUri.fsPath)) { + continue; + } const document = await vscode.workspace.openTextDocument(fileUri); if (!this._shouldSkip(document)) { this._processDocument(document, 'workspaceScan'); diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 48e882b..05c89c8 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -15,13 +15,15 @@ * @updated 08/07/2026 */ // ---------- IMPORTS -import * as TreeSitter from 'web-tree-sitter'; -import { Language } from 'web-tree-sitter'; +import type * as TreeSitterNamespace from 'web-tree-sitter'; import { copyWasmBinaries, getTreeSitterWasmPath } from './wasmHelper.js'; import { computeComplexity, type ComplexityScores } from './complexityAnalyzer.js'; import type { SupportedLanguage } from '../types.js'; import * as vscode from 'vscode'; +declare const require: (id: string) => unknown; +let treeSitterModule: typeof TreeSitterNamespace | undefined; + // ---------- LANGUAGE MAPPING /** * Maps a VS Code language identifier to our internal language name and @@ -45,7 +47,7 @@ const LANGUAGE_WASM_MAP: Record(); + private readonly _parsers = new Map(); private _initialized = false; private constructor() { @@ -70,9 +72,18 @@ export class TreeSitterManager { // 1. Copy WASM binaries to global storage const wasmPaths = await copyWasmBinaries(context); - // 2. Initialise the web-tree-sitter runtime + // 2. Lazy-load and initialise the web-tree-sitter runtime. + try { + treeSitterModule ??= require('web-tree-sitter/web-tree-sitter.cjs') as typeof TreeSitterNamespace; + } catch (err) { + const msg = + 'The dependency "web-tree-sitter" is not available. If you installed this extension from a VSIX or the Marketplace, ensure the publisher packaged runtime dependencies. For development, run the extension from the workspace (F5) after running `pnpm install`).'; + void vscode.window.showErrorMessage(`[CodePulse] ${msg}`); + throw err; + } + const tsWasmPath = getTreeSitterWasmPath(context); - await TreeSitter.Parser.init({ + await treeSitterModule.Parser.init({ locateFile(): string { return tsWasmPath; }, @@ -87,8 +98,8 @@ export class TreeSitterManager { } try { - const language = await Language.load(wasmPath); - const parser = new TreeSitter.Parser(); + const language = await treeSitterModule.Language.load(wasmPath); + const parser = new treeSitterModule.Parser(); parser.setLanguage(language); this._parsers.set(vscodeLang, parser); } catch (err) { diff --git a/src/parser/wasmHelper.ts b/src/parser/wasmHelper.ts index 765ee46..96ff512 100644 --- a/src/parser/wasmHelper.ts +++ b/src/parser/wasmHelper.ts @@ -101,9 +101,9 @@ export async function copyWasmBinaries( const wasmPaths = new Map(); for (const artifact of WASM_ARTIFACTS) { - // Source is inside the extension's node_modules + // Source is inside the extension's deps/ directory (runtime deps) const extensionPath = context.extensionUri.fsPath; - const sourcePath = path.join(extensionPath, 'node_modules', artifact.sourceRelPath); + const sourcePath = path.join(extensionPath, 'deps', artifact.sourceRelPath); const targetPath = path.join(wasmDir, artifact.targetFileName); try { @@ -132,7 +132,7 @@ export async function copyWasmBinaries( export function getTreeSitterWasmPath(context: vscode.ExtensionContext): string { return path.join( context.extensionUri.fsPath, - 'node_modules', + 'deps', 'web-tree-sitter', 'web-tree-sitter.wasm', );