@dsi18n/airtable-sync 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -0
- package/dist/cli.js +537 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +530 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/core/node.ts","../src/core/fetch.ts","../src/core/normalize.ts","../src/core/validate.ts","../src/core/run-build.ts","../src/load-project-env.ts","../src/index.ts"],"names":["mkdirNode","writeFileNode","resolve","DEFAULT_RATE_LIMIT_DELAY_MS","sleep","extractPlaceholders","dirname","path","loadEnv"],"mappings":"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","file":"index.js","sourcesContent":["import type { BuildConfig } from \"./core/run-build.js\";\n\nexport function parseBooleanEnv(value: string | undefined): boolean {\n return value === \"1\" || value === \"true\";\n}\n\nexport function parseNumberEnv(value: string | undefined): number | undefined {\n if (!value) {\n return undefined;\n }\n\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\n/** Build config from process environment (CLI / CI). */\nexport function loadBuildConfigFromEnv(): BuildConfig {\n return {\n airtableApiKey: process.env.AIRTABLE_API_KEY,\n baseId: process.env.AIRTABLE_BASE_ID,\n table: process.env.AIRTABLE_TABLE,\n tablesCsv: process.env.I18N_TABLES,\n outDir: process.env.I18N_OUT_DIR,\n version: process.env.I18N_VERSION,\n keyField: process.env.I18N_KEY_FIELD,\n defaultLocale: process.env.I18N_DEFAULT_LOCALE,\n localesCsv: process.env.I18N_LOCALES,\n typesOutFile: process.env.I18N_TYPES_OUT_FILE,\n debug: parseBooleanEnv(process.env.I18N_DEBUG),\n rateLimitDelayMs: parseNumberEnv(process.env.I18N_RATE_LIMIT_DELAY_MS),\n };\n}\n","// @ts-nocheck\nimport { mkdir as mkdirNode, writeFile as writeFileNode } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport async function mkdir(pathValue: string): Promise<void> {\n await mkdirNode(pathValue, { recursive: true });\n}\n\nexport async function writeUtf8File(\n pathValue: string,\n content: string,\n): Promise<void> {\n await writeFileNode(pathValue, content, \"utf8\");\n}\n\nexport function joinPath(...parts: string[]): string {\n return path.join(...parts);\n}\n\nexport function dirname(pathValue: string): string {\n return path.dirname(pathValue);\n}\n","// @ts-nocheck\n\ntype AirtableFieldValue = string | number | boolean | null | undefined;\n\nexport type AirtableRecord = {\n id: string;\n fields: Record<string, AirtableFieldValue>;\n};\n\ntype AirtableResponse = {\n records: AirtableRecord[];\n offset?: string;\n};\n\nexport type FetchFn = typeof fetch;\n\nexport type FetchParams = {\n apiKey?: string;\n baseId: string;\n table: string;\n fetchFn?: FetchFn;\n debug?: boolean;\n rateLimitDelayMs?: number;\n};\n\ntype AirtableMetaResponse = {\n tables: Array<{\n id: string;\n name: string;\n }>;\n};\n\nconst RETRY_ATTEMPTS = 3;\nconst RETRY_BASE_DELAY_MS = 300;\nconst DEFAULT_RATE_LIMIT_DELAY_MS = 200;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nfunction isRetryableStatus(status: number): boolean {\n return status === 429 || status >= 500;\n}\n\nasync function fetchWithRetry(\n url: URL,\n init: RequestInit,\n fetchFn: FetchFn,\n debug?: boolean,\n): Promise<Response> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < RETRY_ATTEMPTS; attempt += 1) {\n try {\n if (debug) {\n console.log(`[fetch] attempt=${attempt + 1} url=${url.toString()}`);\n }\n const response = await fetchFn(url, init);\n if (!isRetryableStatus(response.status)) {\n return response;\n }\n\n if (debug) {\n console.warn(\n `[fetch] retryable_status=${response.status} attempt=${attempt + 1} url=${url.toString()}`,\n );\n }\n const body = await response.text();\n lastError = new Error(\n `Retryable Airtable response (${response.status}): ${response.statusText}\\n${body}`,\n );\n } catch (error: unknown) {\n if (debug) {\n console.warn(\n `[fetch] network_error attempt=${attempt + 1} url=${url.toString()}`,\n );\n }\n lastError =\n error instanceof Error ? error : new Error(\"Unknown fetch error\");\n }\n\n if (attempt < RETRY_ATTEMPTS - 1) {\n await sleep(RETRY_BASE_DELAY_MS * (attempt + 1));\n }\n }\n\n throw lastError ?? new Error(\"Fetch failed after retries\");\n}\n\nasync function fetchAirtablePage(params: {\n apiKey?: string;\n baseId: string;\n table: string;\n offset?: string;\n fetchFn?: FetchFn;\n debug?: boolean;\n}): Promise<AirtableResponse> {\n const encodedTable = encodeURIComponent(params.table);\n const url = new URL(\n `https://api.airtable.com/v0/${params.baseId}/${encodedTable}`,\n );\n\n if (params.offset) {\n url.searchParams.set(\"offset\", params.offset);\n }\n\n const headers: Record<string, string> = {};\n if (params.apiKey) {\n headers.Authorization = `Bearer ${params.apiKey}`;\n }\n\n const fetchFn = params.fetchFn ?? fetch;\n if (params.debug) {\n const offsetValue = params.offset ?? \"none\";\n console.log(`[fetch] table=${params.table} offset=${offsetValue}`);\n }\n const response = await fetchWithRetry(url, { headers }, fetchFn, params.debug);\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Airtable request failed (${response.status}): ${response.statusText}\\n${body}`,\n );\n }\n\n return (await response.json()) as AirtableResponse;\n}\n\nexport async function fetchAllRecords(\n params: FetchParams,\n): Promise<AirtableRecord[]> {\n const records: AirtableRecord[] = [];\n let offset: string | undefined;\n const rateLimitDelayMs = params.rateLimitDelayMs ?? DEFAULT_RATE_LIMIT_DELAY_MS;\n\n do {\n const page = await fetchAirtablePage({\n apiKey: params.apiKey,\n baseId: params.baseId,\n table: params.table,\n offset,\n fetchFn: params.fetchFn,\n debug: params.debug,\n });\n records.push(...page.records);\n offset = page.offset;\n if (offset) {\n await sleep(rateLimitDelayMs);\n }\n } while (offset);\n\n return records;\n}\n\nexport async function listBaseTables(params: {\n apiKey: string;\n baseId: string;\n fetchFn?: FetchFn;\n debug?: boolean;\n}): Promise<string[]> {\n // Airtable metadata endpoint requires extra scopes in many workspaces.\n // Keep this isolated so callers can fallback to explicit table lists.\n const url = new URL(`https://api.airtable.com/v0/meta/bases/${params.baseId}/tables`);\n const fetchFn = params.fetchFn ?? fetch;\n const response = await fetchWithRetry(\n url,\n {\n headers: {\n Authorization: `Bearer ${params.apiKey}`,\n },\n },\n fetchFn,\n params.debug,\n );\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n `Airtable metadata request failed (${response.status}): ${response.statusText}\\n${body}`,\n );\n }\n\n const json = (await response.json()) as AirtableMetaResponse;\n return json.tables.map((table) => table.name);\n}\n","export type NormalizedEntry = {\n key: string;\n translations: Partial<Record<string, string>>;\n};\n\ntype AirtableFieldValue = string | number | boolean | null | undefined;\n\nexport type AirtableRecord = {\n id: string;\n fields: Record<string, AirtableFieldValue>;\n};\n\nexport function parseLocalesFromEnv(value: string | undefined): string[] {\n if (!value) {\n return [];\n }\n\n return value\n .split(\",\")\n .map((locale) => locale.trim())\n .filter(Boolean);\n}\n\nexport function inferLocalesFromRecords(\n records: AirtableRecord[],\n keyField: string,\n): string[] {\n const locales = new Set<string>();\n\n for (const record of records) {\n const fields = Object.keys(record.fields);\n for (const fieldName of fields) {\n if (fieldName === keyField) {\n continue;\n }\n\n const value = record.fields[fieldName];\n if (typeof value === \"string\") {\n locales.add(fieldName);\n }\n }\n }\n\n return [...locales].sort();\n}\n\nexport function normalizeRecords(\n records: AirtableRecord[],\n locales: string[],\n keyField: string,\n options?: {\n debug?: boolean;\n },\n): NormalizedEntry[] {\n const normalizedEntries: Array<NormalizedEntry | null> = records\n .map((record) => {\n const keyValue = record.fields[keyField];\n if (typeof keyValue !== \"string\" || keyValue.trim().length === 0) {\n if (options?.debug) {\n console.warn(`Skipping record ${record.id}: invalid key`);\n }\n return null;\n }\n\n const key = keyValue.trim();\n const translations: Partial<Record<string, string>> = Object.fromEntries(\n locales\n .map((locale) => {\n const value = record.fields[locale];\n if (typeof value !== \"string\") {\n return null;\n }\n\n const trimmed = value.trim();\n return trimmed.length > 0 ? [locale, trimmed] : null;\n })\n .filter((item): item is [string, string] => item !== null),\n );\n\n return { key, translations };\n });\n\n return normalizedEntries.filter(\n (entry): entry is NormalizedEntry => entry !== null,\n );\n}\n","import type { NormalizedEntry } from \"./normalize.js\";\n\nexport type ValidationIssue =\n | {\n type: \"missing_translation\";\n key: string;\n locale: string;\n }\n | {\n type: \"missing_placeholder\";\n key: string;\n locale: string;\n baseLocale: string;\n placeholder: string;\n }\n | {\n type: \"extra_placeholder\";\n key: string;\n locale: string;\n baseLocale: string;\n placeholder: string;\n };\n\ntype PlaceholderIssue = Extract<\n ValidationIssue,\n { type: \"missing_placeholder\" | \"extra_placeholder\" }\n>;\n\nfunction extractPlaceholders(text: string): string[] {\n const matches = text.match(/\\{([a-zA-Z0-9_]+)\\}/g) ?? [];\n const cleaned = matches.map((token) => token.replace(/[{}]/g, \"\"));\n return [...new Set(cleaned)];\n}\n\nexport function detectDuplicateKeys(entries: NormalizedEntry[]): string[] {\n const seen = new Set<string>();\n const duplicates = new Set<string>();\n\n for (const entry of entries) {\n if (seen.has(entry.key)) {\n duplicates.add(entry.key);\n continue;\n }\n\n seen.add(entry.key);\n }\n\n return [...duplicates].sort();\n}\n\nexport function validateMissingTranslations(\n entries: NormalizedEntry[],\n locales: string[],\n): ValidationIssue[] {\n return entries\n .flatMap((entry) =>\n locales.map((locale) => ({\n key: entry.key,\n locale,\n value: entry.translations[locale],\n })),\n )\n .filter((item) => item.value == null || item.value === \"\")\n .map((item) => ({\n type: \"missing_translation\" as const,\n key: item.key,\n locale: item.locale,\n }));\n}\n\nexport function validatePlaceholdersConsistency(\n entries: NormalizedEntry[],\n locales: string[],\n baseLocale: string,\n): ValidationIssue[] {\n const otherLocales = locales.filter((locale) => locale !== baseLocale);\n\n return entries.flatMap((entry) => {\n const baseTranslation = entry.translations[baseLocale];\n if (!baseTranslation) {\n return [];\n }\n const basePlaceholders = extractPlaceholders(baseTranslation);\n\n return otherLocales\n .map((locale) => {\n const translation = entry.translations[locale];\n if (!translation) {\n return null;\n }\n const placeholders = extractPlaceholders(translation);\n const missingIssues = basePlaceholders\n .filter((placeholder) => !placeholders.includes(placeholder))\n .map((placeholder) => ({\n type: \"missing_placeholder\" as const,\n key: entry.key,\n locale,\n baseLocale,\n placeholder,\n }));\n const extraIssues = placeholders\n .filter((placeholder) => !basePlaceholders.includes(placeholder))\n .map((placeholder) => ({\n type: \"extra_placeholder\" as const,\n key: entry.key,\n locale,\n baseLocale,\n placeholder,\n }));\n\n return [...missingIssues, ...extraIssues];\n })\n .flat()\n .filter((item): item is PlaceholderIssue => item !== null);\n });\n}\n\nexport function validateDefaultLocale(\n locales: string[],\n defaultLocale: string,\n): void {\n if (!locales.includes(defaultLocale)) {\n throw new Error(\n `Default locale \"${defaultLocale}\" is not present in locales: ${locales.join(\", \")}`,\n );\n }\n}\n","import { dirname, joinPath, mkdir, writeUtf8File } from \"./node.js\";\nimport {\n fetchAllRecords,\n listBaseTables,\n type AirtableRecord,\n type FetchFn,\n} from \"./fetch.js\";\nimport {\n inferLocalesFromRecords,\n normalizeRecords,\n parseLocalesFromEnv,\n type NormalizedEntry,\n} from \"./normalize.js\";\nimport {\n detectDuplicateKeys,\n type ValidationIssue,\n validateDefaultLocale,\n validateMissingTranslations,\n validatePlaceholdersConsistency,\n} from \"./validate.js\";\nimport type {\n LocaleDictionaryFile,\n ManifestFile,\n} from \"@dsi18n/schema/contracts\";\n\n/** Relative to each `<locale>.json` inside the default `packages/locales/` layout. */\nconst DICTIONARY_SCHEMA_REF = \"../schema/dictionary.schema.json\";\nconst MANIFEST_SCHEMA_REF = \"../schema/manifest.schema.json\";\n\nexport type BuildConfig = {\n airtableApiKey?: string;\n baseId?: string;\n table?: string;\n tablesCsv?: string;\n outDir?: string;\n version?: string;\n keyField?: string;\n defaultLocale?: string;\n localesCsv?: string;\n typesOutFile?: string;\n fetchFn?: FetchFn;\n debug?: boolean;\n rateLimitDelayMs?: number;\n};\n\nconst DEFAULT_OUT_DIR = \"packages/locales\";\nconst DEFAULT_VERSION = \"0.1.0\";\nconst DEFAULT_KEY_FIELD = \"key\";\nconst DEFAULT_LOCALE = \"en\";\nconst DEFAULT_TYPES_OUT_FILE =\n \"packages/i18n-core/src/generated/translation-keys.ts\";\nconst DEFAULT_RATE_LIMIT_DELAY_MS = 200;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nfunction formatValidationIssue(issue: ValidationIssue): string {\n if (issue.type === \"missing_translation\") {\n return `Missing ${issue.locale} for key: ${issue.key}`;\n }\n\n if (issue.type === \"missing_placeholder\") {\n return `Missing placeholder \"${issue.placeholder}\" for key \"${issue.key}\" (${issue.locale}) using base locale \"${issue.baseLocale}\"`;\n }\n\n return `Extra placeholder \"${issue.placeholder}\" for key \"${issue.key}\" (${issue.locale}) using base locale \"${issue.baseLocale}\"`;\n}\n\nfunction parseTablesFromEnv(value: string | undefined): string[] {\n if (!value) {\n return [];\n }\n\n return value\n .split(\",\")\n .map((table) => table.trim())\n .filter(Boolean);\n}\n\nfunction extractPlaceholders(text: string): string[] {\n const matches = text.match(/\\{([a-zA-Z0-9_]+)\\}/g) ?? [];\n const cleaned = matches.map((token) => token.replace(/[{}]/g, \"\"));\n return [...new Set(cleaned)];\n}\n\nfunction buildDictionaries(\n entries: NormalizedEntry[],\n locales: string[],\n version: string,\n): Record<string, LocaleDictionaryFile> {\n const dictionaries = Object.fromEntries(\n locales.map((locale) => [\n locale,\n {\n locale,\n version,\n entries: {},\n },\n ]),\n ) as Record<string, LocaleDictionaryFile>;\n\n entries.forEach((entry) => {\n locales.forEach((locale) => {\n const value = entry.translations[locale];\n if (!value) {\n return;\n }\n\n dictionaries[locale].entries[entry.key] = {\n value,\n placeholders: extractPlaceholders(value),\n };\n });\n });\n\n return dictionaries;\n}\n\nasync function writeOutputs(params: {\n outDir: string;\n dictionaries: Record<string, LocaleDictionaryFile>;\n manifest: ManifestFile;\n}): Promise<void> {\n await mkdir(params.outDir);\n\n const dictionaryEntries = Object.entries(params.dictionaries);\n await Promise.all(\n dictionaryEntries.map(async ([locale, dictionary]) => {\n const filePath = joinPath(params.outDir, `${locale}.json`);\n const payload: LocaleDictionaryFile = {\n $schema: DICTIONARY_SCHEMA_REF,\n locale: dictionary.locale,\n version: dictionary.version,\n entries: dictionary.entries,\n };\n await writeUtf8File(filePath, `${JSON.stringify(payload, null, 2)}\\n`);\n }),\n );\n\n const manifestPath = joinPath(params.outDir, \"manifest.json\");\n const manifestPayload: ManifestFile = {\n ...params.manifest,\n $schema: MANIFEST_SCHEMA_REF,\n };\n await writeUtf8File(\n manifestPath,\n `${JSON.stringify(manifestPayload, null, 2)}\\n`,\n );\n}\n\nfunction buildTypeDefinitions(keys: string[]): string {\n const sortedKeys = [...keys].sort();\n\n const keyUnion =\n sortedKeys.length > 0\n ? sortedKeys.map((key) => ` | \"${key}\"`).join(\"\\n\")\n : ' | \"__no_translation_keys__\"';\n\n return [\n \"// AUTO-GENERATED FILE. DO NOT EDIT.\",\n \"// Generated by packages/airtable-sync/src/core/run-build.ts\",\n \"\",\n \"export type TranslationKey =\",\n keyUnion + \";\",\n \"\",\n \"export const translationKeys = [\",\n ...sortedKeys.map((key) => ` \"${key}\",`),\n \"] as const;\",\n \"\",\n ].join(\"\\n\");\n}\n\nasync function writeTypeDefinitions(\n typeOutputPath: string,\n entries: NormalizedEntry[],\n): Promise<void> {\n const keys = entries.map((entry) => entry.key);\n const typeContent = buildTypeDefinitions(keys);\n\n await mkdir(dirname(typeOutputPath));\n await writeUtf8File(typeOutputPath, typeContent);\n}\n\nexport async function runBuild(config: BuildConfig): Promise<void> {\n const baseId = config.baseId;\n const airtableApiKey = config.airtableApiKey;\n\n if (!baseId) {\n throw new Error(\"Missing required config: baseId\");\n }\n\n if (!airtableApiKey) {\n throw new Error(\"Missing airtableApiKey\");\n }\n\n const outDir = config.outDir ?? DEFAULT_OUT_DIR;\n const version = config.version ?? DEFAULT_VERSION;\n const keyField = config.keyField ?? DEFAULT_KEY_FIELD;\n const defaultLocale = config.defaultLocale ?? DEFAULT_LOCALE;\n const typesOutFile = config.typesOutFile ?? DEFAULT_TYPES_OUT_FILE;\n const rateLimitDelayMs =\n config.rateLimitDelayMs ?? DEFAULT_RATE_LIMIT_DELAY_MS;\n\n const tablesFromCsv = parseTablesFromEnv(config.tablesCsv);\n const selectedTables =\n config.table && config.table.trim().length > 0\n ? [config.table.trim()]\n : tablesFromCsv;\n const tables =\n selectedTables.length > 0\n ? selectedTables\n : await (async () => {\n if (!config.airtableApiKey) {\n throw new Error(\n \"AIRTABLE_TABLE or I18N_TABLES is required unless AIRTABLE_API_KEY can access Airtable metadata.\",\n );\n }\n\n // If this metadata call fails due to restricted scopes, pass\n // AIRTABLE_TABLE or I18N_TABLES to skip metadata discovery.\n return listBaseTables({\n apiKey: airtableApiKey,\n baseId,\n fetchFn: config.fetchFn,\n debug: config.debug,\n });\n })();\n\n if (tables.length === 0) {\n throw new Error(\"No tables found to read from Airtable base.\");\n }\n\n const records = await tables.reduce<Promise<AirtableRecord[]>>(\n async (\n accPromise: Promise<AirtableRecord[]>,\n table: string,\n index: number,\n ) => {\n const acc = await accPromise;\n if (index > 0) {\n await sleep(rateLimitDelayMs);\n }\n\n const tableRecords = await fetchAllRecords({\n apiKey: airtableApiKey,\n baseId,\n table,\n fetchFn: config.fetchFn,\n debug: config.debug,\n rateLimitDelayMs,\n });\n\n return [...acc, ...tableRecords];\n },\n Promise.resolve([]),\n );\n const configuredLocales = parseLocalesFromEnv(config.localesCsv);\n const inferredLocales = inferLocalesFromRecords(records, keyField);\n const locales =\n configuredLocales.length > 0 ? configuredLocales : inferredLocales;\n\n if (locales.length === 0) {\n throw new Error(\n \"No locales found. Set I18N_LOCALES or provide language columns in Airtable.\",\n );\n }\n\n const normalized = normalizeRecords(records, locales, keyField, {\n debug: config.debug,\n });\n const duplicateKeys = detectDuplicateKeys(normalized);\n if (duplicateKeys.length > 0) {\n throw new Error(\n `Duplicate translation keys found: ${duplicateKeys.join(\", \")}`,\n );\n }\n\n const missingTranslations = validateMissingTranslations(normalized, locales);\n if (missingTranslations.length > 0) {\n console.warn(\"Missing translations:\");\n missingTranslations.forEach((issue: ValidationIssue) =>\n console.warn(` - ${formatValidationIssue(issue)}`),\n );\n }\n\n validateDefaultLocale(locales, defaultLocale);\n\n const placeholderMismatches = validatePlaceholdersConsistency(\n normalized,\n locales,\n defaultLocale,\n );\n if (placeholderMismatches.length > 0) {\n console.warn(\"Placeholder consistency issues:\");\n placeholderMismatches.forEach((issue: ValidationIssue) =>\n console.warn(` - ${formatValidationIssue(issue)}`),\n );\n }\n\n const dictionaries = buildDictionaries(normalized, locales, version);\n\n const manifest: ManifestFile = {\n version,\n defaultLocale,\n fallbackChain: [defaultLocale],\n locales,\n generatedAt: new Date().toISOString(),\n source: {\n type: \"airtable\",\n baseId,\n tables,\n },\n };\n\n await writeOutputs({\n outDir,\n dictionaries,\n manifest,\n });\n await writeTypeDefinitions(typesOutFile, normalized);\n\n console.log(\n `Generated locales: ${locales.join(\", \")} | records: ${normalized.length} | out: ${outDir} | types: ${typesOutFile}`,\n );\n}\n","import { existsSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { config as loadEnv } from \"dotenv\";\n\n/**\n * Loads `.env` from `startDir` and each parent directory (up to 6 levels).\n * Root `.env` is applied first; nested `.env` files override those keys.\n *\n * @param startDir - Directory to start from (default: `process.cwd()`).\n */\nexport function loadProjectEnv(startDir = process.cwd()): void {\n const chain: string[] = [];\n let dir = resolve(startDir);\n for (let i = 0; i < 6; i += 1) {\n chain.unshift(dir);\n const parent = dirname(dir);\n if (parent === dir) {\n break;\n }\n dir = parent;\n }\n for (const folder of chain) {\n const path = join(folder, \".env\");\n if (existsSync(path)) {\n loadEnv({ path });\n }\n }\n}\n","import { loadBuildConfigFromEnv } from \"./config.js\";\nimport { runBuild } from \"./core/run-build.js\";\n\nexport type { BuildConfig } from \"./core/run-build.js\";\nexport { runBuild } from \"./core/run-build.js\";\nexport {\n loadBuildConfigFromEnv,\n parseBooleanEnv,\n parseNumberEnv,\n} from \"./config.js\";\nexport { loadProjectEnv } from \"./load-project-env.js\";\n\nexport async function main(): Promise<void> {\n await runBuild(loadBuildConfigFromEnv());\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dsi18n/airtable-sync",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Build-time Airtable → dsi18n locale JSON (manifest + per-locale dictionaries).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"bin": {
|
|
15
|
+
"dsi18n-sync": "./dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup && rollup -c rollup.dts.config.mjs",
|
|
26
|
+
"prepack": "npm run build",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"dotenv": "^16.5.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@dsi18n/schema": "*",
|
|
41
|
+
"@rollup/plugin-alias": "^5.1.1",
|
|
42
|
+
"@types/node": "^22.14.0",
|
|
43
|
+
"rollup": "^4.60.3",
|
|
44
|
+
"rollup-plugin-dts": "^6.4.1",
|
|
45
|
+
"tsup": "^8.4.0",
|
|
46
|
+
"typescript": "^5.8.3",
|
|
47
|
+
"vitest": "^3.1.2"
|
|
48
|
+
}
|
|
49
|
+
}
|