Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
0.8.21
- Replace gulp with esbuild for build system
- Fix preview loading race conditions
- Add mermaid quadrantChart test case

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
  • Loading branch information
baryon and claude committed Jan 30, 2026
commit d823d10db181b93235b39730a89b70fa3a9e653d
41 changes: 19 additions & 22 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
// Check https://github.com/microsoft/vscode-test-adapter-converter for reference
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"group": "build",
"problemMatcher": "$esbuild-watch",
"isBackground": true,
"label": "npm: watch",
},
{
"type": "npm",
"script": "build",
"group": "build",
"problemMatcher": "$esbuild",
"label": "npm: build",
}
]
}
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"group": "build",
"problemMatcher": "$esbuild-watch",
"isBackground": true,
"label": "npm: watch"
},
{
"type": "npm",
"script": "build",
"group": "build",
"problemMatcher": "$esbuild",
"label": "npm: build"
}
]
}
73 changes: 46 additions & 27 deletions build.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
const { execSync } = require('child_process');
const { cpSync, existsSync, rmSync } = require('fs');
const path = require('path');
const { context, build } = require('esbuild');
const { polyfillNode } = require('esbuild-plugin-polyfill-node');

/**
* Copy crossnote assets
*/
function copyCrossnoteAssets() {
const crossnoteDir = path.resolve(__dirname, 'crossnote');
const sourceDir = path.resolve(__dirname, 'node_modules/crossnote/out');

if (existsSync(crossnoteDir)) {
rmSync(crossnoteDir, { recursive: true });
}

for (const subdir of ['dependencies', 'styles', 'webview']) {
cpSync(path.resolve(sourceDir, subdir), path.resolve(crossnoteDir, subdir), {
recursive: true,
});
}
console.log('[build] Copied crossnote assets');
}

/**
* Clean output directory
*/
function cleanOutput() {
const outDir = path.resolve(__dirname, 'out');
if (existsSync(outDir)) {
rmSync(outDir, { recursive: true });
}
console.log('[build] Cleaned output directory');
}

