Skip to content

Commit

Permalink
Foam as Web Extension (#1395)
Browse files Browse the repository at this point in the history
See #1290 for context.
Major thanks to @pderaaij that did all the hard work here.

* using js-sha1 instead of node's crypto to compute sha1
* Using esbuild to bundle native and web extension (WIP)
* Added message regarding unsupported embeds in web extension
* support for graph webview in web extension
  • Loading branch information
riccardoferretti authored Sep 17, 2024
1 parent cd9ee4d commit dde11f8
Show file tree
Hide file tree
Showing 23 changed files with 207 additions and 58 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules
.DS_Store
.vscode-test/
.vscode-test-web/
*.tsbuildinfo
*.vsix
*.log
Expand Down
1 change: 1 addition & 0 deletions packages/foam-vscode/.vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ out/**/*.spec.*
test-data/**
src/**
jest.config.js
esbuild.js
.test-workspace
.gitignore
vsc-extension-quickstart.md
Expand Down
107 changes: 107 additions & 0 deletions packages/foam-vscode/esbuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// also see https://code.visualstudio.com/api/working-with-extensions/bundling-extension
const assert = require('assert');
const esbuild = require('esbuild');

// pass the platform to esbuild as an argument

function getPlatform() {
const args = process.argv.slice(2);
const pArg = args.find(arg => arg.startsWith('--platform='));
if (pArg) {
return pArg.split('=')[1];
}
throw new Error('No platform specified. Pass --platform <web|node>.');
}

const platform = getPlatform();
assert(['web', 'node'].includes(platform), 'Platform must be "web" or "node".');

const production = process.argv.includes('--production');
const watch = process.argv.includes('--watch');

const config = {
web: {
platform: 'browser',
format: 'cjs',
outfile: `out/bundles/extension-web.js`,
plugins: [
{
name: 'path-browserify',
setup(build) {
build.onResolve({ filter: /^path$/ }, args => {
return { path: require.resolve('path-browserify') };
});
},
},
{
name: 'wikilink-embed',
setup(build) {
build.onResolve({ filter: /wikilink-embed/ }, args => {
return {
path: require.resolve(
args.resolveDir + '/wikilink-embed-web-extension.ts'
),
};
});
},
},
],
},
node: {
platform: 'node',
format: 'cjs',
outfile: `out/bundles/extension-node.js`,
plugins: [],
},
};

async function main() {
const ctx = await esbuild.context({
...config[platform],
entryPoints: ['src/extension.ts'],
bundle: true,
minify: production,
sourcemap: !production,
sourcesContent: false,
external: ['vscode'],
logLevel: 'silent',
plugins: [
...config[platform].plugins,
/* add to the end of plugins array */
esbuildProblemMatcherPlugin,
],
});
if (watch) {
await ctx.watch();
} else {
await ctx.rebuild();
await ctx.dispose();
}
}

