Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion esbuild.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ const aliasPlugin = {
}
}
});

// For Node builds, use the /node subpath of @hpcc-js/observablehq-compiler
// which has a clean Node-compatible build without Rollup's broken require() stubs.
if (build.initialOptions.platform === "node") {
build.onResolve({ filter: /^@hpcc-js\/observablehq-compiler$/ }, () => {
return { path: path.resolve("node_modules/@hpcc-js/observablehq-compiler/dist/node/index.js") };
});
}
}
};

Expand Down Expand Up @@ -77,7 +85,7 @@ async function main(tsconfigRaw, entryPoint, platform, format, plugins = [], out
];

const external = platform === "node"
? ["vscode", "fs", "path", "os"]
? ["vscode"]
: ["vscode", ...npmExternals];

const ctx = await esbuild.context({
Expand Down
2,229 changes: 1,700 additions & 529 deletions package-lock.json

Large diffs are not rendered by default.

25 changes: 18 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
"watch": "run-p gen-types-watch build-ts-watch",
"lint": "eslint ./src",
"lint-fix": "npm run lint -- --fix",
"test": "run-s lint package",
"unit-test": "vitest run",
"integration-test": "tsc -p tests/integration/tsconfig.json && node out/tests/integration/runTest.js",
"test": "run-s lint build unit-test integration-test package",
"vscode:prepublish": "run-s clean build",
"publish": "vsce publish",
"package": "vsce package -o ./observable-js.vsix",
Expand All @@ -47,18 +49,20 @@
"update": "npx -y npm-check-updates -u -t minor"
},
"devDependencies": {
"@hpcc-js/esbuild-plugins": "1.5.0",
"@hpcc-js/esbuild-plugins": "1.8.7",
"@hpcc-js/observable-shim": "3.1.0",
"@hpcc-js/observablehq-compiler": "3.5.1",
"@hpcc-js/util": "3.3.10",
"@hpcc-js/observablehq-compiler": "3.7.9",
"@hpcc-js/util": "3.5.5",
"@observablehq/inspector": "5.0.1",
"@observablehq/notebook-kit": "1.4.1",
"@observablehq/notebook-kit": "1.5.2",
"@observablehq/runtime": "5.9.9",
"@observablehq/stdlib": "5.8.8",
"@types/mocha": "10.0.10",
"@types/node": "24.5.2",
"@types/vscode": "1.102.0",
"@types/vscode-notebook-renderer": "1.72.4",
"@vscode/extension-telemetry": "1.0.0",
"@vscode/test-electron": "2.5.2",
"@vscode/vsce": "3.6.0",
"acorn": "8.15.0",
"acorn-walk": "8.3.4",
Expand All @@ -71,20 +75,27 @@
"esbuild-plugin-umd-wrapper": "3.0.0",
"eslint": "9.35.0",
"eslint-plugin-react-hooks": "5.2.0",
"eslint-plugin-unused-imports": "^4.2.0",
"eslint-plugin-unused-imports": "4.2.0",
"globals": "16.4.0",
"jsdom": "26.1.0",
"mocha": "11.7.5",
"node-fetch": "3.3.2",
"npm-run-all": "4.1.5",
"os-browserify": "0.3.0",
"path-browserify": "1.0.1",
"rimraf": "6.0.1",
"serialize-javascript": "7.0.5",
"stream-browserify": "3.0.0",
"tslib": "2.8.1",
"typescript": "5.9.2",
"typescript-eslint": "8.44.0",
"uuid": "13.0.0",
"vitest": "^3.2.4"
"vite": "7.3.2",
"vitest": "4.1.3"
},
"overrides": {
"vite": "$vite",
"serialize-javascript": "$serialize-javascript"
},
"author": {
"name": "Gordon Smith"
Expand Down
3 changes: 2 additions & 1 deletion src/notebook-kit/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export const observable2vscode: Record<Cell["mode"], string> = {
"dot": "dot",
"node": "node",
"python": "python",
"ts": "typescript"
"ts": "typescript",
"r": "r"
};

export interface NotebookCell {
Expand Down
29 changes: 29 additions & 0 deletions tests/extension.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, it, expect } from "vitest";
import { existsSync, readFileSync, statSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const rootDir = resolve(__dirname, "..");
const distDir = resolve(rootDir, "dist");

describe("extension bundle", () => {
it("dist/extension.js exists", () => {
expect(existsSync(resolve(distDir, "extension.js"))).toBe(true);
});

it("dist/xhr-sync-worker.js exists", () => {
expect(existsSync(resolve(distDir, "xhr-sync-worker.js"))).toBe(true);
});

it("bundle has reasonable size", () => {
const stats = statSync(resolve(distDir, "extension.js"));
expect(stats.size).toBeGreaterThan(100_000);
});

it("bundle exports activate and deactivate", () => {
const src = readFileSync(resolve(distDir, "extension.js"), "utf-8");
expect(src).toContain("activate");
expect(src).toContain("deactivate");
});
});
23 changes: 23 additions & 0 deletions tests/integration/runTest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { resolve } from "node:path";
import { runTests } from "@vscode/test-electron";

async function main(): Promise<void> {
const extensionDevelopmentPath = resolve(__dirname, "..", "..", "..");
const extensionTestsPath = resolve(__dirname, "suite/index");
const workspacePath = resolve(extensionDevelopmentPath, "samples");

await runTests({
extensionDevelopmentPath,
extensionTestsPath,
launchArgs: [
workspacePath,
"--disable-extensions",
"--disable-gpu",
],
});
}

main().catch((err) => {
console.error("Failed to run tests:", err);
process.exit(1);
});
22 changes: 22 additions & 0 deletions tests/integration/suite/extension.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as assert from "node:assert/strict";
import * as vscode from "vscode";

const EXTENSION_ID = "GordonSmith.observable-js";

suite("Extension Integration", () => {

test("extension is installed", () => {
const ext = vscode.extensions.getExtension(EXTENSION_ID);
assert.ok(ext, `Extension ${EXTENSION_ID} should be installed`);
});

test("extension activates successfully", async function () {
const ext = vscode.extensions.getExtension(EXTENSION_ID);
assert.ok(ext, `Extension ${EXTENSION_ID} should be installed`);

if (!ext.isActive) {
await ext.activate();
}
assert.ok(ext.isActive, "Extension should be active after activation");
});
});
37 changes: 37 additions & 0 deletions tests/integration/suite/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { resolve } from "node:path";
import { globSync } from "node:fs";

// Patch globalThis.window BEFORE importing Mocha.
// The VS Code Extension Host (Electron renderer process) defines `document`
// but not `window`. Mocha 11's utils.js uses `typeof document` to detect
// browser vs Node and then accesses `window.location`, which crashes.
if (typeof globalThis.window === "undefined") {
(globalThis as Record<string, unknown>).window = {
location: (globalThis as Record<string, unknown>).location || { href: "" },
};
}

import Mocha from "mocha";

export function run(): Promise<void> {
const mocha = new Mocha({
ui: "tdd",
timeout: 30_000,
color: true,
});

const testFiles = globSync("*.test.js", { cwd: __dirname });
for (const f of testFiles) {
mocha.addFile(resolve(__dirname, f));
}

return new Promise((resolveP, reject) => {
mocha.run((failures) => {
if (failures > 0) {
reject(new Error(`${failures} test(s) failed.`));
} else {
resolveP();
}
});
});
}
15 changes: 15 additions & 0 deletions tests/integration/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "../../out/tests/integration",
"rootDir": ".",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": [
"**/*.ts"
]
}
7 changes: 7 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from "vitest/config";

export default defineConfig({
test: {
include: ["tests/**/*.spec.ts"],
},
});
Loading