/**
* @type {import('esbuild').Plugin}
*/
Expand All @@ -11,10 +42,6 @@ const esbuildProblemMatcherPlugin = {
setup(build) {
build.onStart(() => {
console.log('[watch] build started');

// Run `gulp copy:files` before build
execSync('gulp copy-files');
console.log('[watch] gulp copy-files');
});
build.onEnd((result) => {
if (result.errors.length) {
Expand All @@ -37,17 +64,14 @@ const nativeConfig = {
entryPoints: ['./src/extension.ts'],
bundle: true,
minify: true,
platform: 'node', // For CJS
platform: 'node',
outfile: './out/native/extension.js',
target: 'node16',
format: 'cjs',
external: ['vscode'],
sourcemap: true,
};

// FIX:
const defaultDocument = {
readyState: 'ready',
};
const defaultWindow = {
document: {
currentScript: {
Expand All @@ -70,65 +94,60 @@ const webConfig = {
entryPoints: ['./src/extension-web.ts'],
bundle: true,
minify: true,
platform: 'browser', // For ESM
platform: 'browser',
outfile: './out/web/extension.js',
target: 'es2020',
format: 'cjs',
external: ['vscode'],
sourcemap: true,
plugins: [
polyfillNode({
polyfills: {
fs: true,
},
globals: {
// global: true,
},
}),
],
define: {
// eslint-disable-next-line @typescript-eslint/naming-convention
// window: 'globalThis',
// global: 'globalThis',
// window: "globalThis",
'window': JSON.stringify(defaultWindow),
// document: JSON.stringify(defaultDocument),
'process.env.IS_VSCODE_WEB_EXTENSION': '"true"',
},
};

async function main() {
// Copy assets and clean output
cleanOutput();
copyCrossnoteAssets();

try {
// Watch mode
if (process.argv.includes('--watch')) {
// Native
const nativeContext = await context({
...nativeConfig,
sourcemap: true,
minify: false,
plugins: [esbuildProblemMatcherPlugin, ...(nativeConfig.plugins ?? [])],
plugins: [esbuildProblemMatcherPlugin],
});

// Web
const webContext = await context({
...webConfig,
sourcemap: true,
minify: false,
define: {
...(webConfig.define ?? {}),
...{
'process.env.IS_VSCODE_WEB_EXTENSION_DEV_MODE': '"true"',
},
...webConfig.define,
'process.env.IS_VSCODE_WEB_EXTENSION_DEV_MODE': '"true"',
},
plugins: [esbuildProblemMatcherPlugin, ...(webConfig.plugins ?? [])],
plugins: [esbuildProblemMatcherPlugin, ...webConfig.plugins],
});

await Promise.all([nativeContext.watch(), webContext.watch()]);
} else {
// Build mode
await Promise.all([build(nativeConfig), build(webConfig)]);
console.log('[build] Build completed');
}
} catch (error) {
console.error(error);
process.exit(1);
}
}

Expand Down
39 changes: 0 additions & 39 deletions gulpfile.js

This file was deleted.

9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "markdown-preview-enhanced",
"displayName": "%displayName%",
"version": "0.8.20",
"version": "0.8.21",
"description": "%description%",
"categories": [
"Other"
Expand All @@ -24,7 +24,8 @@
"main": "./out/native/extension.js",
"browser": "./out/web/extension.js",
"scripts": {
"build": "gulp copy-files && gulp clean-out && node build.js",
"build": "node build.js",
"watch": "node build.js --watch",
"package": "vsce package --no-dependencies",
"package:pre": "yarn build && vsce package --no-dependencies --pre-release",
"check:all": "yarn check:eslint && yarn check:prettier",
Expand All @@ -37,8 +38,7 @@
"run-in-browser": "concurrently \"vscode-test-web --browserType=chromium --extensionDevelopmentPath=. $SERVE_DIR\" \"npx http-server ./crossnote -p 6789 --cors\"",
"run-in-vscode-dev": "npx serve --cors -l 5000 --ssl-cert $HOME/certs/localhost.pem --ssl-key $HOME/certs/localhost-key.pem",
"test": "yarn build && node ./node_modules/vscode/bin/test",
"vscode:prepublish": "yarn install && yarn build",
"watch": "gulp copy-files && gulp clean-out && node build.js --watch"
"vscode:prepublish": "yarn install && yarn build"
},
"contributes": {
"commands": [
Expand Down Expand Up @@ -722,7 +722,6 @@
"esbuild": "^0.25.0",
"esbuild-plugin-polyfill-node": "^0.3.0",
"eslint": "^8.48.0",
"gulp": "^4.0.2",
"http-server": "^14.1.1",
"husky": "^8.0.3",
"lint-staged": "^9.4.2",
Expand Down
10 changes: 10 additions & 0 deletions src/extension-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ export async function initExtensionCommon(context: vscode.ExtensionContext) {
const sourceUri = vscode.Uri.parse(uri);
const previewProvider = await getPreviewContentProvider(sourceUri);
notebooksManager.setSystemColorScheme(systemColorScheme);

// Validate that this sourceUri is still the active preview target
// This prevents stale webviewFinishLoading callbacks from updating wrong content
if (!previewProvider.shouldUpdateMarkdown(sourceUri)) {
console.debug(
`[MPE] Skipping webviewFinishLoading for stale sourceUri: ${sourceUri.fsPath}`,
);
return;
}

previewProvider.updateMarkdown(sourceUri);
}

Expand Down
32 changes: 32 additions & 0 deletions src/preview-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ export class PreviewProvider {
private renderRequestSeq = 0;
private latestRenderRequestBySourceUri: Map<string, number> = new Map();

/**
* Tracks the latest init request ID for each sourceUri.
* Used to validate webviewFinishLoading callbacks.
*/
private latestInitRequestBySourceUri: Map<string, number> = new Map();

/**
* Each PreviewProvider has a one notebook.
*/
Expand Down Expand Up @@ -277,6 +283,9 @@ export class PreviewProvider {
PreviewProvider.singlePreviewPanelSourceUriTarget = null;
this.previewToDocumentMap = new Map();
this.previewMaps = new Map();
// Clear all tracking maps in single preview mode
this.latestInitRequestBySourceUri.clear();
this.latestRenderRequestBySourceUri.clear();
} else {
const previews = this.getPreviews(sourceUri);
if (previews) {
Expand All @@ -285,6 +294,10 @@ export class PreviewProvider {
this.deletePreviewFromMap(sourceUri, preview);
});
}
// Clean up tracking for this specific sourceUri
const sourceUriString = sourceUri.toString();
this.latestInitRequestBySourceUri.delete(sourceUriString);
this.latestRenderRequestBySourceUri.delete(sourceUriString);
}
}

Expand Down Expand Up @@ -445,6 +458,8 @@ export class PreviewProvider {
try {
const initRequestId = ++this.initRequestSeq;
this.latestInitRequestByPreview.set(previewPanel, initRequestId);
// Track init request by sourceUri for webviewFinishLoading validation
this.latestInitRequestBySourceUri.set(sourceUri.toString(), initRequestId);

const html = await engine.generateHTMLTemplateForPreview({
inputString,
Expand Down Expand Up @@ -496,6 +511,9 @@ export class PreviewProvider {
// Clear all pending update timeouts
this.updateTimeouts.forEach((timeout) => clearTimeout(timeout));
this.updateTimeouts.clear();
// Clear all tracking maps
this.latestInitRequestBySourceUri.clear();
this.latestRenderRequestBySourceUri.clear();
// this.engineMaps = {};
PreviewProvider.singlePreviewPanel = null;
PreviewProvider.singlePreviewPanelSourceUriTarget = null;
Expand Down Expand Up @@ -544,6 +562,20 @@ export class PreviewProvider {
}
}

/**
* Check if updateMarkdown should proceed for the given sourceUri.
* Returns false if:
* - In SinglePreview mode and sourceUri is not the current target
* - Preview is not initialized or has been disposed
*/
public shouldUpdateMarkdown(sourceUri: Uri): boolean {
if (!this.isSinglePreviewTarget(sourceUri)) {
return false;
}
const previews = this.getPreviews(sourceUri);
return !!(previews && previews.length > 0);
}

public updateMarkdown(sourceUri: Uri, triggeredBySave?: boolean) {
if (!this.isSinglePreviewTarget(sourceUri)) {
return;
Expand Down
21 changes: 21 additions & 0 deletions test/markdown/diagrams.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@ graph TD;
C-->D;
```

---

`mermaid` quadrantChart (v10.2.0+)

```mermaid
quadrantChart
title Reach and engagement of campaigns
x-axis Low Reach --> High Reach
y-axis Low Engagement --> High Engagement
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
Campaign A: [0.3, 0.6]
Campaign B: [0.45, 0.23]
Campaign C: [0.57, 0.69]
Campaign D: [0.78, 0.34]
Campaign E: [0.40, 0.34]
Campaign F: [0.35, 0.78]
```

## [PlantUML](https://shd101wyy.github.io/markdown-preview-enhanced/#/diagrams?id=plantuml)

`puml`
Expand Down
Loading