/**
* @type {import('esbuild').Plugin}
*/
const esbuildProblemMatcherPlugin = {
name: 'esbuild-problem-matcher',

setup(build) {
build.onStart(() => {
console.log('[watch] build started');
});
build.onEnd(result => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`);
console.error(
` ${location.file}:${location.line}:${location.column}:`
);
});
console.log('[watch] build finished');
});
},
};

main().catch(e => {
console.error(e);
process.exit(1);
});
25 changes: 15 additions & 10 deletions packages/foam-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"activationEvents": [
"workspaceContains:.vscode/foam.json"
],
"main": "./out/extension.js",
"main": "./out/bundles/extension-node.js",
"browser": "./out/bundles/extension-web.js",
"capabilities": {
"untrustedWorkspaces": {
"supported": "limited",
Expand Down Expand Up @@ -657,21 +658,23 @@
]
},
"scripts": {
"build": "tsc -p ./",
"pretest": "yarn build",
"test": "node ./out/test/run-tests.js",
"pretest:unit": "yarn build",
"test:unit": "node ./out/test/run-tests.js --unit",
"pretest:e2e": "yarn build",
"test:e2e": "node ./out/test/run-tests.js --e2e",
"build:node": "node esbuild.js --platform=node",
"build:web": "node esbuild.js --platform=web",
"build": "yarn build:node && yarn build:web",
"vscode:prepublish": "yarn clean && yarn build:node --production && yarn build:web --production",
"compile": "tsc -p ./",
"test-reset-workspace": "rm -rf .test-workspace && mkdir .test-workspace && touch .test-workspace/.keep",
"test-setup": "yarn compile && yarn build && yarn test-reset-workspace",
"test": "yarn test-setup && node ./out/test/run-tests.js",
"test:unit": "yarn test-setup && node ./out/test/run-tests.js --unit",
"test:e2e": "yarn test-setup && node ./out/test/run-tests.js --e2e",
"lint": "dts lint src",
"clean": "rimraf out",
"watch": "tsc --build ./tsconfig.json --watch",
"vscode:start-debugging": "yarn clean && yarn watch",
"esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/extension.js --external:vscode --format=cjs --platform=node",
"vscode:prepublish": "yarn run esbuild-base -- --minify",
"package-extension": "npx vsce package --yarn",
"install-extension": "code --install-extension ./foam-vscode-$npm_package_version.vsix",
"open-in-browser": "vscode-test-web --quality=stable --browser=chromium --extensionDevelopmentPath=. ",
"publish-extension-openvsx": "npx ovsx publish foam-vscode-$npm_package_version.vsix -p $OPENVSX_TOKEN",
"publish-extension-vscode": "npx vsce publish --packagePath foam-vscode-$npm_package_version.vsix",
"publish-extension": "yarn publish-extension-vscode && yarn publish-extension-openvsx"
Expand Down Expand Up @@ -713,7 +716,9 @@
"gray-matter": "^4.0.2",
"lodash": "^4.17.21",
"lru-cache": "^7.14.1",
"js-sha1": "^0.7.0",
"markdown-it-regex": "^0.2.0",
"path-browserify": "^1.0.1",
"remark-frontmatter": "^2.0.0",
"remark-parse": "^8.0.2",
"remark-wiki-link": "^0.0.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('generateStdMdLink', () => {
'[first-document](first-document.md)',
];
expect(actual.length).toEqual(expected.length);
const _ = actual.map((LinkReplace, index) => {
actual.forEach((LinkReplace, index) => {
expect(LinkReplace.newText).toEqual(expected[index]);
});
});
Expand All @@ -64,7 +64,7 @@ describe('generateStdMdLink', () => {
.map(link => convertLinkFormat(link, 'wikilink', _workspace, note));
const expected: string[] = ['[[first-document|file]]'];
expect(actual.length).toEqual(expected.length);
const _ = actual.map((LinkReplace, index) => {
actual.forEach((LinkReplace, index) => {
expect(LinkReplace.newText).toEqual(expected[index]);
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/foam-vscode/src/core/model/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ResourceLink } from './note';
import { URI } from './uri';
import { FoamWorkspace } from './workspace';
import { IDisposable } from '../common/lifecycle';
import { Logger, withTiming } from '../utils/log';
import { Logger } from '../utils/log';
import { Emitter } from '../common/event';

export type Connection = {
Expand Down
1 change: 0 additions & 1 deletion packages/foam-vscode/src/core/model/location.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Range } from './range';
import { URI } from './uri';
import { ResourceLink } from './note';

/**
* Represents a location inside a resource, such as a line
Expand Down
1 change: 0 additions & 1 deletion packages/foam-vscode/src/core/model/uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ function encode(uri: URI, skipEncoding: boolean): string {
: encodeURIComponentMinimal;

let res = '';
// eslint-disable-next-line prefer-const
let { scheme, authority, path, query, fragment } = uri;
if (scheme) {
res += scheme;
Expand Down
4 changes: 2 additions & 2 deletions packages/foam-vscode/src/core/services/markdown-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,9 @@ export const getBlockFor = (
}
});

let nLines = startLine == -1 ? 1 : endLine - startLine;
let nLines = startLine === -1 ? 1 : endLine - startLine;
let block =
startLine == -1
startLine === -1
? lines[searchLine] ?? ''
: lines.slice(startLine, endLine).join('\n');

Expand Down
5 changes: 2 additions & 3 deletions packages/foam-vscode/src/core/utils/core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import crypto from 'crypto';
import sha1 from 'js-sha1';

export function isNotNull<T>(value: T | null): value is T {
return value != null;
Expand All @@ -20,5 +20,4 @@ export function isNumeric(value: string): boolean {
return /-?\d+$/.test(value);
}

export const hash = (text: string) =>
crypto.createHash('sha1').update(text).digest('hex');
export const hash = (text: string) => sha1.sha1(text);
1 change: 0 additions & 1 deletion packages/foam-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { VsCodeWatcher } from './services/watcher';
import { createMarkdownParser } from './core/services/markdown-parser';
import VsCodeBasedParserCache from './services/cache';
import { createMatcherAndDataStore } from './services/editor';
import { getFoamVsCodeConfig } from './services/config';

export async function activate(context: ExtensionContext) {
const logger = new VsCodeOutputLogger();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { CREATE_NOTE_COMMAND, createNote } from './create-note';
import { Location } from '../../core/model/location';
import { Range } from '../../core/model/range';
import { ResourceLink } from '../../core/model/note';
import { MarkdownResourceProvider } from '../../core/services/markdown-provider';
import { createMarkdownParser } from '../../core/services/markdown-parser';

describe('create-note command', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/foam-vscode/src/features/commands/create-note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export async function createNote(args: CreateNoteArgs, foam: Foam) {
const edit = MarkdownLink.createUpdateLinkEdit(args.sourceLink.data, {
target: identifier,
});
if (edit.newText != args.sourceLink.data.rawText) {
if (edit.newText !== args.sourceLink.data.rawText) {
const updateLink = new vscode.WorkspaceEdit();
const uri = toVsCodeUri(args.sourceLink.uri);
updateLink.replace(
Expand Down
3 changes: 0 additions & 3 deletions packages/foam-vscode/src/features/navigation-provider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import { FoamGraph } from '../core/model/graph';
import { commandAsURI } from '../utils/commands';
import { CREATE_NOTE_COMMAND } from './commands/create-note';
import { Location } from '../core/model/location';
import { URI } from '../core/model/uri';
import { Range } from '../core/model/range';
import { ResourceLink } from '../core/model/note';

describe('Document navigation', () => {
const parser = createMarkdownParser([]);
Expand Down
14 changes: 7 additions & 7 deletions packages/foam-vscode/src/features/panels/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export default async function activate(
treeDataProvider: provider,
showCollapseAll: true,
});
const baseTitle = treeView.title;

const updateTreeView = async () => {
provider.target = vscode.window.activeTextEditor
Expand All @@ -53,12 +52,7 @@ export default async function activate(
}

export class ConnectionsTreeDataProvider extends BaseTreeProvider<vscode.TreeItem> {
public show = new ContextMemento<'all links' | 'backlinks' | 'forward links'>(
this.state,
`foam-vscode.views.connections.show`,
'all links',
true
);
public show: ContextMemento<'all links' | 'backlinks' | 'forward links'>;
public target?: URI = undefined;
public nValues = 0;
private connectionItems: ResourceRangeTreeItem[] = [];
Expand All @@ -70,6 +64,12 @@ export class ConnectionsTreeDataProvider extends BaseTreeProvider<vscode.TreeIte
registerCommands = true // for testing. don't love it, but will do for now
) {
super();
this.show = new ContextMemento<'all links' | 'backlinks' | 'forward links'>(
this.state,
`foam-vscode.views.connections.show`,
'all links',
true
);
if (!registerCommands) {
return;
}
Expand Down
28 changes: 16 additions & 12 deletions packages/foam-vscode/src/features/panels/dataviz.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as vscode from 'vscode';
import { TextDecoder } from 'util';
import { Foam } from '../../core/model/foam';
import { Logger } from '../../core/utils/log';
import { fromVsCodeUri } from '../../utils/vsc-utils';
Expand Down Expand Up @@ -167,28 +166,33 @@ async function getWebviewContent(
context: vscode.ExtensionContext,
panel: vscode.WebviewPanel
) {
const datavizPath = vscode.Uri.joinPath(
vscode.Uri.file(context.extensionPath),
const datavizUri = vscode.Uri.joinPath(
context.extensionUri,
'static',
'dataviz'
);

const getWebviewUri = (fileName: string) =>
panel.webview.asWebviewUri(vscode.Uri.joinPath(datavizPath, fileName));
panel.webview.asWebviewUri(vscode.Uri.joinPath(datavizUri, fileName));

const indexHtml = await vscode.workspace.fs.readFile(
vscode.Uri.joinPath(datavizPath, 'index.html')
);
const indexHtml =
vscode.env.uiKind === vscode.UIKind.Desktop
? new TextDecoder('utf-8').decode(
await vscode.workspace.fs.readFile(
vscode.Uri.joinPath(datavizUri, 'index.html')
)
)
: await fetch(getWebviewUri('index.html').toString()).then(r => r.text());

// Replace the script paths with the appropriate webview URI.
const filled = new TextDecoder('utf-8')
.decode(indexHtml)
.replace(/data-replace (src|href)="[^"]+"/g, match => {
const filled = indexHtml.replace(
/data-replace (src|href)="[^"]+"/g,
match => {
const i = match.indexOf(' ');
const j = match.indexOf('=');
const uri = getWebviewUri(match.slice(j + 2, -1).trim());
return match.slice(i + 1, j) + '="' + uri.toString() + '"';
});
}
);

return filled;
}
Expand Down
12 changes: 7 additions & 5 deletions packages/foam-vscode/src/features/panels/notes-explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,20 @@ export class NotesProvider extends FolderTreeProvider<
NotesTreeItems,
Resource
> {
public show = new ContextMemento<'all' | 'notes-only'>(
this.state,
`foam-vscode.views.notes-explorer.show`,
'all'
);
public show: ContextMemento<'all' | 'notes-only'>;

constructor(
private workspace: FoamWorkspace,
private graph: FoamGraph,
private state: vscode.Memento
) {
super();
this.show = new ContextMemento<'all' | 'notes-only'>(
this.state,
`foam-vscode.views.notes-explorer.show`,
'all'
);

this.disposables.push(
vscode.commands.registerCommand(
`foam-vscode.views.notes-explorer.show:all`,
Expand Down
Loading

0 comments on commit dde11f8

Please sign in to comment.