Switch view to ESBuild
This switches the view code from Webpack to ESBuild to unify the build systems for the extension and view code. There are no changes in behavior, except that some features are now not supported (like dynamic require/import) and importing the `classnames` package fails. However, these were really easy to fix and don't hinder the further development of the view code, so I've just fixed those instances.
This commit is contained in:
@@ -8,9 +8,14 @@ import {
|
||||
copyWasmFiles,
|
||||
} from "./typescript";
|
||||
import { compileTextMateGrammar } from "./textmate";
|
||||
import { compileView, watchView } from "./webpack";
|
||||
import { packageExtension } from "./package";
|
||||
import { injectAppInsightsKey } from "./appInsights";
|
||||
import {
|
||||
checkViewTypeScript,
|
||||
compileViewEsbuild,
|
||||
watchViewCheckTypeScript,
|
||||
watchViewEsbuild,
|
||||
} from "./view";
|
||||
|
||||
export const buildWithoutPackage = series(
|
||||
cleanOutput,
|
||||
@@ -19,23 +24,30 @@ export const buildWithoutPackage = series(
|
||||
copyWasmFiles,
|
||||
checkTypeScript,
|
||||
compileTextMateGrammar,
|
||||
compileView,
|
||||
compileViewEsbuild,
|
||||
checkViewTypeScript,
|
||||
),
|
||||
);
|
||||
|
||||
export const watch = parallel(watchEsbuild, watchCheckTypeScript, watchView);
|
||||
export const watch = parallel(
|
||||
watchEsbuild,
|
||||
watchCheckTypeScript,
|
||||
watchViewEsbuild,
|
||||
watchViewCheckTypeScript,
|
||||
);
|
||||
|
||||
export {
|
||||
cleanOutput,
|
||||
compileTextMateGrammar,
|
||||
watchEsbuild,
|
||||
watchCheckTypeScript,
|
||||
watchView,
|
||||
watchViewEsbuild,
|
||||
compileEsbuild,
|
||||
copyWasmFiles,
|
||||
checkTypeScript,
|
||||
injectAppInsightsKey,
|
||||
compileView,
|
||||
compileViewEsbuild,
|
||||
checkViewTypeScript,
|
||||
};
|
||||
export default series(
|
||||
buildWithoutPackage,
|
||||
|
||||
@@ -4,7 +4,7 @@ import esbuild from "gulp-esbuild";
|
||||
import { createProject, reporter } from "gulp-typescript";
|
||||
import del from "del";
|
||||
|
||||
function goodReporter(): reporter.Reporter {
|
||||
export function goodReporter(): reporter.Reporter {
|
||||
return {
|
||||
error: (error, typescript) => {
|
||||
if (error.tsFile) {
|
||||
|
||||
40
extensions/ql-vscode/gulpfile.ts/view.ts
Normal file
40
extensions/ql-vscode/gulpfile.ts/view.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { dest, src, watch } from "gulp";
|
||||
import esbuild from "gulp-esbuild";
|
||||
import { createProject } from "gulp-typescript";
|
||||
import { goodReporter } from "./typescript";
|
||||
|
||||
const tsProject = createProject("src/view/tsconfig.json");
|
||||
|
||||
export function compileViewEsbuild() {
|
||||
return src("./src/view/webview.tsx")
|
||||
.pipe(
|
||||
esbuild({
|
||||
outfile: "webview.js",
|
||||
bundle: true,
|
||||
format: "iife",
|
||||
platform: "browser",
|
||||
target: "chrome114", // Electron 25, VS Code 1.85
|
||||
jsx: "automatic",
|
||||
sourcemap: "linked",
|
||||
sourceRoot: "..",
|
||||
loader: {
|
||||
".ttf": "file",
|
||||
},
|
||||
}),
|
||||
)
|
||||
.pipe(dest("out"));
|
||||
}
|
||||
|
||||
export function watchViewEsbuild() {
|
||||
watch("src/view/**/*.ts", compileViewEsbuild);
|
||||
}
|
||||
|
||||
export function checkViewTypeScript() {
|
||||
// This doesn't actually output the TypeScript files, it just
|
||||
// runs the TypeScript compiler and reports any errors.
|
||||
return tsProject.src().pipe(tsProject(goodReporter()));
|
||||
}
|
||||
|
||||
export function watchViewCheckTypeScript() {
|
||||
watch("src/view/**/*.ts", checkViewTypeScript);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import { resolve } from "path";
|
||||
import * as webpack from "webpack";
|
||||
import MiniCssExtractPlugin from "mini-css-extract-plugin";
|
||||
import { isDevBuild } from "./dev";
|
||||
|
||||
export const config: webpack.Configuration = {
|
||||
mode: isDevBuild ? "development" : "production",
|
||||
entry: {
|
||||
webview: "./src/view/webview.tsx",
|
||||
},
|
||||
output: {
|
||||
path: resolve(__dirname, "..", "out"),
|
||||
filename: "[name].js",
|
||||
},
|
||||
devtool: isDevBuild ? "inline-source-map" : "source-map",
|
||||
resolve: {
|
||||
extensions: [".js", ".ts", ".tsx", ".json"],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
loader: "ts-loader",
|
||||
options: {
|
||||
configFile: "src/view/tsconfig.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: "less-loader",
|
||||
options: {
|
||||
javascriptEnabled: true,
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(woff(2)?|ttf|eot)$/,
|
||||
type: "asset/resource",
|
||||
generator: {
|
||||
filename: "fonts/[hash][ext][query]",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
plugins: [new MiniCssExtractPlugin()],
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
import { Configuration, Stats, webpack } from "webpack";
|
||||
import { config } from "./webpack.config";
|
||||
|
||||
export function compileView(cb: (err?: Error) => void) {
|
||||
doWebpack(config, true, cb);
|
||||
}
|
||||
|
||||
export function watchView(cb: (err?: Error) => void) {
|
||||
const watchConfig = {
|
||||
...config,
|
||||
watch: true,
|
||||
watchOptions: {
|
||||
aggregateTimeout: 200,
|
||||
poll: 1000,
|
||||
},
|
||||
};
|
||||
doWebpack(watchConfig, false, cb);
|
||||
}
|
||||
|
||||
function doWebpack(
|
||||
internalConfig: Configuration,
|
||||
failOnError: boolean,
|
||||
cb: (err?: Error) => void,
|
||||
) {
|
||||
const resultCb = (error: Error | undefined, stats?: Stats) => {
|
||||
if (error) {
|
||||
cb(error);
|
||||
}
|
||||
if (stats) {
|
||||
console.log(
|
||||
stats.toString({
|
||||
errorDetails: true,
|
||||
colors: true,
|
||||
assets: false,
|
||||
builtAt: false,
|
||||
version: false,
|
||||
hash: false,
|
||||
entrypoints: false,
|
||||
timings: false,
|
||||
modules: false,
|
||||
errors: true,
|
||||
}),
|
||||
);
|
||||
if (stats.hasErrors()) {
|
||||
if (failOnError) {
|
||||
cb(new Error("Compilation errors detected."));
|
||||
return;
|
||||
} else {
|
||||
console.error("Compilation errors detected.");
|
||||
}
|
||||
}
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
webpack(internalConfig, resultCb);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { styled } from "styled-components";
|
||||
import * as classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
name: string;
|
||||
@@ -18,7 +17,7 @@ export const Codicon = ({ name, label, className, slot }: Props) => (
|
||||
role="img"
|
||||
aria-label={label}
|
||||
title={label}
|
||||
className={classNames("codicon", `codicon-${name}`, className)}
|
||||
className={`codicon codicon-${name}${className ? ` ${className}` : ""}`}
|
||||
slot={slot}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from "react";
|
||||
import { Codicon } from "./Codicon";
|
||||
import * as classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
label?: string;
|
||||
@@ -11,6 +10,6 @@ export const LoadingIcon = ({ label = "Loading...", className }: Props) => (
|
||||
<Codicon
|
||||
name="loading"
|
||||
label={label}
|
||||
className={classNames(className, "codicon-modifier-spin")}
|
||||
className={`${className ? `${className} ` : ""}codicon-modifier-spin`}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -3,11 +3,27 @@ import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { vscode } from "./vscode-api";
|
||||
|
||||
import { registerUnhandledErrorListener } from "./common/errors";
|
||||
import { WebviewDefinition } from "./webview-definition";
|
||||
|
||||
import compareView from "./compare";
|
||||
import dataFlowPathsView from "./data-flow-paths";
|
||||
import methodModelingView from "./method-modeling";
|
||||
import modelEditorView from "./model-editor";
|
||||
import resultsView from "./results";
|
||||
import variantAnalysisView from "./variant-analysis";
|
||||
|
||||
// Allow all views to use Codicons
|
||||
import "@vscode/codicons/dist/codicon.css";
|
||||
import { registerUnhandledErrorListener } from "./common/errors";
|
||||
|
||||
const views: Record<string, WebviewDefinition> = {
|
||||
compare: compareView,
|
||||
"data-flow-paths": dataFlowPathsView,
|
||||
"method-modeling": methodModelingView,
|
||||
"model-editor": modelEditorView,
|
||||
results: resultsView,
|
||||
"variant-analysis": variantAnalysisView,
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
registerUnhandledErrorListener();
|
||||
@@ -25,10 +41,11 @@ const render = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// It's a lot harder to use dynamic imports since those don't import the CSS
|
||||
// and require a less strict CSP policy
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires,import/no-dynamic-require
|
||||
const view: WebviewDefinition = require(`./${viewName}/index.tsx`).default;
|
||||
const view: WebviewDefinition = views[viewName];
|
||||
if (!view) {
|
||||
console.error(`Could not find view with name "${viewName}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
const root = createRoot(element);
|
||||
root.render(
|
||||
|
||||
Reference in New Issue
Block a user