diff --git a/site/public/_routes.json b/site/public/_routes.json new file mode 100644 index 0000000..1575b1e --- /dev/null +++ b/site/public/_routes.json @@ -0,0 +1,5 @@ +{ + "version": 1, + "include": ["/utilities", "/utilities/", "/pt-br/utilities", "/pt-br/utilities/", "/libs/javascript/", "/pt-br/libs/javascript/"], + "exclude": [] +} diff --git a/site/public/_worker.js b/site/public/_worker.js new file mode 100644 index 0000000..90ca7e8 --- /dev/null +++ b/site/public/_worker.js @@ -0,0 +1,23 @@ +// Cloudflare Pages runs this only for the paths in _routes.json; every other request is a static +// file. The old JavaScript docs (docsify) kept a section in the query string: /utilities?id=isvalidcpf +// for a function, /pt-br/utilities?id=cep-e-endereço for a heading. A _redirects rule cannot read +// a query string, so this sends each to its utility's page, from /legacy-ids.json (see +// src/lib/legacy-ids.ts). Without a known id: /utilities goes to the library's page as before, and +// the library's page is served as is. +export default { + async fetch(request, env) { + const url = new URL(request.url); + const pt = url.pathname.startsWith('/pt-br/'); + const prefix = pt ? '/pt-br' : ''; + const id = url.searchParams.get('id'); + if (id) { + const res = await env.ASSETS.fetch(new URL('/legacy-ids.json', url)); + if (res.ok) { + const to = (await res.json())[pt ? 'pt-BR' : 'en'][id.toLowerCase()]; + if (to) return Response.redirect(new URL(prefix + to, url).toString(), 301); + } + } + if (/^(\/pt-br)?\/utilities\/?$/.test(url.pathname)) return Response.redirect(new URL(`${prefix}/libs/javascript/${url.search}`, url).toString(), 301); + return env.ASSETS.fetch(request); + }, +}; diff --git a/site/src/app/legacy-ids.json/route.ts b/site/src/app/legacy-ids.json/route.ts new file mode 100644 index 0000000..35e6990 --- /dev/null +++ b/site/src/app/legacy-ids.json/route.ts @@ -0,0 +1,9 @@ +// /legacy-ids.json: where each anchor of the old JavaScript docs is now (see lib/legacy-ids.ts). +// public/_worker.js reads it for the old URLs that carry an ?id=. +import { legacyIds } from '@/lib/legacy-ids'; + +export const dynamic = 'force-static'; + +export function GET() { + return Response.json(legacyIds()); +} diff --git a/site/src/lib/legacy-ids.ts b/site/src/lib/legacy-ids.ts new file mode 100644 index 0000000..35dbb81 --- /dev/null +++ b/site/src/lib/legacy-ids.ts @@ -0,0 +1,64 @@ +// Where the old JavaScript docs' anchors are now. That site (docsify) put a section in the query +// string: /utilities?id=isvalidcpf for a function, /utilities?id=cep-e-endereço for a heading. Those +// URLs reach public/_worker.js, which answers with a 301 to the utility's page from this map. +import { loadSpecs, loadStatus } from './data'; +import { type Locale, pick } from './i18n'; +import { slug } from './prose'; + +// The old page's section headings that are not a utility's title, in both languages. +const SECTIONS: Record = { + 'cep-and-address': 'cep', + 'cep-e-endereço': 'cep', + pix: 'pix-key', + 'nf-e-key': 'nfe-key', + 'chave-de-nf-e': 'nfe-key', + 'nfs-e-key': 'nfse-key', + 'chave-de-nfs-e': 'nfse-key', + pis: 'pis', + 'processo-jurídico': 'legal-process', + 'bank-accounts-and-banks': 'bank-account', + 'contas-bancárias-e-bancos': 'bank-account', + 'currency-numbers-and-dates-in-words': 'currency', + 'moeda-números-e-datas-por-extenso': 'currency', + 'states-and-municipalities': 'state', + 'estados-e-municípios': 'state', + 'holidays-and-business-days': 'date', + 'feriados-e-dias-úteis': 'date', + 'voter-id-título-de-eleitor': 'voter-id', + 'título-de-eleitor': 'voter-id', + 'certidão-civil-registry-certificate': 'certidao', + 'cei-cno-and-caepf': 'cei', + 'cei-cno-e-caepf': 'cei', + 'gtin-product-barcode': 'gtin', + 'gtin-código-de-barras-de-produto': 'gtin', + 'inscrição-estadual-ie': 'ie', + 'e-mail': 'email', + 'credit-card': 'credit-card', + 'cartão-de-crédito': 'credit-card', + 'professional-registration': 'registro-profissional', + 'registro-profissional': 'registro-profissional', + vin: 'vin', +}; + +/** Old anchor (lowercase) → the page, without the language prefix, in each language. */ +export function legacyIds() { + const status = loadStatus(); + const js = status?.libs?.javascript?.functions ?? {}; + const specs = loadSpecs() as any[]; + const ids = new Set(specs.map((s) => s.id)); + const out = {} as Record>; + for (const locale of ['en', 'pt-BR'] as Locale[]) { + const m: Record = {}; + for (const [key, id] of Object.entries(SECTIONS)) if (ids.has(id)) m[key] = `/utils/${id}/`; + for (const spec of specs) { + m[spec.id] ??= `/utils/${spec.id}/`; + for (const l of ['en', 'pt-BR'] as Locale[]) m[slug(pick(spec.title, l))] ??= `/utils/${spec.id}/`; + for (const op of spec.operations) { + const symbol = js[op.fnId]?.symbol; + if (symbol) m[symbol.toLowerCase()] = `/utils/${spec.id}/#${slug(pick(op.label, locale))}`; + } + } + out[locale] = m; + } + return out; +}