Convert to consistently use JS modules (#6854)

# Description of Changes
Modernises the codebase and gets rid of warnings where Node complains
that it doesn't know what type of JS it's supposed to be reading on
`.js` files. We might as well update everything to just use correct JS
syntax instead of keeping with some files having Node-specific imports.
This commit is contained in:
James Brunton
2026-07-07 11:11:24 +00:00
committed by GitHub
parent 20204f0ddc
commit 17aa71850c
27 changed files with 93 additions and 66 deletions
+5 -2
View File
@@ -1,3 +1,6 @@
module.exports = {
plugins: [require("@tailwindcss/postcss"), require("autoprefixer")],
import tailwindcssPostcss from "@tailwindcss/postcss";
import autoprefixer from "autoprefixer";
export default {
plugins: [tailwindcssPostcss, autoprefixer],
};
+6 -6
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env node
const { icons } = require("@iconify-json/material-symbols");
const fs = require("fs");
const path = require("path");
import { icons } from "@iconify-json/material-symbols";
import fs from "node:fs";
import path from "node:path";
// Check for verbose flag
const isVerbose =
@@ -19,7 +19,7 @@ const debug = (message) => {
// Function to scan codebase for LocalIcon usage
function scanForUsedIcons() {
const usedIcons = new Set();
const srcDir = path.join(__dirname, "..", "src");
const srcDir = path.join(import.meta.dirname, "..", "src");
info("🔍 Scanning codebase for LocalIcon usage...");
@@ -140,7 +140,7 @@ async function main() {
// Check if we need to regenerate (compare with existing)
const outputPath = path.join(
__dirname,
import.meta.dirname,
"..",
"src",
"assets",
@@ -200,7 +200,7 @@ async function main() {
}
// Create output directory
const outputDir = path.join(__dirname, "..", "src", "assets");
const outputDir = path.join(import.meta.dirname, "..", "src", "assets");
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
+7 -14
View File
@@ -1,28 +1,21 @@
#!/usr/bin/env node
const { execSync } = require("node:child_process");
const {
existsSync,
mkdirSync,
writeFileSync,
readFileSync,
} = require("node:fs");
const path = require("node:path");
import { execSync } from "node:child_process";
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
import path from "node:path";
import { argv } from "node:process";
const { argv } = require("node:process");
const inputIdx = argv.indexOf("--input");
const INPUT_FILE = inputIdx > -1 ? argv[inputIdx + 1] : null;
const POSTPROCESS_ONLY = !!INPUT_FILE;
// __dirname is available in CommonJS by default
/**
* Generate 3rd party licenses for frontend dependencies
* This script creates a JSON file similar to the Java backend's 3rdPartyLicenses.json
*/
const OUTPUT_FILE = path.join(
__dirname,
import.meta.dirname,
"..",
"src",
"assets",
@@ -30,7 +23,7 @@ const OUTPUT_FILE = path.join(
);
// package.json lives at the workspace root (frontend/), not editor/. The
// script is at frontend/editor/scripts/, so walk up two levels.
const PACKAGE_JSON = path.join(__dirname, "..", "..", "package.json");
const PACKAGE_JSON = path.join(import.meta.dirname, "..", "..", "package.json");
// Ensure the output directory exists
const outputDir = path.dirname(OUTPUT_FILE);
@@ -192,7 +185,7 @@ try {
// Write license warnings to a separate file for CI/CD
const warningsFile = path.join(
__dirname,
import.meta.dirname,
"..",
"src",
"assets",
+25 -22
View File
@@ -16,11 +16,10 @@
/* global document, getComputedStyle */ // used inside page.evaluate (browser context)
import fs from "node:fs/promises";
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const HERE = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(HERE, "..");
@@ -69,11 +68,14 @@ export const THEME = {
};
// ---- icon resolution (material-symbols via iconify) ------------------------
function resolveIcon(icon) {
async function resolveIcon(icon) {
if (!icon) return "";
if (icon.trim().startsWith("<svg")) return icon; // raw svg passed through
const { getIconData, iconToSVG } = require("@iconify/utils");
const set = require("@iconify-json/material-symbols/icons.json");
const { getIconData, iconToSVG } = await import("@iconify/utils");
const { default: set } = await import(
"@iconify-json/material-symbols/icons.json",
{ with: { type: "json" } }
);
const data = getIconData(set, icon);
if (!data) throw new Error(`icon not found in material-symbols: "${icon}"`);
const { attributes, body } = iconToSVG(data);
@@ -148,7 +150,7 @@ const escapeHtml = (s) =>
let _browser = null;
async function getBrowser() {
if (_browser) return _browser;
const puppeteer = require("puppeteer");
const { default: puppeteer } = await import("puppeteer");
_browser = await puppeteer.launch({
headless: "new",
args: ["--no-sandbox"],
@@ -163,7 +165,7 @@ export async function renderOgCard({
outFile,
theme = THEME,
}) {
const iconSvg = resolveIcon(icon);
const iconSvg = await resolveIcon(icon);
const html = await buildHtml({ name, description, iconSvg, theme });
const browser = await getBrowser();
const page = await browser.newPage();
@@ -230,7 +232,7 @@ const kebab = (id) => id.replace(/([A-Z])/g, "-$1").toLowerCase();
// English name/description live next to each tool as the `t(key, fallback)` default.
function readRegistryStrings() {
const src = require("node:fs").readFileSync(
const src = readFileSync(
path.join(ROOT, "src/core/data/useTranslatedToolRegistry.tsx"),
"utf8",
);
@@ -268,7 +270,7 @@ export async function generateMissing(theme = THEME) {
// Each tool's app icon lives as `icon="<material-symbol>"` just before its
// `name: t("home.<id>.title", …)`. Pair each title with the closest preceding icon.
function readRegistryIcons() {
const src = require("node:fs").readFileSync(
const src = readFileSync(
path.join(ROOT, "src/core/data/useTranslatedToolRegistry.tsx"),
"utf8",
);
@@ -288,25 +290,26 @@ function readRegistryIcons() {
return byId;
}
function iconExists(name) {
async function iconExists(name) {
if (!name) return false;
try {
const { getIconData } = require("@iconify/utils");
return !!getIconData(
require("@iconify-json/material-symbols/icons.json"),
name,
const { getIconData } = await import("@iconify/utils");
const { default: set } = await import(
"@iconify-json/material-symbols/icons.json",
{ with: { type: "json" } }
);
return !!getIconData(set, name);
} catch {
return false;
}
}
// First candidate that resolves; also tries dropping a "-rounded" suffix.
function firstResolvableIcon(candidates) {
async function firstResolvableIcon(candidates) {
for (const c of candidates) {
if (iconExists(c)) return c;
if (await iconExists(c)) return c;
const alt = c && c.replace(/-rounded$/, "");
if (alt && alt !== c && iconExists(alt)) return alt;
if (alt && alt !== c && (await iconExists(alt))) return alt;
}
return "description-outline";
}
@@ -324,14 +327,14 @@ export async function generateAll(theme = THEME) {
const { titles, descs } = readRegistryStrings();
const regIcons = readRegistryIcons();
const ogMap = JSON.parse(
require("node:fs").readFileSync(
path.join(ROOT, "src/core/data/ogImageMap.json"),
"utf8",
),
readFileSync(path.join(ROOT, "src/core/data/ogImageMap.json"), "utf8"),
);
const results = [];
for (const [id, basename] of Object.entries(ogMap)) {
const icon = firstResolvableIcon([regIcons[id], MISSING_TOOL_ICONS[id]]);
const icon = await firstResolvableIcon([
regIcons[id],
MISSING_TOOL_ICONS[id],
]);
await renderOgCard({
name: titles[id] || humanizeId(id),
description: descs[id] || "",
@@ -7,7 +7,7 @@ import {
} from "@app/tests/helpers/ui-helpers";
import path from "path";
const FIXTURES_DIR = path.join(__dirname, "../test-fixtures");
const FIXTURES_DIR = path.join(import.meta.dirname, "../test-fixtures");
const ENCRYPTED_PDF = path.join(FIXTURES_DIR, "encrypted.pdf");
const SAMPLE_PDF = path.join(FIXTURES_DIR, "sample.pdf");
@@ -2,7 +2,10 @@ import { test, expect } from "@app/tests/helpers/stub-test-base";
import { uploadFiles } from "@app/tests/helpers/ui-helpers";
import path from "path";
const SAMPLE_PDF = path.join(__dirname, "../test-fixtures/sample.pdf");
const SAMPLE_PDF = path.join(
import.meta.dirname,
"../test-fixtures/sample.pdf",
);
/**
* Add Page Numbers walks the user through a multi-step config: position
@@ -2,7 +2,10 @@ import { test, expect } from "@app/tests/helpers/stub-test-base";
import { uploadFiles } from "@app/tests/helpers/ui-helpers";
import path from "path";
const SAMPLE_PDF = path.join(__dirname, "../test-fixtures/sample.pdf");
const SAMPLE_PDF = path.join(
import.meta.dirname,
"../test-fixtures/sample.pdf",
);
/**
* AddStamp loads, accepts a PDF upload, and remains interactive.
@@ -3,7 +3,7 @@ import { uploadFiles } from "@app/tests/helpers/ui-helpers";
import type { Page, Route } from "@playwright/test";
import path from "path";
const FIXTURES_DIR = path.join(__dirname, "../test-fixtures");
const FIXTURES_DIR = path.join(import.meta.dirname, "../test-fixtures");
const SAMPLE_PDF = path.join(FIXTURES_DIR, "sample.pdf");
// app-config the desktop bundle would return: hardware signing is offered only there.
@@ -5,7 +5,7 @@ import { suppressNativeFilePicker } from "@app/tests/helpers/ui-helpers";
// ---------------------------------------------------------------------------
// Test fixtures — pre-generated keystores in test-fixtures/certs/
// ---------------------------------------------------------------------------
const CERTS_DIR = path.join(__dirname, "../test-fixtures/certs");
const CERTS_DIR = path.join(import.meta.dirname, "../test-fixtures/certs");
const VALID_P12 = path.join(CERTS_DIR, "valid-test.p12");
const EXPIRED_P12 = path.join(CERTS_DIR, "expired-test.p12");
const NOT_YET_VALID_P12 = path.join(CERTS_DIR, "not-yet-valid-test.p12");
@@ -2,7 +2,7 @@ import { test, expect } from "@app/tests/helpers/stub-test-base";
import path from "path";
const ANNOTATED_PDF = path.join(
__dirname,
import.meta.dirname,
"../test-fixtures/annotations_out_of_order.pdf",
);
@@ -22,7 +22,7 @@ import { test, expect, type Page } from "@playwright/test";
import path from "path";
import { mockAppApis } from "@app/tests/helpers/api-stubs";
const FIXTURES_DIR = path.join(__dirname, "../test-fixtures");
const FIXTURES_DIR = path.join(import.meta.dirname, "../test-fixtures");
const PDF_A = path.join(FIXTURES_DIR, "compare_sample_a.pdf");
const PDF_B = path.join(FIXTURES_DIR, "compare_sample_b.pdf");
@@ -10,7 +10,7 @@ import path from "path";
import { mockAppApis } from "@app/tests/helpers/api-stubs";
import { suppressNativeFilePicker } from "@app/tests/helpers/ui-helpers";
const FIXTURES_DIR = path.join(__dirname, "../test-fixtures");
const FIXTURES_DIR = path.join(import.meta.dirname, "../test-fixtures");
const SAMPLE_PDF = path.join(FIXTURES_DIR, "sample.pdf");
// ---------------------------------------------------------------------------
@@ -23,7 +23,7 @@ import fs from "fs";
import { mockAppApis } from "@app/tests/helpers/api-stubs";
import { suppressNativeFilePicker } from "@app/tests/helpers/ui-helpers";
const FIXTURES_DIR = path.join(__dirname, "../test-fixtures");
const FIXTURES_DIR = path.join(import.meta.dirname, "../test-fixtures");
const ENCRYPTED_PDF = path.join(FIXTURES_DIR, "encrypted.pdf");
const FAKE_UNLOCKED_PDF = Buffer.from(
@@ -2,7 +2,7 @@ import { test, expect } from "@app/tests/helpers/stub-test-base";
import { uploadFiles } from "@app/tests/helpers/ui-helpers";
import path from "path";
const FIXTURES_DIR = path.join(__dirname, "../test-fixtures");
const FIXTURES_DIR = path.join(import.meta.dirname, "../test-fixtures");
const SAMPLE_PDF = path.join(FIXTURES_DIR, "sample.pdf");
/**
@@ -13,7 +13,7 @@ import fs from "fs";
// `result.zip` instead of the merged file. The UI fix uses signature-based
// detection - %PDF wins regardless of Content-Type.
const FIXTURES_DIR = path.join(__dirname, "../test-fixtures");
const FIXTURES_DIR = path.join(import.meta.dirname, "../test-fixtures");
const SAMPLE_PDF = path.join(FIXTURES_DIR, "sample.pdf");
const SAMPLE_PDF_BYTES = fs.readFileSync(SAMPLE_PDF);
@@ -10,7 +10,10 @@ import { uploadFiles, dismissTourTooltip } from "@app/tests/helpers/ui-helpers";
// Fixture: 4 portrait pages whose intrinsic /Rotate is 0, 90, 270, 180.
// Page 3 (index 2) is 270 so a single rotate-right lands on a net-0 target -
// the exact case the export used to drop, leaving the source rotation behind.
const ROTATED_PDF = path.join(__dirname, "../test-fixtures/rotated-pages.pdf");
const ROTATED_PDF = path.join(
import.meta.dirname,
"../test-fixtures/rotated-pages.pdf",
);
const SOURCE_ROTATIONS = [0, 90, 270, 180];
/** Read the rotation each thumbnail is currently displaying (= page.rotation). */
@@ -1,7 +1,10 @@
import { test, expect } from "@app/tests/helpers/stub-test-base";
import path from "path";
const SAMPLE_PDF = path.join(__dirname, "../test-fixtures/sample.pdf");
const SAMPLE_PDF = path.join(
import.meta.dirname,
"../test-fixtures/sample.pdf",
);
/**
* The reader/viewer exposes an in-PDF text search via CustomSearchLayer.
@@ -22,7 +22,14 @@ function resolveFixturePath(filename: string): string {
filename,
),
path.join(process.cwd(), "src", "core", "tests", "test-fixtures", filename),
path.join(__dirname, "..", "core", "tests", "test-fixtures", filename),
path.join(
import.meta.dirname,
"..",
"core",
"tests",
"test-fixtures",
filename,
),
];
for (const p of candidates) {
if (fs.existsSync(p)) return p;
@@ -17,7 +17,10 @@ import { uploadFiles, openSettings } from "@app/tests/helpers/ui-helpers";
* - whatsNewStepsConfig.ts
*/
const SAMPLE_PDF = path.join(__dirname, "../test-fixtures/sample.pdf");
const SAMPLE_PDF = path.join(
import.meta.dirname,
"../test-fixtures/sample.pdf",
);
// ---------------------------------------------------------------------------
// 15.1 Static layout - always visible on the main page
@@ -3,7 +3,7 @@ import { uploadFiles } from "@app/tests/helpers/ui-helpers";
import type { Page, Route } from "@playwright/test";
import path from "path";
const FIXTURES_DIR = path.join(__dirname, "../test-fixtures");
const FIXTURES_DIR = path.join(import.meta.dirname, "../test-fixtures");
const SAMPLE_PDF = path.join(FIXTURES_DIR, "sample.pdf");
// Base backend SignatureValidationResult; tests override the trust-related fields.
@@ -17,7 +17,10 @@ import path from "path";
* Backend-free spec.
*/
const SAMPLE_PDF = path.join(__dirname, "../test-fixtures/sample.pdf");
const SAMPLE_PDF = path.join(
import.meta.dirname,
"../test-fixtures/sample.pdf",
);
async function openViewerWithSample(page: import("@playwright/test").Page) {
await page.goto("/read");
@@ -1,7 +1,7 @@
import path from "path";
import { test, expect } from "@app/tests/helpers/stub-test-base";
const FIXTURES_DIR = path.join(__dirname, "../test-fixtures");
const FIXTURES_DIR = path.join(import.meta.dirname, "../test-fixtures");
const SAMPLE_PDF = path.join(FIXTURES_DIR, "sample.pdf");
const MULTIPAGE_PDF = path.join(FIXTURES_DIR, "annotations_out_of_order.pdf");
@@ -2,7 +2,10 @@ import { test, expect } from "@app/tests/helpers/stub-test-base";
import { uploadFiles } from "@app/tests/helpers/ui-helpers";
import path from "path";
const SAMPLE_PDF = path.join(__dirname, "../test-fixtures/sample.pdf");
const SAMPLE_PDF = path.join(
import.meta.dirname,
"../test-fixtures/sample.pdf",
);
/**
* Watermark has three modes — text / image / file overlay — selected via
+1 -1
View File
@@ -1,5 +1,5 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
darkMode: ["class", '[data-mantine-color-scheme="dark"]'],
theme: {
-1
View File
@@ -64,7 +64,6 @@ export default defineConfig(
},
],
"@typescript-eslint/no-explicit-any": "off", // Temporarily disabled until codebase conformant
"@typescript-eslint/no-require-imports": "off", // Temporarily disabled until codebase conformant
"@typescript-eslint/no-unused-vars": [
"error",
{
+1
View File
@@ -2,6 +2,7 @@
"name": "frontend",
"version": "0.1.0",
"private": true,
"type": "module",
"license": "SEE LICENSE IN https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/refs/heads/main/proprietary/LICENSE",
"proxy": "http://localhost:8080",
"dependencies": {
+1 -1
View File
@@ -5,7 +5,7 @@
* Calculates date from 7 days ago and runs npm update/audit with that date
*/
const { spawn } = require("child_process");
import { spawn } from "node:child_process";
// Calculate date from 7 days ago in YYYY-MM-DD format
const date = new Date();