Skip to content
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ squashfs-root/
.plans/
/audits/
dist-electron/
# Legacy annotation output from builds on older branches.
/apps/desktop/src/preview/AnnotationStyles.generated.ts
.electron-runtime/
.showcase/
apps/mobile/.showcase/
Expand Down
40 changes: 0 additions & 40 deletions apps/desktop/scripts/build-preview-annotation-css.mjs

This file was deleted.

106 changes: 106 additions & 0 deletions apps/desktop/scripts/preview-annotation-css.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// @effect-diagnostics nodeBuiltinImport:off - Bundler integration fixtures use Node filesystem APIs outside an Effect runtime.
import * as NodeFSP from "node:fs/promises";
import * as NodeModule from "node:module";
import * as NodeOS from "node:os";
import * as NodePath from "node:path";

import { build } from "vite-plus/pack";
import { expect, it } from "vite-plus/test";

import { previewAnnotationCssPlugin } from "./preview-annotation-css.ts";

it("embeds annotation CSS without generating a source file and removes legacy output", async () => {
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "annotation-css-"));
try {
const preview = NodePath.join(root, "src", "preview");
await NodeFSP.mkdir(preview, { recursive: true });
await NodeFSP.writeFile(NodePath.join(preview, "Annotation.css"), '@import "tailwindcss";\n');
await NodeFSP.writeFile(
NodePath.join(preview, "PickPreload.ts"),
'const className = "fixed";\n',
);
const legacyPath = NodePath.join(preview, "AnnotationStyles.generated.ts");
await NodeFSP.writeFile(legacyPath, "stale generated styles");
const entry = NodePath.join(root, "entry.ts");
await NodeFSP.writeFile(
entry,
'export { previewAnnotationStyles } from "virtual:preview-annotation-css";\n',
);
await build({
entry: [entry],
outDir: NodePath.join(root, "dist"),
format: "cjs",
dts: false,
config: false,
plugins: [previewAnnotationCssPlugin(root)],
});
const require = NodeModule.createRequire(import.meta.url);
const { previewAnnotationStyles } = require(NodePath.join(root, "dist", "entry.cjs"));
expect(previewAnnotationStyles).toContain(".fixed");
expect(previewAnnotationStyles).not.toContain(".static");
await expect(NodeFSP.stat(legacyPath)).rejects.toMatchObject({ code: "ENOENT" });
expect(await NodeFSP.readdir(preview)).toEqual(["Annotation.css", "PickPreload.ts"]);
} finally {
await NodeFSP.rm(root, { recursive: true, force: true });
}
});

it("rebuilds CSS and removes obsolete utilities when either annotation input changes", async () => {
const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "annotation-css-watch-"));
let handle: Awaited<ReturnType<typeof build>> | undefined;
try {
const preview = NodePath.join(root, "src", "preview");
await NodeFSP.mkdir(preview, { recursive: true });
const sourcePath = NodePath.join(preview, "Annotation.css");
const preloadPath = NodePath.join(preview, "PickPreload.ts");
await NodeFSP.writeFile(sourcePath, '@import "tailwindcss";\n');
await NodeFSP.writeFile(preloadPath, 'const className = "fixed";\n');
const entry = NodePath.join(root, "entry.ts");
await NodeFSP.writeFile(
entry,
'export { previewAnnotationStyles } from "virtual:preview-annotation-css";\n',
);
let complete: (() => void) | undefined;
const nextBuild = () =>
new Promise<void>((resolve) => {
complete = resolve;
});
const firstBuild = nextBuild();
handle = await build({
entry: [entry],
outDir: NodePath.join(root, "dist"),
format: "cjs",
dts: false,
config: false,
watch: true,
plugins: [previewAnnotationCssPlugin(root)],
onSuccess: () => {
complete?.();
},
});
await firstBuild;
const require = NodeModule.createRequire(import.meta.url);
const output = NodePath.join(root, "dist", "entry.cjs");
const readCss = () => {
delete require.cache[output];
return require(output).previewAnnotationStyles as string;
};
expect(readCss()).toContain(".fixed");
const preloadBuild = nextBuild();
await NodeFSP.writeFile(preloadPath, 'const className = "absolute";\n');
await preloadBuild;
expect(readCss()).toContain(".absolute");
expect(readCss()).not.toContain(".fixed");
const cssBuild = nextBuild();
await NodeFSP.writeFile(
sourcePath,
'@import "tailwindcss";\n.annotation-marker { color: red; }\n',
);
await cssBuild;
expect(readCss()).toContain(".annotation-marker");
expect(await NodeFSP.readdir(preview)).toEqual(["Annotation.css", "PickPreload.ts"]);
} finally {
await handle?.watch.close();
await NodeFSP.rm(root, { recursive: true, force: true });
}
});
57 changes: 57 additions & 0 deletions apps/desktop/scripts/preview-annotation-css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// @effect-diagnostics nodeBuiltinImport:off - Bundler hooks run outside an Effect runtime and use Node file dependencies.
import * as NodeFSP from "node:fs/promises";
import * as NodeModule from "node:module";
import * as NodePath from "node:path";

