Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 3 additions & 2 deletions .claude/skills/verify/net.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
resolveProvisionOptions,
} from "obsidian-e2e/runner";
import { FIXTURE_HOST } from "../../../scripts/e2e-instance.mjs";
import { jsLiteral } from "../../../scripts/js-literal.mjs";

const root = fileURLToPath(new URL("../../..", import.meta.url));
const config = await loadRunnerConfig(root);
Expand Down Expand Up @@ -64,7 +65,7 @@ const WATCH = `(() => {
}
const entries = [];
// Other hosts log only their origin, because a private feed's path can hold its token.
const pathHosts = new Set(${JSON.stringify(PATH_HOSTS)});
const pathHosts = new Set(${jsLiteral(PATH_HOSTS)});
const logged = (url) => {
try {
const parsed = new URL(url);
Expand All @@ -73,7 +74,7 @@ const WATCH = `(() => {
return "(unparsable URL)";
}
};
const text = ${JSON.stringify(FIXTURE_TRANSCRIPTION)};
const text = ${jsLiteral(FIXTURE_TRANSCRIPTION)};
const originalFetch = window.fetch;
window.fetch = async function (input, init) {
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
Expand Down
9 changes: 5 additions & 4 deletions .claude/skills/verify/screens/instance.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
resolveProvisionOptions,
} from "obsidian-e2e/runner";
import { pngSize } from "./sheet.mjs";
import { jsLiteral } from "../../../../scripts/js-literal.mjs";

/** @typedef {import("./scenes.mjs").Page} Page */

