Migrate to webpack

This commit is contained in:
Andras Schmelczer 2025-01-05 15:30:38 +00:00
parent 4d59ec927c
commit 02486d671e
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
8 changed files with 1773 additions and 370 deletions

View file

@ -51,6 +51,11 @@ jobs:
cd plugin
npm install
npm run lint
if [[ -n $(git status --porcelain) ]]; then
git status --porcelain
echo "Failing CI because the working directory is not clean after linting."
exit 1
fi
- name: Test frontend
run: |

2
.gitignore vendored
View file

@ -8,7 +8,7 @@ node_modules
backend/target
# Obsidian plugin build folder
plugin/build
plugin/dist
backend/db.sqlite3*
backend/config.yml

View file

@ -1,4 +1,4 @@
{
"jest.jestCommandLine": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest",
"jest.jestCommandLine": "npx jest",
"jest.rootPath": "plugin"
}

View file

@ -1,169 +0,0 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
import { sassPlugin } from "esbuild-sass-plugin";
import path from "node:path";
import fs from "node:fs";
import { wasmPack } from "esbuild-plugin-wasm-pack";
const prod = process.argv[2] === "production";
async function copyFiles(sourceDir, destinationDir) {
try {
await fs.promises.mkdir(destinationDir, { recursive: true });
const paths = Array.isArray(sourceDir)
? sourceDir
: (await fs.promises.readdir(sourceDir)).map((file) =>
path.join(sourceDir, file)
);
await Promise.all(
paths.map(async (sourcePath) => {
const stat = await fs.promises.stat(sourcePath);
if (stat.isFile()) {
const destinationFile = path.join(
destinationDir,
path.basename(sourcePath)
);
await fs.promises.copyFile(sourcePath, destinationFile);
console.debug(`Copied ${sourcePath} to ${destinationFile}`);
} else {
console.info(`Skipping directory ${sourcePath}`);
}
})
);
console.info("All files copied successfully.");
} catch (err) {
console.error("Error copying files:", err);
}
}
let wasmPlugin = {
name: "wasm",
setup(build) {
// Resolve ".wasm" files to a path with a namespace
build.onResolve({ filter: /\.wasm$/ }, (args) => {
if (args.resolveDir === "") {
return; // Ignore unresolvable paths
}
return {
path: path.isAbsolute(args.path)
? args.path
: path.join(args.resolveDir, args.path),
namespace: "wasm-binary",
};
});
// Virtual modules in the "wasm-binary" namespace contain the
// actual bytes of the WebAssembly file. This uses esbuild's
// built-in "binary" loader instead of manually embedding the
// binary data inside JavaScript code ourselves.
build.onLoad(
{ filter: /.*/, namespace: "wasm-binary" },
async (args) => ({
contents: await fs.promises.readFile(args.path),
loader: "binary",
})
);
},
};
const copyBundle = () => ({
name: "post-compile",
setup(build) {
build.onEnd((result) => {
if (prod) {
fs.promises.copyFile("manifest.json", "build/manifest.json");
return;
}
if (result.errors.length === 0) {
copyFiles(
["manifest.json", ".hotreload"],
"/mnt/c/Users/Andras/Desktop/test/test/.obsidian/plugins/my-plugin"
);
copyFiles(
"build",
"/mnt/c/Users/Andras/Desktop/test/test/.obsidian/plugins/my-plugin"
);
copyFiles(
["manifest.json", ".hotreload"],
"/mnt/c/Users/Andras/Desktop/test/test2/.obsidian/plugins/my-plugin"
);
copyFiles(
"build",
"/mnt/c/Users/Andras/Desktop/test/test2/.obsidian/plugins/my-plugin"
);
copyFiles(
["manifest.json", ".hotreload"],
"/home/andras/obsidian-test/.obsidian/plugins/my-plugin"
);
copyFiles(
"build",
"/home/andras/obsidian-test/.obsidian/plugins/my-plugin"
);
}
});
},
});
const cssContext = await esbuild.context({
entryPoints: ["src/styles.scss"],
bundle: true,
outfile: "build/styles.css",
plugins: [sassPlugin(), copyBundle()],
});
const jsContext = await esbuild.context({
entryPoints: ["src/vault-link-plugin.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins,
],
format: "cjs",
target: "es2020",
logLevel: "info",
resolveExtensions: [".ts"],
sourcemap: prod ? false : "inline",
treeShaking: false,
outfile: "build/main.js",
minify: prod,
plugins: [
wasmPlugin,
true
? null
: wasmPack({
target: "web",
path: "../backend/sync_lib",
}),
copyBundle(),
].filter(Boolean),
});
if (prod) {
await Promise.all([cssContext.rebuild(), jsContext.rebuild()]);
process.exit(0);
} else {
await Promise.all([cssContext.watch(), jsContext.watch()]);
}

