Merged in feat/syncDefaultMessage (pull request #3022)
Sync defaultMessage from lokalise * Enhance translation sync functionality and tests - Added logging for found component files during sync. - Introduced tests for handling complex components with replacements. - Updated regex in syncIntlFormatMessage to support optional second arguments. - Removed unused test files. * feat(syncDefaultMessage): add script for syncing default message with lokalise * feat(syncDefaultMessage): add script for syncing default message with lokalise Approved-by: Matilda Landström
This commit is contained in:
@@ -16,7 +16,8 @@
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:ui": "playwright test --ui",
|
||||
"test:e2e:debug": "playwright test --debug",
|
||||
"include:shared": "jiti ../../scripts/copyFiles.ts ../../shared/ public/_static/shared"
|
||||
"include:shared": "jiti ../../scripts/copyFiles.ts ../../shared/ public/_static/shared",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@formatjs/intl": "^3.1.6",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
tabWidth: 2,
|
||||
endOfLine: "lf",
|
||||
}
|
||||
14
apps/partner-sas/prettier.config.js
Normal file
14
apps/partner-sas/prettier.config.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @see https://prettier.io/docs/configuration
|
||||
* @type {import("prettier").Config}
|
||||
*/
|
||||
const config = {
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
tabWidth: 2,
|
||||
endOfLine: "lf",
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "bun --watch src/index.ts | pino-pretty -o '{if module}[{module}] {end}{msg}' -i pid,hostname",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 && tsc",
|
||||
"lint:fix": "eslint . --ext ts,tsx --fix --report-unused-disable-directives --max-warnings 0 && tsc"
|
||||
"lint:fix": "eslint . --ext ts,tsx --fix --report-unused-disable-directives --max-warnings 0 && tsc",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@elysiajs/server-timing": "^1.3.0",
|
||||
@@ -28,7 +29,6 @@
|
||||
"eslint": "^9",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"prettier": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Queue,Worker } from "bullmq";
|
||||
import { Queue, Worker } from "bullmq";
|
||||
import z from "zod";
|
||||
|
||||
import { env } from "@/env";
|
||||
@@ -6,7 +6,7 @@ import { sentry } from "@/server/sentry.server.config";
|
||||
import { loggerModule } from "@/utils/logger";
|
||||
import { timeout } from "@/utils/timeout";
|
||||
|
||||
import { bullmqredis,redis } from ".";
|
||||
import { bullmqredis, redis } from ".";
|
||||
|
||||
const DELETE_JOB = "deleteQueueJob";
|
||||
const deleteQueueLogger = loggerModule("deleteQueue");
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
const maskChar = "*";
|
||||
export function mask(
|
||||
value: string,
|
||||
options?: { visibleStart?: number; visibleEnd?: number; maxLength?: number },
|
||||
options?: {
|
||||
visibleStart?: number;
|
||||
visibleEnd?: number;
|
||||
maxLength?: number;
|
||||
},
|
||||
): string {
|
||||
if (!value) return "";
|
||||
|
||||
|
||||
1
apps/scandic-redirect/.prettierignore
Normal file
1
apps/scandic-redirect/.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
netlify/functions/data/*.json
|
||||
@@ -1,12 +1,12 @@
|
||||
import { createReadStream } from "fs";
|
||||
import { join } from "path";
|
||||
import { createReadStream } from "fs"
|
||||
import { join } from "path"
|
||||
|
||||
export default async (req: Request) => {
|
||||
try {
|
||||
const body = await req.json();
|
||||
const body = await req.json()
|
||||
|
||||
if (body.lang && body.pathname) {
|
||||
const filePath = join(import.meta.dirname, `./data/${body.lang}.json`);
|
||||
const filePath = join(import.meta.dirname, `./data/${body.lang}.json`)
|
||||
|
||||
const redirectUrl = await new Promise<string | null>(
|
||||
(resolve, reject) => {
|
||||
@@ -14,58 +14,58 @@ export default async (req: Request) => {
|
||||
emitClose: false,
|
||||
encoding: "utf-8",
|
||||
highWaterMark: 1024,
|
||||
});
|
||||
const data: string[] = [];
|
||||
})
|
||||
const data: string[] = []
|
||||
|
||||
stream.on("data", (chunk) => {
|
||||
if (data.length === 3) {
|
||||
data.shift();
|
||||
data.shift()
|
||||
}
|
||||
data.push(chunk.toString());
|
||||
data.push(chunk.toString())
|
||||
// Since we strip trailing slash (in the trailingSlash middleware) before entering this middleware,
|
||||
// we need check matching paths both including and excluding trailing slash.
|
||||
const re = new RegExp(`"${body.pathname}\/?":"([^"]+)"`);
|
||||
const re = new RegExp(`"${body.pathname}\/?":"([^"]+)"`)
|
||||
|
||||
const match = data.join("").match(re);
|
||||
const match = data.join("").match(re)
|
||||
if (match?.[1]) {
|
||||
stream.destroy();
|
||||
resolve(match[1]);
|
||||
stream.destroy()
|
||||
resolve(match[1])
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
stream.on("error", (err) => {
|
||||
console.error("Stream error:", err);
|
||||
stream.destroy();
|
||||
reject(err);
|
||||
});
|
||||
console.error("Stream error:", err)
|
||||
stream.destroy()
|
||||
reject(err)
|
||||
})
|
||||
|
||||
stream.on("end", () => {
|
||||
stream.destroy();
|
||||
resolve(null); // No match found
|
||||
});
|
||||
stream.destroy()
|
||||
resolve(null) // No match found
|
||||
})
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
if (redirectUrl) {
|
||||
// Make sure to exclude trailing slash in the redirectUrl to avoid an extra middleware roundtrip
|
||||
const redirectUrlWithoutTrailingSlash = redirectUrl.endsWith("/")
|
||||
? redirectUrl.slice(0, -1)
|
||||
: redirectUrl;
|
||||
: redirectUrl
|
||||
if (redirectUrlWithoutTrailingSlash === body.pathname) {
|
||||
console.log(
|
||||
`[scandic-redirect] recieved ${body.pathname}, found ${redirectUrlWithoutTrailingSlash}, no-op`
|
||||
);
|
||||
return new Response("Not Found", { status: 404 });
|
||||
)
|
||||
return new Response("Not Found", { status: 404 })
|
||||
}
|
||||
console.log(
|
||||
`[scandic-redirect] recieved ${body.pathname}, return ${redirectUrlWithoutTrailingSlash}, success`
|
||||
);
|
||||
return new Response(redirectUrlWithoutTrailingSlash);
|
||||
)
|
||||
return new Response(redirectUrlWithoutTrailingSlash)
|
||||
}
|
||||
}
|
||||
console.log(`[scandic-redirect] recieved ${body.pathname}, not found`);
|
||||
return new Response("Not Found", { status: 404 });
|
||||
console.log(`[scandic-redirect] recieved ${body.pathname}, not found`)
|
||||
return new Response("Not Found", { status: 404 })
|
||||
} catch (error) {
|
||||
return new Response("Bad request", { status: 400 });
|
||||
return new Response("Bad request", { status: 400 })
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"generate": "jiti ./scripts/generateRedirectFile/index.ts"
|
||||
"generate": "jiti ./scripts/generateRedirectFile/index.ts",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@netlify/functions": "^3.0.0"
|
||||
|
||||
@@ -2,11 +2,14 @@ import styles from "./page.module.css"
|
||||
|
||||
import type { LangParams, LayoutArgs, StatusParams } from "@/types/params"
|
||||
|
||||
export default async function MiddlewareError(props: LayoutArgs<LangParams & StatusParams>) {
|
||||
const params = await props.params;
|
||||
export default async function MiddlewareError(
|
||||
props: LayoutArgs<LangParams & StatusParams>
|
||||
) {
|
||||
const params = await props.params
|
||||
return (
|
||||
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
|
||||
<div className={styles.layout}>Middleware error {params.lang} {params.status}
|
||||
<div className={styles.layout}>
|
||||
Middleware error {params.lang} {params.status}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ import { Receipt } from "@/components/HotelReservation/MyStay/Receipt"
|
||||
|
||||
import type { LangParams, PageArgs } from "@/types/params"
|
||||
|
||||
export default async function ReceiptPage(props: PageArgs<LangParams, { RefId?: string }>) {
|
||||
const searchParams = await props.searchParams;
|
||||
export default async function ReceiptPage(
|
||||
props: PageArgs<LangParams, { RefId?: string }>
|
||||
) {
|
||||
const searchParams = await props.searchParams
|
||||
if (!searchParams.RefId) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export default async function CampaignHotelListing({
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: "common.tripAdvisorRating",
|
||||
defaultMessage: "TripAdvisor rating",
|
||||
defaultMessage: "Tripadvisor rating",
|
||||
}),
|
||||
value: HotelSortOption.TripAdvisorRating,
|
||||
},
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function Row({ transaction }: RowProps) {
|
||||
transaction.nights === 0
|
||||
? intl.formatMessage({
|
||||
id: "earnAndBurn.journeyTable.pointsActivity",
|
||||
defaultMessage: "Points activity",
|
||||
defaultMessage: "Point activity",
|
||||
})
|
||||
: transaction.hotelName && transaction.city
|
||||
? `${transaction.hotelName}, ${transaction.city} ${nightsMsg}`
|
||||
|
||||
@@ -85,7 +85,7 @@ export default async function DestinationCityPage({
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: "common.tripAdvisorRating",
|
||||
defaultMessage: "TripAdvisor rating",
|
||||
defaultMessage: "Tripadvisor rating",
|
||||
}),
|
||||
value: HotelSortOption.TripAdvisorRating,
|
||||
},
|
||||
|
||||
@@ -235,7 +235,7 @@ export default function GuestDetails({
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: "myStay.guestDetails.modifyGuestDetails",
|
||||
defaultMessage: "Modify guest details",
|
||||
defaultMessage: "Edit guest details",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
@@ -265,7 +265,7 @@ export default function GuestDetails({
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: "myStay.guestDetails.modifyGuestDetails",
|
||||
defaultMessage: "Modify guest details",
|
||||
defaultMessage: "Edit guest details",
|
||||
})}
|
||||
</span>
|
||||
</Typography>
|
||||
@@ -280,7 +280,7 @@ export default function GuestDetails({
|
||||
<Dialog
|
||||
aria-label={intl.formatMessage({
|
||||
id: "myStay.guestDetails.modifyGuestDetails",
|
||||
defaultMessage: "Modify guest details",
|
||||
defaultMessage: "Edit guest details",
|
||||
})}
|
||||
>
|
||||
{({ close }) => (
|
||||
@@ -288,7 +288,7 @@ export default function GuestDetails({
|
||||
<ModalContentWithActions
|
||||
title={intl.formatMessage({
|
||||
id: "myStay.guestDetails.modifyGuestDetails",
|
||||
defaultMessage: "Modify guest details",
|
||||
defaultMessage: "Edit guest details",
|
||||
})}
|
||||
onClose={() => setIsModifyGuestDetailsOpen(false)}
|
||||
content={
|
||||
|
||||
@@ -120,7 +120,7 @@ export default function Form() {
|
||||
{
|
||||
id: "myStay.gla.termsAndConditionsMessage",
|
||||
defaultMessage:
|
||||
"I accept the terms for this stay and the general <termsAndConditionsLink>Booking & Cancellation Terms</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandic's Privacy Policy</privacyPolicyLink>. ",
|
||||
"I accept the terms for this stay and the general <termsAndConditionsLink>Booking & Cancellation Terms</termsAndConditionsLink>, and understand Scandic will process my personal data for this stay in accordance with <privacyPolicyLink>Scandic's Privacy Policy</privacyPolicyLink>.",
|
||||
},
|
||||
{
|
||||
termsAndConditionsLink: (str) => (
|
||||
|
||||
@@ -301,7 +301,7 @@ export default function Room({ booking, roomNr, user }: RoomProps) {
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "myStay.modifyBy",
|
||||
defaultMessage: "Modify By",
|
||||
defaultMessage: "Modify by",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
@@ -36,7 +36,7 @@ export default async function LevelProgressCard({
|
||||
<h2 id="level-progress-card-title" className={styles.title}>
|
||||
{intl.formatMessage({
|
||||
id: "myPages.yourLevelProgress",
|
||||
defaultMessage: "Your Level Progress",
|
||||
defaultMessage: "Your level progress",
|
||||
})}
|
||||
</h2>
|
||||
</Typography>
|
||||
|
||||
@@ -293,7 +293,7 @@ export default function BookedRoomSidePeekContent({
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "myStay.modifyBy",
|
||||
defaultMessage: "Modify By",
|
||||
defaultMessage: "Modify by",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
@@ -85,7 +85,7 @@ export function translateSeatingType(type: string, intl: IntlShape) {
|
||||
case SeatingType.Theatre:
|
||||
return intl.formatMessage({
|
||||
id: "meetingRoomCard.theatre",
|
||||
defaultMessage: "Theatre",
|
||||
defaultMessage: "Theater",
|
||||
})
|
||||
case SeatingType.UShape:
|
||||
return intl.formatMessage({
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
"ci:build": "yarn lint && yarn test && yarn build",
|
||||
"clean": "rm -rf .next",
|
||||
"check-types": "tsc --noEmit",
|
||||
"include:shared": "jiti ../../scripts/copyFiles.ts ../../shared public/_static/shared"
|
||||
"include:shared": "jiti ../../scripts/copyFiles.ts ../../shared public/_static/shared",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@contentstack/live-preview-utils": "^3.2.1",
|
||||
@@ -127,7 +128,6 @@
|
||||
"json-sort-cli": "^4.0.9",
|
||||
"lint-staged": "^15.5.2",
|
||||
"netlify-plugin-cypress": "^2.2.1",
|
||||
"prettier": "^3.5.3",
|
||||
"schema-dts": "^1.1.5",
|
||||
"start-server-and-test": "^2.0.11",
|
||||
"ts-morph": "^25.0.1",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
tabWidth: 2,
|
||||
endOfLine: "lf",
|
||||
}
|
||||
14
apps/scandic-web/prettier.config.js
Normal file
14
apps/scandic-web/prettier.config.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @see https://prettier.io/docs/configuration
|
||||
* @type {import("prettier").Config}
|
||||
*/
|
||||
const config = {
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
tabWidth: 2,
|
||||
endOfLine: "lf",
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -11,6 +11,7 @@
|
||||
"dev:ds": "turbo run dev --filter=@scandic-hotels/design-system --output-logs new-only",
|
||||
"dev:sas": "turbo run dev --filter=@scandic-hotels/partner-sas --output-logs new-only",
|
||||
"test": "turbo run test",
|
||||
"format": "turbo run format",
|
||||
"postinstall": "husky",
|
||||
"icons:update": "jiti scripts/material-symbols-update.mts",
|
||||
"check-types": "turbo run check-types",
|
||||
@@ -25,7 +26,8 @@
|
||||
"i18n:distribute": "jiti scripts/i18n/distribute.ts scandic-web partner-sas",
|
||||
"i18n:push": "yarn i18n:extract && yarn i18n:upload",
|
||||
"i18n:pull": "yarn i18n:download && yarn i18n:compile && yarn i18n:distribute",
|
||||
"i18n:sync": "yarn i18n:push && yarn i18n:pull"
|
||||
"i18n:sync": "yarn i18n:push && yarn i18n:pull",
|
||||
"i18n:syncDefaultMessage": "yarn i18n:download && bun scripts/i18n/syncDefaultMessage/index.ts scripts/i18n/translations/en.json '{apps,packages}/**/*.{tsx,ts}' && yarn format --force"
|
||||
},
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
@@ -41,6 +43,7 @@
|
||||
"husky": "^9.1.7",
|
||||
"jiti": "^1.21.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"prettier": "^3.6.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"turbo": "^2.5.2"
|
||||
},
|
||||
|
||||
@@ -38,7 +38,7 @@ export function Promos({ booking }: PromosProps) {
|
||||
<Promo
|
||||
buttonText={intl.formatMessage({
|
||||
id: "bookingConfirmation.promos.buyAddonsButtonText",
|
||||
defaultMessage: "View and buy add-ons",
|
||||
defaultMessage: "View and buy extras",
|
||||
})}
|
||||
href={myStayURL}
|
||||
text={intl.formatMessage({
|
||||
|
||||
@@ -96,7 +96,7 @@ export default function FilterAndSortModal({
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: "common.tripAdvisorRating",
|
||||
defaultMessage: "TripAdvisor rating",
|
||||
defaultMessage: "Tripadvisor rating",
|
||||
}),
|
||||
value: SortOrder.TripAdvisorRating,
|
||||
},
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function HotelSorter({ discreet }: HotelSorterProps) {
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
id: "common.tripAdvisorRating",
|
||||
defaultMessage: "TripAdvisor rating",
|
||||
defaultMessage: "Tripadvisor rating",
|
||||
}),
|
||||
value: SortOrder.TripAdvisorRating,
|
||||
},
|
||||
|
||||
@@ -63,7 +63,8 @@ export default function BreakfastAccordionItem({
|
||||
<p>
|
||||
{intl.formatMessage({
|
||||
id: "breakfastAccordion.allDayBreakfast",
|
||||
defaultMessage: "All-day breakfast",
|
||||
defaultMessage:
|
||||
"Grab our Brekkie Deal for 69 SEK – coffee & sandwich, available 6 AM–noon.",
|
||||
})}
|
||||
</p>
|
||||
</Typography>
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"lint": "eslint . --max-warnings 0 && tsc --noEmit",
|
||||
"lint:fix": "eslint . --fix && tsc --noEmit",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
"test:watch": "vitest"
|
||||
"test:watch": "vitest",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"exports": {
|
||||
"./BookingFlowConfig": "./lib/bookingFlowConfig/bookingFlowConfig.tsx",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
tabWidth: 2,
|
||||
endOfLine: "lf",
|
||||
}
|
||||
14
packages/booking-flow/prettier.config.js
Normal file
14
packages/booking-flow/prettier.config.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @see https://prettier.io/docs/configuration
|
||||
* @type {import("prettier").Config}
|
||||
*/
|
||||
const config = {
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
tabWidth: 2,
|
||||
endOfLine: "lf",
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -8,7 +8,8 @@
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "eslint . --max-warnings 0 && tsc --noEmit"
|
||||
"lint": "eslint . --max-warnings 0 && tsc --noEmit",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"exports": {
|
||||
"./polyfills": "./polyfills/index.ts",
|
||||
|
||||
@@ -269,7 +269,6 @@
|
||||
"lint-staged": "^15.5.2",
|
||||
"motion": "^12.10.0",
|
||||
"playwright": "^1.54.2",
|
||||
"prettier": "^3.5.3",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"rollup": "^4.40.2",
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"check-types": "tsc --noEmit",
|
||||
"lint": "eslint . --max-warnings 0 && tsc --noEmit"
|
||||
"lint": "eslint . --max-warnings 0 && tsc --noEmit",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"exports": {
|
||||
"./*": "./lib/*.ts",
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"lint": "eslint . --max-warnings 0 && tsc --noEmit",
|
||||
"lint:fix": "eslint . --fix && tsc --noEmit",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
"test:watch": "vitest",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"exports": {
|
||||
".": "./lib/index.ts",
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
tabWidth: 2,
|
||||
endOfLine: "lf",
|
||||
};
|
||||
14
packages/trpc/prettier.config.js
Normal file
14
packages/trpc/prettier.config.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @see https://prettier.io/docs/configuration
|
||||
* @type {import("prettier").Config}
|
||||
*/
|
||||
const config = {
|
||||
semi: false,
|
||||
trailingComma: "es5",
|
||||
singleQuote: false,
|
||||
printWidth: 80,
|
||||
tabWidth: 2,
|
||||
endOfLine: "lf",
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -31,8 +31,11 @@ Examples:
|
||||
const isDryRun = args.includes("--dry-run");
|
||||
const globPattern = args[1];
|
||||
// Find all component files
|
||||
const componentFiles = glob.sync(globPattern);
|
||||
|
||||
const componentFiles = glob.sync(globPattern);
|
||||
console.log(
|
||||
`Found ${componentFiles.length} files to sync using ${globPattern}`
|
||||
);
|
||||
let filesUpdated = 0;
|
||||
|
||||
for (const filePath of componentFiles) {
|
||||
|
||||
@@ -74,9 +74,32 @@ describe("syncFile", () => {
|
||||
});
|
||||
|
||||
expect(fsMock.readFileSync).toHaveBeenCalledWith("file.ts", "utf-8");
|
||||
expect(fsMock.writeFileSync).toHaveBeenCalled();
|
||||
// expect(fsMock.writeFileSync).toHaveBeenCalled();
|
||||
expect(result).toEqual(createMockComponent("myKey", "old message"));
|
||||
});
|
||||
|
||||
it("updates complex components with replacements", async () => {
|
||||
const fsMock = (await import("fs")) as any;
|
||||
|
||||
fsMock.existsSync.mockReturnValue(true);
|
||||
fsMock.readFileSync.mockReturnValue(
|
||||
createComplexMockComponent(
|
||||
"complexKey",
|
||||
"Yes, I accept the general <termsAndConditionsLink>Booking & Cancellation Terms</termsAndConditionsLink>, and understand that Scandic will process my personal data in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>."
|
||||
)
|
||||
);
|
||||
|
||||
const { syncFile } = await import("./syncFile");
|
||||
|
||||
const { fileContent: result } = syncFile({
|
||||
path: "file.ts",
|
||||
translations: { complexKey: "replace this text" },
|
||||
});
|
||||
|
||||
expect(fsMock.readFileSync).toHaveBeenCalledWith("file.ts", "utf-8");
|
||||
// expect(fsMock.writeFileSync).toHaveBeenCalled();
|
||||
expect(result).toContain("replace this text");
|
||||
});
|
||||
});
|
||||
|
||||
function createMockComponent(translationId: string, defaultMessage: string) {
|
||||
@@ -90,3 +113,25 @@ function createMockComponent(translationId: string, defaultMessage: string) {
|
||||
return <div>{message}</div>;
|
||||
}`;
|
||||
}
|
||||
|
||||
function createComplexMockComponent(
|
||||
translationId: string,
|
||||
defaultMessage: string
|
||||
) {
|
||||
return `export function TestComponent() {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<div>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "${translationId}",
|
||||
defaultMessage: "${defaultMessage}",
|
||||
},
|
||||
{
|
||||
replacement: (str) => <a href="#">{str}</a>,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}`;
|
||||
}
|
||||
|
||||
@@ -99,4 +99,38 @@ describe("syncIntlFormatMessage", () => {
|
||||
fileContent,
|
||||
});
|
||||
});
|
||||
|
||||
it("handles formatMessage with replacements", () => {
|
||||
const fileContent =
|
||||
'intl.formatMessage({ id: "myKey", defaultMessage: "<stuff>old message</stuff>" }, { stuff: (str) => <span>{str}</span> } })';
|
||||
expect(
|
||||
syncIntlFormatMessage({
|
||||
fileContent,
|
||||
translations: {
|
||||
myKey: "new message",
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
updated: true,
|
||||
fileContent:
|
||||
'intl.formatMessage({ id: "myKey", defaultMessage: "new message" }, { stuff: (str) => <span>{str}</span> } })',
|
||||
});
|
||||
});
|
||||
|
||||
it("handles formatMessage with replacements", () => {
|
||||
const fileContent =
|
||||
'intl.formatMessage({ id: "myKey", defaultMessage: "<stuff>old message</stuff>" }, { stuff: (str) => <span>{str}</span> } })';
|
||||
expect(
|
||||
syncIntlFormatMessage({
|
||||
fileContent,
|
||||
translations: {
|
||||
myKey: "<stuff>new message</stuff>",
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
updated: true,
|
||||
fileContent:
|
||||
'intl.formatMessage({ id: "myKey", defaultMessage: "<stuff>new message</stuff>" }, { stuff: (str) => <span>{str}</span> } })',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,15 +11,17 @@ export function syncIntlFormatMessage({
|
||||
for (const [messageId, messageValue] of entries) {
|
||||
const escapedId = messageId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
|
||||
// Find intl.formatMessage({...}) blocks that contain the specific id
|
||||
// Find intl.formatMessage({...}) or intl.formatMessage({...}, secondArg) blocks that contain the specific id
|
||||
const outerRegex = new RegExp(
|
||||
`intl\\.formatMessage\\(\\s*\\{([^}]*?\\bid\\s*:\\s*['"]${escapedId}['"][^}]*?)\\}\\s*\\)`,
|
||||
// group 1 = inner object content (without surrounding braces)
|
||||
// group 2 = optional second argument (anything until the closing parenthesis, non-greedy)
|
||||
`intl\\.formatMessage\\(\\s*\\{([^}]*?\\bid\\s*:\\s*['"]${escapedId}['"][^}]*?)\\}\\s*(?:,\\s*([^)]*?))?\\s*\\)`,
|
||||
"gs"
|
||||
);
|
||||
|
||||
fileContent = fileContent.replace(
|
||||
outerRegex,
|
||||
(fullMatch, innerObject) => {
|
||||
(fullMatch: string, innerObject: string, secondArg?: string) => {
|
||||
// Find defaultMessage: '...' or "..."
|
||||
const dmRegex =
|
||||
/defaultMessage\s*:\s*(['"])((?:\\.|[\s\S])*?)\1/;
|
||||
@@ -38,7 +40,9 @@ export function syncIntlFormatMessage({
|
||||
);
|
||||
|
||||
updated = true;
|
||||
return `intl.formatMessage({${newInner}})`;
|
||||
// Preserve secondArg if present
|
||||
const secondArgPart = secondArg ? `, ${secondArg}` : "";
|
||||
return `intl.formatMessage({${newInner}}${secondArgPart})`;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export function TestComponent() {
|
||||
return <div>Test</div>;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export function TestComponent() {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<div>{intl.formatMessage({ id: "myKey", defaultMessage: "Test" })}</div>
|
||||
);
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
},
|
||||
"lint": { "dependsOn": [] },
|
||||
"test": {},
|
||||
"check-types": {}
|
||||
"check-types": {},
|
||||
"format": { "dependsOn": [] }
|
||||
}
|
||||
}
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@@ -5987,7 +5987,6 @@ __metadata:
|
||||
lint-staged: "npm:^15.5.2"
|
||||
motion: "npm:^12.10.0"
|
||||
playwright: "npm:^1.54.2"
|
||||
prettier: "npm:^3.5.3"
|
||||
react: "npm:^19.1.0"
|
||||
react-dom: "npm:^19.1.0"
|
||||
rollup: "npm:^4.40.2"
|
||||
@@ -6084,7 +6083,6 @@ __metadata:
|
||||
ioredis: "npm:^5.6.1"
|
||||
pino: "npm:^9.6.0"
|
||||
pino-pretty: "npm:^13.0.0"
|
||||
prettier: "npm:^3.5.3"
|
||||
typescript: "npm:^5.7.2"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -6192,7 +6190,6 @@ __metadata:
|
||||
netlify-plugin-cypress: "npm:^2.2.1"
|
||||
next: "npm:15.3.4"
|
||||
next-auth: "npm:5.0.0-beta.29"
|
||||
prettier: "npm:^3.5.3"
|
||||
react: "npm:19.1.0"
|
||||
react-aria-components: "npm:^1.8.0"
|
||||
react-day-picker: "npm:^9.6.7"
|
||||
@@ -16714,12 +16711,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prettier@npm:^3.5.3":
|
||||
version: 3.5.3
|
||||
resolution: "prettier@npm:3.5.3"
|
||||
"prettier@npm:^3.6.2":
|
||||
version: 3.6.2
|
||||
resolution: "prettier@npm:3.6.2"
|
||||
bin:
|
||||
prettier: bin/prettier.cjs
|
||||
checksum: 10c0/3880cb90b9dc0635819ab52ff571518c35bd7f15a6e80a2054c05dbc8a3aa6e74f135519e91197de63705bcb38388ded7e7230e2178432a1468005406238b877
|
||||
checksum: 10c0/488cb2f2b99ec13da1e50074912870217c11edaddedeadc649b1244c749d15ba94e846423d062e2c4c9ae683e2d65f754de28889ba06e697ac4f988d44f45812
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -18012,6 +18009,7 @@ __metadata:
|
||||
husky: "npm:^9.1.7"
|
||||
jiti: "npm:^1.21.0"
|
||||
lint-staged: "npm:^15.2.2"
|
||||
prettier: "npm:^3.6.2"
|
||||
ts-node: "npm:^10.9.2"
|
||||
turbo: "npm:^2.5.2"
|
||||
languageName: unknown
|
||||
|
||||
Reference in New Issue
Block a user