Expand Down Expand Up @@ -204,7 +205,7 @@ export async function theme(name) {
export async function go(page) {
await evaluate(`
const plugin = app.plugins.plugins.podnotes;
plugin.navigation.replace(${JSON.stringify(page)});
plugin.navigation.replace(${jsLiteral(page)});
await plugin.activateView();
return true;
`);
Expand Down Expand Up @@ -267,7 +268,7 @@ export async function episodeNote() {
*/
export async function press(text) {
const button = `[...document.querySelectorAll("section.pn-player button")].find((button) =>
button.getBoundingClientRect().width > 0 && button.innerText.trim() === ${JSON.stringify(text)})`;
button.getBoundingClientRect().width > 0 && button.innerText.trim() === ${jsLiteral(text)})`;
// "true" or "false" for a panel switch or tab.
const state = `((button) => !button ? "missing" :
button.getAttribute("aria-pressed") ?? button.getAttribute("aria-selected") ?? "action")(${button})`;
Expand Down Expand Up @@ -359,7 +360,7 @@ async function settle(text = "") {
const moving = document.getAnimations().filter(
(animation) => animation.playState === "running" && view.contains(animation.effect?.target),
).length;
const shown = view.innerText.includes(${JSON.stringify(text)});
const shown = view.innerText.includes(${jsLiteral(text)});
return { loading, moving, shown, markup: view.innerHTML.length };
`);
const ready = state.loading === 0 && state.moving === 0 && state.shown;
Expand Down Expand Up @@ -391,7 +392,7 @@ export async function shot(file, text, themeName, statusBar = false, notice = ""
document.querySelectorAll(".tooltip").forEach((tooltip) => tooltip.remove());
const notices = [...document.querySelectorAll(".notice")].map((notice) => {
const said = notice.innerText.trim();
if (!${JSON.stringify(notice)}) notice.remove();
if (!${jsLiteral(notice)}) notice.remove();
return said;
});
// Obsidian's status bar floats over the view's bottom-right corner.
Expand Down
5 changes: 3 additions & 2 deletions .claude/skills/verify/screens/scenes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import os from "node:os";
import path from "node:path";
import { FIXTURE_HOST } from "../../../../scripts/e2e-instance.mjs";
import { config, evaluate, FIXTURES, provision, waitFor } from "./instance.mjs";
import { jsLiteral } from "../../../../scripts/js-literal.mjs";

const DATA_PATH = path.join(provision.vaultPath, ".obsidian/plugins/podnotes/data.json");

Expand Down Expand Up @@ -382,14 +383,14 @@ async function localFiles(data) {
LOCAL_FILES.map(([file]) => [file, fs.statSync(path.join(provision.vaultPath, file)).size]),
);
await waitFor(
`const sizes = ${JSON.stringify(sizes)};
`const sizes = ${jsLiteral(sizes)};
return Object.entries(sizes).every(([file, size]) => app.vault.getAbstractFileByPath(file)?.stat.size === size);`,
"Obsidian did not index the local media files",
10000,
);
/** @type {{ file: string, title: string, url: string, streamUrl: string, ctime: number }[]} */
const files = await evaluate(`
return ${JSON.stringify(Object.keys(sizes))}.map((file) => {
return ${jsLiteral(Object.keys(sizes))}.map((file) => {
const found = app.vault.getAbstractFileByPath(file);
return { file, title: found.basename, url: app.fileManager.generateMarkdownLink(found, ""),
streamUrl: app.vault.getResourcePath(found), ctime: found.stat.ctime };
Expand Down
10 changes: 4 additions & 6 deletions .claude/skills/verify/ui.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
resolveInstanceOptions,
resolveProvisionOptions,
} from "obsidian-e2e/runner";
import { jsLiteral } from "../../../scripts/js-literal.mjs";

const root = fileURLToPath(new URL("../../..", import.meta.url));
const config = await loadRunnerConfig(root);
Expand Down Expand Up @@ -79,8 +80,8 @@ async function locate(css, text) {
const output = await obsidian([
"eval",
`code=(() => {
const text = ${JSON.stringify(text ?? "")};
const matches = [...document.querySelectorAll(${JSON.stringify(css)})].filter((el) => {
const text = ${jsLiteral(text ?? "")};
const matches = [...document.querySelectorAll(${jsLiteral(css)})].filter((el) => {
const rect = el.getBoundingClientRect();
return rect.width > 0 && rect.height > 0 && (text === "" ||
(${exact} ? el.innerText.trim() === text : el.textContent.includes(text)));
Expand Down Expand Up @@ -177,10 +178,7 @@ async function openUri(uri) {
split === -1 ? "true" : decodeURIComponent(pair.slice(split + 1));
}
data.action = action.replace(/\/+$/, "");
const output = await obsidian([
"eval",
`code=window.OBS_ACT(${JSON.stringify(data)}), "delivered"`,
]);
const output = await obsidian(["eval", `code=window.OBS_ACT(${jsLiteral(data)}), "delivered"`]);
if (!output.endsWith("=> delivered")) throw new Error(output);
}

Expand Down
3 changes: 0 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ updates:
- dependency-name: "typescript"
update-types:
- "version-update:semver-major"
# patches/svelte-check.mjs patches exactly this version and fails on any other.
# Upgrade it by hand to a release that includes the fix, and delete the patch.
- dependency-name: "svelte-check"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22.23.2
22.23.4
18 changes: 6 additions & 12 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,13 @@ root symlinks for local Obsidian loading.
- `npm run typecheck`: run TypeScript 7's native `tsc --noEmit` for source and test projects.
- `npm run lint`: run oxlint against TypeScript sources and tooling scripts (includes the obsidianmd guideline rules and the `svelte-policy` internals guard via jsPlugins), then the `svelte-policy` component checker, configured by `svelte-policy.json`.
- `npm run format:check`: run the configured oxfmt check (`npm run format` to write).
- `npm run check:a11y`: patch svelte-check if needed, run `lint:tsconfig`, then run `svelte-check --tsgo-experimental-api --fail-on-warnings` for source and test projects.
- `npm run check:a11y`: run `lint:tsconfig`, then run `svelte-check --tsgo-experimental-api --fail-on-warnings` for source and test projects.
- `npm run lint:tsconfig`: fail with TS18003 when `tsconfig.json` or `tsconfig.test.json` matches no files, using the native compiler without type checking.
- `npm run test`: run Svelte checks and the application Vitest suite.
- `npm run test:unit`: run the application Vitest suite without the Svelte checks.
- `npm run test:tooling`: run the real-process Svelte checker failure tests and the build's syntax floor test, including rejection of tagged templates that disable Obsidian's eval cache. CI runs these separately on every PR.
- `npm run build`: type-check and produce the production plugin bundle.
- `npm run docs:build`: build the Starlight documentation and check its internal links.
- `npm run docs:deploy`: build docs and deploy `docs/dist` to Cloudflare Pages.

`@typescript/native` aliases TypeScript 7. `typecheck` and `lint:tsconfig` call
its compiler by path because npm can link the shared `tsc` command to either
Expand All @@ -53,16 +52,11 @@ compiler's API (`--tsgo-experimental-api`), but it still loads the `typescript`
package, as do the TypeScript ESLint packages loaded by oxlint's Obsidian rules.
Keep `typescript` on version 6.

svelte-check 4.7.6 exits 0 when its check throws, for example when the native
compiler cannot start. `patches/svelte-check.mjs` applies the upstream fix,
[sveltejs/language-tools#3133](https://github.com/sveltejs/language-tools/pull/3133),
to exactly that version. It runs on `postinstall` and again at the start of
`check:a11y`, so an install with `--ignore-scripts` is patched before the check
runs. It fails on any other svelte-check version or an unrecognized file, so
svelte-check is pinned and Dependabot skips it. Once a svelte-check release
includes the fix, upgrade to it and delete the script, its two calls and the
`patches` lint/format paths in `package.json`, and the Dependabot ignore. The native API path also passes when
a tsconfig matches no files, which `lint:tsconfig` catches.
svelte-check exits non-zero when its check throws, for example when the native
compiler cannot start, since 4.7.7 includes
[sveltejs/language-tools#3133](https://github.com/sveltejs/language-tools/pull/3133).
The native API path still passes when a tsconfig matches no files, which
`lint:tsconfig` catches.
`tests/tooling/svelteCheck.test.ts` runs the real `check:a11y` in temporary
workspaces and requires it to fail on each of these cases.

Expand Down
3 changes: 1 addition & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,5 @@ Cloudflare Pages builds through its GitHub integration with `docs/` as the root
directory. It installs this package's dependencies, runs the dashboard build
command (which ends in `npm run docs:build`, an alias of `npm run build` here),
and publishes `dist`, set by `pages_build_output_dir` in `wrangler.jsonc`.
`npm run docs:deploy` from the repo root builds and deploys with Wrangler
instead. The `Docs` check in the Documentation GitHub workflow builds the docs
The `Docs` check in the Documentation GitHub workflow builds the docs
on every pull request and push to master.
Loading
Loading