mirror of
https://github.com/VladSez/easy-invoice-pdf
synced 2026-04-21 21:47:52 +00:00
* feat: add debug local storage UI and update README; include new template parameter handling in invoice form * feat: add URL compression logic when generating link to invoice to reduce url length + add unit tests + improved existing e2e tests * ci: remove type check step from unit tests workflow to streamline CI process * test: update e2e tests for Stripe invoice sharing logic and template; increase timeout for visibility checks * test: refactor e2e tests for invoice generation and sharing; update element selectors and enhance URL disallow rules in robots.txt * chore: enhance README with detailed features and update about page references; add GitHub star CTA component * chore: update configuration files for Prettier, run prettify across the project * chore: run dedupe * test: add e2e tests for Open Graph meta tags in invoice templates; verify correct rendering for default and Stripe templates * chore: remove @stagewise/toolbar-next package and related development toolbar component from the project
198 lines
5.4 KiB
JavaScript
198 lines
5.4 KiB
JavaScript
// @ts-check
|
|
|
|
import { withSentryConfig } from "@sentry/nextjs";
|
|
import createNextIntlPlugin from "next-intl/plugin";
|
|
import createMDX from "@next/mdx";
|
|
import { createJiti } from "jiti";
|
|
import remarkGfm from "remark-gfm";
|
|
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import fs from "node:fs";
|
|
|
|
const loadTsFileViaJiti = createJiti(fileURLToPath(import.meta.url));
|
|
|
|
// Import ENV file here to validate during build. Using jiti@^1 we can import .ts files :)
|
|
loadTsFileViaJiti("./src/env");
|
|
|
|
// Validate all i18n files, that are used to translate the /about page
|
|
async function validatei18nAndTranslationFiles() {
|
|
// Validate our custom translations object against schema, that is used to translate pdf fields
|
|
try {
|
|
// Import the translations schema using jiti
|
|
// @ts-ignore
|
|
const { translationsSchema, TRANSLATIONS } = await loadTsFileViaJiti.import(
|
|
"./src/app/schema/translations.ts",
|
|
);
|
|
|
|
const result = translationsSchema.safeParse(TRANSLATIONS);
|
|
|
|
if (!result.success) {
|
|
console.error("❌ Invalid translations:", result.error.message);
|
|
process.exit(1);
|
|
}
|
|
} catch (error) {
|
|
console.error("❌ Error validating translations:", error);
|
|
process.exit(1);
|
|
}
|
|
|
|
const messagesDir = path.join(process.cwd(), "messages");
|
|
|
|
// Import the messages schema using jiti
|
|
// @ts-ignore
|
|
const { messagesSchema } = await loadTsFileViaJiti.import(
|
|
"./src/app/schema/i18n-schema.ts",
|
|
);
|
|
|
|
// Validate messages
|
|
const is18nJSONMessageFiles = fs
|
|
.readdirSync(messagesDir)
|
|
.filter((file) => file.endsWith(".json"));
|
|
|
|
const validationPromises = is18nJSONMessageFiles.map(async (file) => {
|
|
try {
|
|
const messages = JSON.parse(
|
|
await fs.promises.readFile(path.join(messagesDir, file), "utf8"),
|
|
);
|
|
|
|
const result = messagesSchema.safeParse(messages);
|
|
|
|
if (!result.success) {
|
|
return {
|
|
file,
|
|
success: false,
|
|
error: result.error.message,
|
|
};
|
|
}
|
|
|
|
return {
|
|
file,
|
|
success: true,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
file,
|
|
success: false,
|
|
error: `Error reading/parsing file: ${error}`,
|
|
};
|
|
}
|
|
});
|
|
|
|
const results = await Promise.allSettled(validationPromises);
|
|
|
|
const hasErrors = results.some(
|
|
(result) =>
|
|
result.status === "rejected" ||
|
|
(result.status === "fulfilled" && !result.value.success),
|
|
);
|
|
|
|
if (hasErrors) {
|
|
results.forEach((result) => {
|
|
if (result.status === "rejected") {
|
|
console.error(`❌ Unexpected error:`, result.reason);
|
|
} else if (!result.value.success) {
|
|
console.error(
|
|
`❌ Invalid i18n messages in ${result.value.file}:`,
|
|
result.value.error,
|
|
);
|
|
}
|
|
});
|
|
|
|
console.error("❌ Message validation failed");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Since the function is now async, we need to handle it properly
|
|
validatei18nAndTranslationFiles().catch((error) => {
|
|
console.error("❌ Fatal error during validation:", error);
|
|
process.exit(1);
|
|
});
|
|
|
|
const withNextIntl = createNextIntlPlugin({
|
|
experimental: {
|
|
createMessagesDeclaration: "./messages/en.json",
|
|
},
|
|
});
|
|
|
|
const withMDX = createMDX({
|
|
// Add markdown plugins here, as desired
|
|
extension: /\.mdx?$/,
|
|
options: {
|
|
remarkPlugins: [remarkGfm],
|
|
rehypePlugins: [],
|
|
},
|
|
});
|
|
|
|
/** @type {import('next').NextConfig} */
|
|
const nextConfig = {
|
|
// Configure the file extensions that Next.js should handle
|
|
pageExtensions: ["ts", "tsx", "js", "jsx", "md", "mdx"],
|
|
typescript: {
|
|
ignoreBuildErrors: true,
|
|
},
|
|
eslint: {
|
|
ignoreDuringBuilds: true,
|
|
},
|
|
compiler: {
|
|
removeConsole: process.env.VERCEL_ENV === "production",
|
|
},
|
|
logging: {
|
|
fetches: {
|
|
fullUrl: true,
|
|
},
|
|
},
|
|
webpack: (config) => {
|
|
config.resolve.alias.canvas = false;
|
|
return config;
|
|
},
|
|
async rewrites() {
|
|
return [
|
|
{
|
|
// proxy umami analytics https://umami.is/docs/guides/running-on-vercel
|
|
source: "/stats/:match*",
|
|
destination: "https://cloud.umami.is/:match*",
|
|
},
|
|
];
|
|
},
|
|
async redirects() {
|
|
return [
|
|
{
|
|
source: "/:locale/app", // Redirect all /:locale/app requests to the root, because we changed the structure of the app
|
|
destination: "/",
|
|
permanent: true,
|
|
},
|
|
];
|
|
},
|
|
};
|
|
|
|
export default withSentryConfig(withNextIntl(withMDX(nextConfig)), {
|
|
// For all available options, see:
|
|
// https://www.npmjs.com/package/@sentry/webpack-plugin#options
|
|
|
|
org: "easyinvoicepdf",
|
|
project: "easy-invoice-pdf",
|
|
|
|
// Only print logs for uploading source maps in CI
|
|
silent: !process.env.CI,
|
|
|
|
// For all available options, see:
|
|
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
|
|
|
|
// Upload a larger set of source maps for prettier stack traces (increases build time)
|
|
widenClientFileUpload: true,
|
|
|
|
// Automatically annotate React components to show their full name in breadcrumbs and session replay
|
|
reactComponentAnnotation: {
|
|
enabled: true,
|
|
},
|
|
|
|
// Uncomment to route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
|
|
// This can increase your server load as well as your hosting bill.
|
|
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
|
|
// side errors will fail.
|
|
// tunnelRoute: "/monitoring",
|
|
|
|
// Automatically tree-shake Sentry logger statements to reduce bundle size
|
|
disableLogger: true,
|
|
});
|