diff --git a/.gitignore b/.gitignore index b531b3f9e..5b6d95c6c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules .DS_Store .vscode-test/ +.vscode-test-web/ *.tsbuildinfo *.vsix *.log diff --git a/packages/foam-vscode/.vscodeignore b/packages/foam-vscode/.vscodeignore index 91aae824c..53a694e59 100644 --- a/packages/foam-vscode/.vscodeignore +++ b/packages/foam-vscode/.vscodeignore @@ -6,6 +6,7 @@ out/**/*.spec.* test-data/** src/** jest.config.js +esbuild.js .test-workspace .gitignore vsc-extension-quickstart.md diff --git a/packages/foam-vscode/esbuild.js b/packages/foam-vscode/esbuild.js new file mode 100644 index 000000000..61eb6cafe --- /dev/null +++ b/packages/foam-vscode/esbuild.js @@ -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 .'); +} + +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); +}); diff --git a/packages/foam-vscode/package.json b/packages/foam-vscode/package.json index b7d5f06bc..77dbb28b4 100644 --- a/packages/foam-vscode/package.json +++ b/packages/foam-vscode/package.json @@ -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", @@ -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" @@ -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", diff --git a/packages/foam-vscode/src/core/janitor/convert-links-format.test.ts b/packages/foam-vscode/src/core/janitor/convert-links-format.test.ts index b0c8b3eae..13b9b3044 100644 --- a/packages/foam-vscode/src/core/janitor/convert-links-format.test.ts +++ b/packages/foam-vscode/src/core/janitor/convert-links-format.test.ts @@ -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]); }); }); @@ -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]); }); }); diff --git a/packages/foam-vscode/src/core/model/graph.ts b/packages/foam-vscode/src/core/model/graph.ts index 49070a352..1e5860c0e 100644 --- a/packages/foam-vscode/src/core/model/graph.ts +++ b/packages/foam-vscode/src/core/model/graph.ts @@ -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 = { diff --git a/packages/foam-vscode/src/core/model/location.ts b/packages/foam-vscode/src/core/model/location.ts index 53d0819d7..3241d1756 100644 --- a/packages/foam-vscode/src/core/model/location.ts +++ b/packages/foam-vscode/src/core/model/location.ts @@ -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 diff --git a/packages/foam-vscode/src/core/model/uri.ts b/packages/foam-vscode/src/core/model/uri.ts index 6c2c0b040..16010d1c0 100644 --- a/packages/foam-vscode/src/core/model/uri.ts +++ b/packages/foam-vscode/src/core/model/uri.ts @@ -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; diff --git a/packages/foam-vscode/src/core/services/markdown-parser.ts b/packages/foam-vscode/src/core/services/markdown-parser.ts index bdec510c0..9f7da2b47 100644 --- a/packages/foam-vscode/src/core/services/markdown-parser.ts +++ b/packages/foam-vscode/src/core/services/markdown-parser.ts @@ -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'); diff --git a/packages/foam-vscode/src/core/utils/core.ts b/packages/foam-vscode/src/core/utils/core.ts index 85accb1c6..d56680a48 100644 --- a/packages/foam-vscode/src/core/utils/core.ts +++ b/packages/foam-vscode/src/core/utils/core.ts @@ -1,4 +1,4 @@ -import crypto from 'crypto'; +import sha1 from 'js-sha1'; export function isNotNull(value: T | null): value is T { return value != null; @@ -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); diff --git a/packages/foam-vscode/src/extension.ts b/packages/foam-vscode/src/extension.ts index 6d15df44a..f27bdf604 100644 --- a/packages/foam-vscode/src/extension.ts +++ b/packages/foam-vscode/src/extension.ts @@ -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(); diff --git a/packages/foam-vscode/src/features/commands/create-note.spec.ts b/packages/foam-vscode/src/features/commands/create-note.spec.ts index 4bbad0ee8..3c5fed0de 100644 --- a/packages/foam-vscode/src/features/commands/create-note.spec.ts +++ b/packages/foam-vscode/src/features/commands/create-note.spec.ts @@ -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', () => { diff --git a/packages/foam-vscode/src/features/commands/create-note.ts b/packages/foam-vscode/src/features/commands/create-note.ts index 633c8be44..abfd322e2 100644 --- a/packages/foam-vscode/src/features/commands/create-note.ts +++ b/packages/foam-vscode/src/features/commands/create-note.ts @@ -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( diff --git a/packages/foam-vscode/src/features/navigation-provider.spec.ts b/packages/foam-vscode/src/features/navigation-provider.spec.ts index 2e392e8da..c03a14b1c 100644 --- a/packages/foam-vscode/src/features/navigation-provider.spec.ts +++ b/packages/foam-vscode/src/features/navigation-provider.spec.ts @@ -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([]); diff --git a/packages/foam-vscode/src/features/panels/connections.ts b/packages/foam-vscode/src/features/panels/connections.ts index 159d91b85..f34c71b5c 100644 --- a/packages/foam-vscode/src/features/panels/connections.ts +++ b/packages/foam-vscode/src/features/panels/connections.ts @@ -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 @@ -53,12 +52,7 @@ export default async function activate( } export class ConnectionsTreeDataProvider extends BaseTreeProvider { - 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[] = []; @@ -70,6 +64,12 @@ export class ConnectionsTreeDataProvider extends BaseTreeProvider( + this.state, + `foam-vscode.views.connections.show`, + 'all links', + true + ); if (!registerCommands) { return; } diff --git a/packages/foam-vscode/src/features/panels/dataviz.ts b/packages/foam-vscode/src/features/panels/dataviz.ts index 0bd4dc15a..dff44b6e7 100644 --- a/packages/foam-vscode/src/features/panels/dataviz.ts +++ b/packages/foam-vscode/src/features/panels/dataviz.ts @@ -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'; @@ -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; } diff --git a/packages/foam-vscode/src/features/panels/notes-explorer.ts b/packages/foam-vscode/src/features/panels/notes-explorer.ts index 14adc35bc..094841a26 100644 --- a/packages/foam-vscode/src/features/panels/notes-explorer.ts +++ b/packages/foam-vscode/src/features/panels/notes-explorer.ts @@ -91,11 +91,7 @@ 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, @@ -103,6 +99,12 @@ export class NotesProvider extends FolderTreeProvider< 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`, diff --git a/packages/foam-vscode/src/features/panels/utils/grouped-resources-tree-data-provider.ts b/packages/foam-vscode/src/features/panels/utils/grouped-resources-tree-data-provider.ts index 54e04088e..7b06184d4 100644 --- a/packages/foam-vscode/src/features/panels/utils/grouped-resources-tree-data-provider.ts +++ b/packages/foam-vscode/src/features/panels/utils/grouped-resources-tree-data-provider.ts @@ -35,11 +35,7 @@ export abstract class GroupedResourcesTreeDataProvider extends FolderTreeProvide GroupedResourceTreeItem, URI > { - public groupBy = new ContextMemento<'off' | 'folder'>( - this.state, - `foam-vscode.views.${this.providerId}.group-by`, - 'folder' - ); + public groupBy: ContextMemento<'off' | 'folder'>; /** * Creates an instance of GroupedResourcesTreeDataProvider. @@ -61,6 +57,12 @@ export abstract class GroupedResourcesTreeDataProvider extends FolderTreeProvide private matcher: IMatcher ) { super(); + this.groupBy = new ContextMemento<'off' | 'folder'>( + this.state, + `foam-vscode.views.${this.providerId}.group-by`, + 'folder' + ); + this.disposables.push( vscode.commands.registerCommand( `foam-vscode.views.${this.providerId}.group-by:folder`, diff --git a/packages/foam-vscode/src/features/preview/wikilink-embed-web-extension.ts b/packages/foam-vscode/src/features/preview/wikilink-embed-web-extension.ts new file mode 100644 index 000000000..f5da415cc --- /dev/null +++ b/packages/foam-vscode/src/features/preview/wikilink-embed-web-extension.ts @@ -0,0 +1,27 @@ +/*global markdownit:readonly*/ + +import markdownItRegex from 'markdown-it-regex'; +import { ResourceParser } from '../../core/model/note'; +import { FoamWorkspace } from '../../core/model/workspace'; + +export const WIKILINK_EMBED_REGEX = + /((?:(?:full|content)-(?:inline|card)|full|content|inline|card)?!\[\[[^[\]]+?\]\])/; + +export const markdownItWikilinkEmbed = ( + md: markdownit, + workspace: FoamWorkspace, + parser: ResourceParser +) => { + return md.use(markdownItRegex, { + name: 'embed-wikilinks', + regex: WIKILINK_EMBED_REGEX, + replace: (wikilinkItem: string) => { + return ` +
+Embeds are not supported in web extension:
${wikilinkItem} +
`; + }, + }); +}; + +export default markdownItWikilinkEmbed; diff --git a/packages/foam-vscode/src/features/preview/wikilink-embed.ts b/packages/foam-vscode/src/features/preview/wikilink-embed.ts index dab4f953e..d8e554453 100644 --- a/packages/foam-vscode/src/features/preview/wikilink-embed.ts +++ b/packages/foam-vscode/src/features/preview/wikilink-embed.ts @@ -34,7 +34,7 @@ export const markdownItWikilinkEmbed = ( regex: WIKILINK_EMBED_REGEX, replace: (wikilinkItem: string) => { try { - const [_, noteEmbedModifier, wikilink] = wikilinkItem.match( + const [, noteEmbedModifier, wikilink] = wikilinkItem.match( WIKILINK_EMBED_REGEX_GROUPS ); diff --git a/packages/foam-vscode/src/services/editor.ts b/packages/foam-vscode/src/services/editor.ts index f8da83df2..5ff0ed19d 100644 --- a/packages/foam-vscode/src/services/editor.ts +++ b/packages/foam-vscode/src/services/editor.ts @@ -1,4 +1,3 @@ -import { TextEncoder } from 'util'; import { isEmpty } from 'lodash'; import { EndOfLine, diff --git a/packages/foam-vscode/src/services/templates.ts b/packages/foam-vscode/src/services/templates.ts index 5cec0024b..a590a1f1d 100644 --- a/packages/foam-vscode/src/services/templates.ts +++ b/packages/foam-vscode/src/services/templates.ts @@ -1,5 +1,4 @@ import { URI } from '../core/model/uri'; -import { TextEncoder } from 'util'; import { SnippetString, ViewColumn, diff --git a/yarn.lock b/yarn.lock index 5f1784644..6d6b56628 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7441,6 +7441,11 @@ js-sdsl@^4.1.4: resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== +js-sha1@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/js-sha1/-/js-sha1-0.7.0.tgz#fecaf5f36bb09a51b01da46b43a207c8452c9c1e" + integrity sha512-oQZ1Mo7440BfLSv9TX87VNEyU52pXPVG19F9PL3gTgNt0tVxlZ8F4O6yze3CLuLx28TxotxvlyepCNaaV0ZjMw== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -8777,6 +8782,11 @@ pascal-case@^3.1.2: no-case "^3.0.4" tslib "^2.0.3" +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"