1825
plugin/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -4,19 +4,27 @@
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"dev": "webpack watch --mode development",
"build": "webpack --mode production",
"test": "jest",
"lint": "eslint --fix src",
"lint": "eslint --fix src && prettier --write \"src/**/*.(ts|scss|json|html)\"",
"version": "node version-bump.mjs"
},
"keywords": [],
"author": "",
"type": "commonjs",
"license": "MIT",
"prettier": {
"trailingComma": "none",
"tabWidth": 2,
"useTabs": false,
"endOfLine": "lf"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node": "^16.11.6",
"builtin-modules": "3.3.0",
"css-loader": "^7.1.2",
"date-fns": "^4.1.0",
"dayjs": "^1.11.13",
"esbuild": "0.24.0",
@ -25,14 +33,25 @@
"eslint": "9.17.0",
"eslint-plugin-unused-imports": "^4.1.4",
"fetch-retry": "^6.0.0",
"file-loader": "^6.2.0",
"fs-extra": "^11.2.0",
"jest": "^29.7.0",
"mini-css-extract-plugin": "^2.9.2",
"obsidian": "1.7.2",
"openapi-fetch": "0.13.3",
"openapi-typescript": "7.4.4",
"p-queue": "^8.0.1",
"prettier": "^3.4.2",
"resolve-url-loader": "^5.0.0",
"sass-loader": "^16.0.4",
"sync_lib": "file:../backend/sync_lib/pkg",
"terser-webpack-plugin": "^5.3.11",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.1",
"tslib": "2.4.0",
"typescript": "5.7.2",
"typescript-eslint": "8.18.0"
"typescript-eslint": "8.18.0",
"webpack": "^5.97.1",
"webpack-cli": "^6.0.1"
}
}

View file

@ -7,7 +7,7 @@
"target": "ES6",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"moduleResolution": "bundler",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,

109
plugin/webpack.config.js Normal file
View file

@ -0,0 +1,109 @@
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const fs = require("fs-extra");
module.exports = (env, argv) => ({
devtool: argv.mode === "development" ? "inline-source-map" : false,
entry: {
index: "./src/vault-link-plugin.ts"
},
watchOptions: {
ignored: "**/node_modules"
},
externals: {
obsidian: "commonjs obsidian"
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
module: true
}
})
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "styles.css"
}),
new (require("webpack").DefinePlugin)({
__CURRENT_DATE__: Date.now()
}),
{
apply: (compiler) => {
if (argv.mode !== "development") {
return;
}
compiler.hooks.done.tap("Copy Files Plugin", (stats) => {
const source = path.resolve(__dirname, "dist");
const destinations = [
"/mnt/c/Users/Andras/Desktop/test/test/.obsidian/plugins/my-plugin",
"/mnt/c/Users/Andras/Desktop/test/test2/.obsidian/plugins/my-plugin",
"/home/andras/obsidian-test/.obsidian/plugins/my-plugin"
];
destinations.forEach((destination) => {
fs.copy(source, destination)
.then(() => console.log("Files copied successfully after build!"))
.catch((err) => console.error("Error copying files:", err));
fs.createFile(path.join(destination, ".hotreload"));
});
});
}
}
],
module: {
rules: [
{
test: /\.json$/i,
type: "asset/resource",
generator: {
filename: "[name][ext]"
}
},
{
test: /\.scss$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"resolve-url-loader",
{
loader: "sass-loader",
options: {
sourceMap: true // required by resolve-url-loader
}
}
]
},
{
test: /\.ts$/,
use: ["ts-loader"]
},
{
test: /\.wasm$/,
type: "asset/inline"
}
]
},
resolve: {
extensions: [
".ts",
".js" // required for development
],
alias: {
root: __dirname,
src: path.resolve(__dirname, "src")
}
},
output: {
clean: true,
filename: "main.js",
library: {
type: "commonjs" // required for Obsidian
},
path: path.resolve(__dirname, "dist"),
publicPath: ""
}
});