import { compile } from "tailwindcss";
import type { TsdownPlugin } from "vite-plus/pack";

const moduleId = "virtual:preview-annotation-css";
const resolvedModuleId = `\0${moduleId}`;
const require = NodeModule.createRequire(import.meta.url);
const tailwindRoot = NodePath.dirname(require.resolve("tailwindcss/package.json"));

export function previewAnnotationCssPlugin(appRoot: string): TsdownPlugin {
const previewRoot = NodePath.join(appRoot, "src", "preview");
const inputs = [
NodePath.join(previewRoot, "Annotation.css"),
NodePath.join(previewRoot, "PickPreload.ts"),
NodePath.join(tailwindRoot, "theme.css"),
NodePath.join(tailwindRoot, "preflight.css"),
] as const;

return {
name: "preview-annotation-css",
async buildStart() {
// Older builds wrote this module into the source tree. It is no longer an input.
await NodeFSP.rm(NodePath.join(previewRoot, "AnnotationStyles.generated.ts"), {
force: true,
});
},
resolveId(id) {
if (id === moduleId) return { id: resolvedModuleId, external: false };
},
async load(id) {
if (id !== resolvedModuleId) return;
for (const input of inputs) this.addWatchFile(input);
const [annotationSource, preloadSource, themeSource, preflightSource] = await Promise.all([
NodeFSP.readFile(inputs[0], "utf8"),
NodeFSP.readFile(inputs[1], "utf8"),
NodeFSP.readFile(inputs[2], "utf8"),
NodeFSP.readFile(inputs[3], "utf8"),
]);
const candidates = new Set(
Array.from(preloadSource.matchAll(/!?-?[A-Za-z0-9_:@/.[\]()%,-]+/g), (match) => match[0]),
);
const compiler = await compile(
[
themeSource,
preflightSource,
annotationSource.replace('@import "tailwindcss";', "@tailwind utilities;"),
].join("\n"),
{ base: appRoot },
);
return `export const previewAnnotationStyles = ${JSON.stringify(compiler.build([...candidates]))};`;
},
};
}
3 changes: 0 additions & 3 deletions apps/desktop/src/preview/AnnotationStyles.generated.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/desktop/src/preview/PickPreload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
} from "@t3tools/contracts";

import { resolveAnnotationSubmission } from "./AnnotationKeyboard.ts";
import { previewAnnotationStyles } from "./AnnotationStyles.generated.ts";
import { previewAnnotationStyles } from "virtual:preview-annotation-css";
import { installRecordingCursor } from "./RecordingCursor.ts";
import { DEFAULT_RECORDING_INPUT_OPTIONS } from "./RecordingInput.ts";
import {
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/preview/annotation-css.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module "virtual:preview-annotation-css" {
export const previewAnnotationStyles: string;
}
2 changes: 1 addition & 1 deletion apps/desktop/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"types": ["node", "electron"],
"lib": ["ESNext", "DOM", "esnext.disposable"]
},
"include": ["src", "vite.config.ts", "../../scripts/lib"]
"include": ["src", "vite.config.ts", "scripts/preview-annotation-css*.ts", "../../scripts/lib"]
}
12 changes: 7 additions & 5 deletions apps/desktop/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import "vite-plus/test/config";
import { defineConfig } from "vite-plus";

import { previewAnnotationCssPlugin } from "./scripts/preview-annotation-css.ts";

import { isDesktopRuntimeExternalDependency } from "../../scripts/lib/desktop-external-packages.ts";
import { loadRepoEnv } from "../../scripts/lib/public-config.ts";

Expand All @@ -24,20 +26,18 @@ export default defineConfig({
run: {
tasks: {
build: {
command:
"node scripts/build-browser-secret.mjs && node scripts/build-preview-annotation-css.mjs && vp pack",
command: "node scripts/build-browser-secret.mjs && vp pack",
dependsOn: ["t3#build"],
cache: false,
},
dev: {
command:
"node scripts/build-browser-secret.mjs && node scripts/build-preview-annotation-css.mjs && cross-env T3CODE_DESKTOP_DEV=1 vp pack --watch",
"node scripts/build-browser-secret.mjs && cross-env T3CODE_DESKTOP_DEV=1 vp pack --watch",
dependsOn: ["t3#build"],
cache: false,
},
"dev:bundle": {
command:
"node scripts/build-browser-secret.mjs && node scripts/build-preview-annotation-css.mjs && vp pack --watch",
command: "node scripts/build-browser-secret.mjs && vp pack --watch",
cache: false,
},
"dev:electron": {
Expand Down Expand Up @@ -119,7 +119,9 @@ export default defineConfig({
dts: false,
sourcemap: true,
outExtensions: () => ({ js: ".cjs" }),
name: "preview-pick-preload",
entry: ["src/preview-pick-preload.ts"],
plugins: [previewAnnotationCssPlugin(import.meta.dirname)],
deps: {
alwaysBundle: (id) => id === "react-grab" || id.startsWith("react-grab/"),
},
Expand Down
Loading