@formatjs/cli-lib 8.5.0 → 8.5.2
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/gts_extractor-Dc-C_f4R.js +14 -0
- package/gts_extractor-Dc-C_f4R.js.map +1 -0
- package/{src/hbs_extractor.js → hbs_extractor-Eiqkz4d7.js} +9 -8
- package/hbs_extractor-Eiqkz4d7.js.map +1 -0
- package/index.d.ts +144 -7
- package/index.js +633 -2
- package/index.js.map +1 -0
- package/package.json +5 -5
- package/parse_script-EspfGgzZ.js +93 -0
- package/parse_script-EspfGgzZ.js.map +1 -0
- package/{src/svelte_extractor.js → svelte_extractor-Fr1Z3JHX.js} +16 -37
- package/svelte_extractor-Fr1Z3JHX.js.map +1 -0
- package/vue_extractor-Btw05cfG.js +41 -0
- package/vue_extractor-Btw05cfG.js.map +1 -0
- package/main.d.ts +0 -1
- package/main.js +0 -3
- package/src/cli.d.ts +0 -2
- package/src/cli.js +0 -145
- package/src/compile.d.ts +0 -63
- package/src/compile.js +0 -95
- package/src/compile_folder.d.ts +0 -2
- package/src/compile_folder.js +0 -8
- package/src/console_utils.d.ts +0 -7
- package/src/console_utils.js +0 -62
- package/src/extract.d.ts +0 -87
- package/src/extract.js +0 -212
- package/src/formatters/crowdin.d.ts +0 -7
- package/src/formatters/crowdin.js +0 -21
- package/src/formatters/default.d.ts +0 -6
- package/src/formatters/default.js +0 -9
- package/src/formatters/index.d.ts +0 -9
- package/src/formatters/index.js +0 -31
- package/src/formatters/lokalise.d.ts +0 -9
- package/src/formatters/lokalise.js +0 -18
- package/src/formatters/simple.d.ts +0 -4
- package/src/formatters/simple.js +0 -8
- package/src/formatters/smartling.d.ts +0 -21
- package/src/formatters/smartling.js +0 -39
- package/src/formatters/transifex.d.ts +0 -9
- package/src/formatters/transifex.js +0 -18
- package/src/gts_extractor.d.ts +0 -1
- package/src/gts_extractor.js +0 -14
- package/src/hbs_extractor.d.ts +0 -1
- package/src/parse_script.d.ts +0 -7
- package/src/parse_script.js +0 -43
- package/src/pseudo_locale.d.ts +0 -22
- package/src/pseudo_locale.js +0 -231
- package/src/svelte_extractor.d.ts +0 -2
- package/src/verify/checkExtraKeys.d.ts +0 -1
- package/src/verify/checkExtraKeys.js +0 -32
- package/src/verify/checkMissingKeys.d.ts +0 -1
- package/src/verify/checkMissingKeys.js +0 -33
- package/src/verify/checkStructuralEquality.d.ts +0 -1
- package/src/verify/checkStructuralEquality.js +0 -71
- package/src/verify/index.d.ts +0 -13
- package/src/verify/index.js +0 -26
- package/src/vue_extractor.d.ts +0 -2
- package/src/vue_extractor.js +0 -58
package/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["format","compile","format","compile","format","compile","format","compile","format","compile","compile","defaultFormatter","transifex","smartling","simple","lokalise","crowdin","stringify"],"sources":["../formatters/crowdin.ts","../formatters/default.ts","../formatters/lokalise.ts","../formatters/simple.ts","../formatters/smartling.ts","../formatters/transifex.ts","../formatters/index.ts","../extract.ts","../pseudo_locale.ts","../compile.ts"],"sourcesContent":["import {\n type CompileFn,\n type FormatFn,\n} from '#packages/cli-lib/formatters/default.js'\n\nexport type CrowdinJson = Record<\n string,\n {\n message: string\n description?: string\n }\n>\n\nexport const format: FormatFn<CrowdinJson> = msgs => {\n const results: CrowdinJson = {}\n for (const [id, msg] of Object.entries(msgs)) {\n results[id] = {\n message: msg.defaultMessage!,\n description:\n typeof msg.description === 'string'\n ? msg.description\n : JSON.stringify(msg.description),\n }\n }\n return results\n}\n\nexport const compile: CompileFn<CrowdinJson> = msgs => {\n const results: Record<string, string> = {}\n for (const [id, msg] of Object.entries(msgs)) {\n if (id === 'smartling') {\n continue\n }\n results[id] = msg.message\n }\n return results\n}\n","import {type MessageDescriptor} from '@formatjs/ts-transformer'\nexport type FormatFn<T = Record<string, MessageDescriptor>> = (\n msgs: Record<string, MessageDescriptor>\n) => T\n\nexport type CompileFn<T = Record<string, MessageDescriptor>> = (\n msgs: T\n) => Record<string, string>\n\nexport type SerializeFn<T = Record<string, MessageDescriptor>> = (\n msgs: T\n) => string\n\nexport const format: FormatFn = msgs => msgs\n\nexport const compile: CompileFn = msgs => {\n const results: Record<string, string> = {}\n for (const k in msgs) {\n results[k] = msgs[k].defaultMessage!\n }\n return results\n}\n","import {\n type CompileFn,\n type FormatFn,\n} from '#packages/cli-lib/formatters/default.js'\n\nexport type StructuredJson = Record<\n string,\n {\n translation: string\n notes?: string\n context?: string\n limit?: string\n }\n>\n\nexport const format: FormatFn<StructuredJson> = msgs => {\n const results: StructuredJson = {}\n for (const [id, msg] of Object.entries(msgs)) {\n results[id] = {\n translation: msg.defaultMessage!,\n notes:\n typeof msg.description === 'string'\n ? msg.description\n : JSON.stringify(msg.description),\n }\n }\n return results\n}\n\nexport const compile: CompileFn<StructuredJson> = msgs => {\n const results: Record<string, string> = {}\n for (const [id, msg] of Object.entries(msgs)) {\n results[id] = msg.translation\n }\n return results\n}\n","import {\n type CompileFn,\n type FormatFn,\n} from '#packages/cli-lib/formatters/default.js'\n\nexport type PhraseJson = Record<string, string>\n\nexport const format: FormatFn<PhraseJson> = msgs => {\n return Object.keys(msgs).reduce((all: PhraseJson, k) => {\n all[k] = msgs[k].defaultMessage!\n return all\n }, {})\n}\n\nexport const compile: CompileFn<PhraseJson> = msgs => msgs\n","import type {Comparator} from 'json-stable-stringify'\nimport {\n type CompileFn,\n type FormatFn,\n} from '#packages/cli-lib/formatters/default.js'\n\nexport interface SmartlingDirectives {\n translate_paths: [\n {\n path: string\n key: string\n instruction: string\n },\n ]\n variants_enabled: boolean\n string_format: string\n [k: string]: any\n}\n\nexport type SmartlingJson = {\n smartling: SmartlingDirectives\n} & Record<\n string,\n {\n message: string\n description?: string\n }\n>\n\nexport const format: FormatFn<SmartlingJson> = msgs => {\n const results: SmartlingJson = {\n smartling: {\n translate_paths: [\n {\n path: '*/message',\n key: '{*}/message',\n instruction: '*/description',\n },\n ],\n variants_enabled: true,\n string_format: 'icu',\n },\n } as any\n for (const [id, msg] of Object.entries(msgs)) {\n results[id] = {\n message: msg.defaultMessage!,\n description:\n typeof msg.description === 'string'\n ? msg.description\n : JSON.stringify(msg.description),\n }\n }\n return results\n}\n\nexport const compareMessages: Comparator = (el1, el2) => {\n // `smartling` has to be the 1st key\n if (el1.key === 'smartling') {\n return -1\n }\n if (el2.key === 'smartling') {\n return 1\n }\n return el1.key < el2.key ? -1 : el1.key === el2.key ? 0 : 1\n}\n\nexport const compile: CompileFn<SmartlingJson> = msgs => {\n const results: Record<string, string> = {}\n for (const [id, msg] of Object.entries(msgs)) {\n if (id === 'smartling') {\n continue\n }\n results[id] = msg.message\n }\n return results\n}\n","import {\n type CompileFn,\n type FormatFn,\n} from '#packages/cli-lib/formatters/default.js'\n\nexport type StructuredJson = Record<\n string,\n {\n string: string\n developer_comment?: string\n context?: string\n character_limit?: string\n }\n>\n\nexport const format: FormatFn<StructuredJson> = msgs => {\n const results: StructuredJson = {}\n for (const [id, msg] of Object.entries(msgs)) {\n results[id] = {\n string: msg.defaultMessage!,\n developer_comment:\n typeof msg.description === 'string'\n ? msg.description\n : JSON.stringify(msg.description),\n }\n }\n return results\n}\n\nexport const compile: CompileFn<StructuredJson> = msgs => {\n const results: Record<string, string> = {}\n for (const [id, msg] of Object.entries(msgs)) {\n results[id] = msg.string\n }\n return results\n}\n","import type {Comparator} from 'json-stable-stringify'\nimport {resolve} from 'path'\nimport {pathToFileURL} from 'url'\nimport * as crowdin from '#packages/cli-lib/formatters/crowdin.js'\nimport * as defaultFormatter from '#packages/cli-lib/formatters/default.js'\nimport {\n type CompileFn,\n type FormatFn,\n type SerializeFn,\n} from '#packages/cli-lib/formatters/default.js'\nimport * as lokalise from '#packages/cli-lib/formatters/lokalise.js'\nimport * as simple from '#packages/cli-lib/formatters/simple.js'\nimport * as smartling from '#packages/cli-lib/formatters/smartling.js'\nimport * as transifex from '#packages/cli-lib/formatters/transifex.js'\n\nexport interface Formatter<T> {\n serialize?: SerializeFn<T>\n format: FormatFn<T>\n compile: CompileFn<T>\n compareMessages?: Comparator\n}\n\nexport async function resolveBuiltinFormatter(\n format?: string | Formatter<unknown>\n): Promise<any> {\n if (!format) {\n return defaultFormatter\n }\n if (typeof format !== 'string') {\n return format\n }\n switch (format) {\n case 'transifex':\n return transifex\n case 'smartling':\n return smartling\n case 'simple':\n return simple\n case 'lokalise':\n return lokalise\n case 'crowdin':\n return crowdin\n }\n try {\n // eslint-disable-next-line import/dynamic-import-chunkname\n return import(pathToFileURL(resolve(process.cwd(), format)).href)\n } catch (e) {\n console.error(`Cannot resolve formatter ${format}`)\n throw e\n }\n}\n","import {\n type MessageDescriptor,\n type Opts,\n interpolateName,\n} from '@formatjs/ts-transformer'\nimport {outputFile} from 'fs-extra/esm'\nimport {\n debug,\n getStdinAsString,\n warn,\n writeStdout,\n} from '#packages/cli-lib/console_utils.js'\nimport * as stringifyNs from 'json-stable-stringify'\n\nimport {\n type Formatter,\n resolveBuiltinFormatter,\n} from '#packages/cli-lib/formatters/index.js'\nimport {parseScript} from '#packages/cli-lib/parse_script.js'\nimport {readFile} from 'fs/promises'\n\nconst stringify = (stringifyNs as any).default || stringifyNs\nexport interface ExtractionResult<M = Record<string, string>> {\n /**\n * List of extracted messages\n */\n messages: MessageDescriptor[]\n /**\n * Metadata extracted w/ `pragma`\n */\n meta?: M\n}\n\nexport interface ExtractedMessageDescriptor extends MessageDescriptor {\n /**\n * Line number\n */\n line?: number\n /**\n * Column number\n */\n col?: number\n /**\n * Metadata extracted from pragma\n */\n meta?: Record<string, string>\n}\n\nexport type ExtractCLIOptions = Omit<\n ExtractOpts,\n 'overrideIdFn' | 'onMsgExtracted' | 'onMetaExtracted'\n> & {\n /**\n * Output File\n */\n outFile?: string\n /**\n * Input File\n */\n inFile?: string\n /**\n * Ignore file glob pattern\n */\n ignore?: string[]\n /**\n * Whether to follow symbolic links when traversing directories.\n * Defaults to true for compatibility with pnpm symlinked node_modules.\n */\n followLinks?: boolean\n}\n\nexport type ExtractOpts = Opts & {\n /**\n * Whether to throw an error if we had any issues with\n * 1 of the source files\n */\n throws?: boolean\n /**\n * Message ID interpolation pattern\n */\n idInterpolationPattern?: string\n /**\n * Whether we read from stdin instead of a file\n */\n readFromStdin?: boolean\n /**\n * Either path to a formatter file that controls the shape of JSON file from `outFile` or {@link Formatter} object.\n */\n format?: string | Formatter<any>\n /**\n * Whether to hoist selectors & flatten sentences\n */\n flatten?: boolean\n /**\n * An AbortSignal to cancel the extraction\n */\n signal?: AbortSignal\n} & Pick<Opts, 'onMsgExtracted' | 'onMetaExtracted'>\n\nfunction calculateLineColFromOffset(\n text: string,\n start?: number\n): Pick<ExtractedMessageDescriptor, 'line' | 'col'> {\n if (!start) {\n return {line: 1, col: 1}\n }\n const chunk = text.slice(0, start)\n const lines = chunk.split('\\n')\n const lastLine = lines[lines.length - 1]\n return {line: lines.length, col: lastLine.length}\n}\n\nasync function processFile(\n source: string,\n fn: string,\n {idInterpolationPattern, ...opts}: Opts & {idInterpolationPattern?: string}\n) {\n let messages: ExtractedMessageDescriptor[] = []\n let meta: Record<string, string> | undefined\n\n const onMsgExtracted = opts.onMsgExtracted\n const onMetaExtracted = opts.onMetaExtracted\n\n opts = {\n ...opts,\n additionalComponentNames: [\n '$formatMessage',\n ...(opts.additionalComponentNames || []),\n ],\n onMsgExtracted(filePath, msgs) {\n if (opts.extractSourceLocation) {\n msgs = msgs.map(msg => ({\n ...msg,\n ...calculateLineColFromOffset(source, msg.start),\n }))\n }\n messages = messages.concat(msgs)\n\n if (onMsgExtracted) {\n onMsgExtracted(filePath, msgs)\n }\n },\n onMetaExtracted(filePath, m) {\n meta = m\n\n if (onMetaExtracted) {\n onMetaExtracted(filePath, m)\n }\n },\n }\n\n if (!opts.overrideIdFn && idInterpolationPattern) {\n opts = {\n ...opts,\n overrideIdFn: (id, defaultMessage, description, fileName) =>\n id ||\n interpolateName(\n {\n resourcePath: fileName,\n } as any,\n idInterpolationPattern,\n {\n content: description\n ? `${defaultMessage}#${\n typeof description === 'string'\n ? description\n : stringify(description)\n }`\n : defaultMessage,\n }\n ),\n }\n }\n\n debug('Processing opts for %s: %s', fn, opts)\n\n const scriptParseFn = parseScript(opts, fn)\n if (fn.endsWith('.vue')) {\n debug('Processing %s using vue extractor', fn)\n const {parseFile} = await import('./vue_extractor.js')\n parseFile(source, fn, scriptParseFn)\n } else if (fn.endsWith('.svelte')) {\n debug('Processing %s using svelte extractor', fn)\n const {parseFile} = await import('./svelte_extractor.js')\n parseFile(source, fn, scriptParseFn)\n } else if (fn.endsWith('.hbs')) {\n debug('Processing %s using hbs extractor', fn)\n const {parseFile} = await import('./hbs_extractor.js')\n parseFile(source, fn, opts)\n } else if (fn.endsWith('.gts') || fn.endsWith('.gjs')) {\n debug('Processing %s as gts/gjs file', fn)\n const {parseFile} = await import('./gts_extractor.js')\n parseFile(source, fn, opts)\n } else {\n debug('Processing %s using typescript extractor', fn)\n scriptParseFn(source)\n }\n debug('Done extracting %s messages: %s', fn, messages)\n if (meta) {\n debug('Extracted meta:', meta)\n messages.forEach(m => (m.meta = meta))\n }\n return {messages, meta}\n}\n\n/**\n * Extract strings from source files\n * @param files list of files\n * @param extractOpts extract options\n * @returns messages serialized as JSON string since key order\n * matters for some `format`\n */\nexport async function extract(\n files: readonly string[],\n extractOpts: ExtractOpts\n): Promise<string> {\n const {throws, readFromStdin, signal, ...opts} = extractOpts\n // When throws is not explicitly true, we want to collect partial results\n const shouldThrow = throws === true\n // Pass throws option to transformer for per-message error handling\n const optsWithThrows = {\n ...opts,\n throws: shouldThrow,\n onMsgError: !shouldThrow\n ? (_: string, e: Error) => warn(e.message)\n : undefined,\n }\n let rawResults: Array<ExtractionResult | undefined> = []\n try {\n if (readFromStdin) {\n debug(`Reading input from stdin`)\n // Read from stdin\n if (process.stdin.isTTY) {\n warn('Reading source file from TTY.')\n }\n const stdinSource = await getStdinAsString()\n rawResults = [await processFile(stdinSource, 'dummy', optsWithThrows)]\n } else {\n // Use Promise.allSettled when throws is not explicitly true to collect partial results\n if (!shouldThrow) {\n const settledResults = await Promise.allSettled(\n files.map(async fn => {\n debug('Extracting file:', fn)\n const source = await readFile(fn, {encoding: 'utf8', signal})\n return processFile(source, fn, optsWithThrows)\n })\n )\n rawResults = settledResults.map(result => {\n if (result.status === 'fulfilled') {\n return result.value\n } else {\n warn(String(result.reason))\n return undefined\n }\n })\n } else {\n rawResults = await Promise.all(\n files.map(async fn => {\n debug('Extracting file:', fn)\n const source = await readFile(fn, {encoding: 'utf8', signal})\n return processFile(source, fn, optsWithThrows)\n })\n )\n }\n }\n } catch (e) {\n if (shouldThrow) {\n throw e\n } else {\n warn(String(e))\n }\n }\n\n const formatter: Formatter<unknown> = await resolveBuiltinFormatter(\n opts.format\n )\n const extractionResults = rawResults.filter((r): r is ExtractionResult => !!r)\n\n const extractedMessages = new Map<string, MessageDescriptor>()\n\n for (const {messages} of extractionResults) {\n for (const message of messages) {\n const {id, description, defaultMessage} = message\n if (!id) {\n const error = new Error(\n `[FormatJS CLI] Missing message id for message:\n${JSON.stringify(message, undefined, 2)}`\n )\n if (throws) {\n throw error\n } else {\n warn(error.message)\n }\n continue\n }\n\n if (extractedMessages.has(id)) {\n const existing = extractedMessages.get(id)!\n if (\n stringify(description) !== stringify(existing.description) ||\n defaultMessage !== existing.defaultMessage\n ) {\n const error = new Error(\n `[FormatJS CLI] Duplicate message id: \"${id}\", ` +\n 'but the `description` and/or `defaultMessage` are different.'\n )\n if (throws) {\n throw error\n } else {\n warn(error.message)\n }\n }\n }\n extractedMessages.set(id, message)\n }\n }\n const results: Record<string, Omit<MessageDescriptor, 'id'>> = {}\n const messages = Array.from(extractedMessages.values())\n for (const {id, ...msg} of messages) {\n // GH #3537: flatten is now applied during extraction in the babel plugin,\n // so we don't need to apply it again here. The messages are already flattened.\n results[id] = msg\n }\n if (typeof formatter.serialize === 'function') {\n return formatter.serialize(formatter.format(results as any))\n }\n return (\n stringify(formatter.format(results as any), {\n space: 2,\n cmp: formatter.compareMessages || undefined,\n }) ?? ''\n )\n}\n\n/**\n * Extract strings from source files, also writes to a file.\n * @param files list of files\n * @param extractOpts extract options\n * @returns A Promise that resolves if output file was written successfully\n */\nexport default async function extractAndWrite(\n files: readonly string[],\n extractOpts: ExtractCLIOptions\n): Promise<void> {\n const {outFile, ...opts} = extractOpts\n const serializedResult = (await extract(files, opts)) + '\\n'\n if (outFile) {\n debug('Writing output file:', outFile)\n return outputFile(outFile, serializedResult)\n }\n await writeStdout(serializedResult)\n}\n","import {\n parse,\n type MessageFormatElement,\n TYPE,\n isLiteralElement,\n isPluralElement,\n isSelectElement,\n isTagElement,\n type LiteralElement,\n} from '@formatjs/icu-messageformat-parser'\n\nfunction forEachLiteralElement(\n ast: MessageFormatElement[],\n fn: (el: LiteralElement) => void\n): void {\n ast.forEach(el => {\n if (isLiteralElement(el)) {\n fn(el)\n } else if (isPluralElement(el) || isSelectElement(el)) {\n for (const opt of Object.values(el.options)) {\n forEachLiteralElement(opt.value, fn)\n }\n } else if (isTagElement(el)) {\n forEachLiteralElement(el.children, fn)\n }\n })\n}\n\nexport function generateXXLS(\n msg: string | MessageFormatElement[]\n): MessageFormatElement[] {\n const ast = typeof msg === 'string' ? parse(msg) : msg\n const lastChunk = ast[ast.length - 1]\n if (lastChunk && isLiteralElement(lastChunk)) {\n lastChunk.value += 'SSSSSSSSSSSSSSSSSSSSSSSSS'\n return ast\n }\n return [...ast, {type: TYPE.literal, value: 'SSSSSSSSSSSSSSSSSSSSSSSSS'}]\n}\n\nexport function generateXXAC(\n msg: string | MessageFormatElement[]\n): MessageFormatElement[] {\n const ast = typeof msg === 'string' ? parse(msg) : msg\n forEachLiteralElement(ast, el => {\n el.value = el.value.toUpperCase()\n })\n return ast\n}\n\nexport function generateXXHA(\n msg: string | MessageFormatElement[]\n): MessageFormatElement[] {\n const ast = typeof msg === 'string' ? parse(msg) : msg\n const [firstChunk, ...rest] = ast\n if (firstChunk && isLiteralElement(firstChunk)) {\n firstChunk.value = '[javascript]' + firstChunk.value\n return [firstChunk, ...rest]\n }\n return [{type: TYPE.literal, value: '[javascript]'}, ...ast]\n}\n\ntype _TupleOf<T, N extends number, R extends unknown[]> = R['length'] extends N\n ? R\n : _TupleOf<T, N, [T, ...R]>\n\ntype Tuple<T, N extends number> = N extends N\n ? number extends N\n ? T[]\n : _TupleOf<T, N, []>\n : never\n\ntype PseudoLocaleTransformMap = {\n caps: Tuple<number, 26>\n small: Tuple<number, 26>\n}\n\nconst ACCENTED_MAP: PseudoLocaleTransformMap = {\n // ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ\n // prettier-ignore\n \"caps\": [550, 385, 391, 7698, 7702, 401, 403, 294, 298, 308, 310, 319, 7742, 544, 510, 420, 586, 344, 350, 358, 364, 7804, 7814, 7818, 7822, 7824],\n // ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ\n // prettier-ignore\n \"small\": [551, 384, 392, 7699, 7703, 402, 608, 295, 299, 309, 311, 320, 7743, 414, 511, 421, 587, 345, 351, 359, 365, 7805, 7815, 7819, 7823, 7825],\n}\n\nconst FLIPPED_MAP: PseudoLocaleTransformMap = {\n // ∀ԐↃᗡƎℲ⅁HIſӼ⅂WNOԀÒᴚS⊥∩ɅMX⅄Z\n // prettier-ignore\n \"caps\": [8704, 1296, 8579, 5601, 398, 8498, 8513, 72, 73, 383, 1276, 8514, 87, 78, 79, 1280, 210, 7450, 83, 8869, 8745, 581, 77, 88, 8516, 90],\n // ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz\n // prettier-ignore\n \"small\": [592, 113, 596, 112, 477, 607, 387, 613, 305, 638, 670, 645, 623, 117, 111, 100, 98, 633, 115, 647, 110, 652, 653, 120, 654, 122],\n}\n\n/**\n * Based on: https://hg.mozilla.org/mozilla-central/file/a1f74e8c8fb72390d22054d6b00c28b1a32f6c43/intl/l10n/L10nRegistry.jsm#l425\n */\nfunction transformString(\n map: PseudoLocaleTransformMap,\n elongate = false,\n msg: string\n) {\n return msg.replace(/[a-z]/gi, ch => {\n const cc = ch.charCodeAt(0)\n if (cc >= 97 && cc <= 122) {\n const newChar = String.fromCodePoint(map.small[cc - 97])\n // duplicate \"a\", \"e\", \"o\" and \"u\" to emulate ~30% longer text\n if (elongate && (cc === 97 || cc === 101 || cc === 111 || cc === 117)) {\n return newChar + newChar\n }\n return newChar\n }\n if (cc >= 65 && cc <= 90) {\n return String.fromCodePoint(map.caps[cc - 65])\n }\n return ch\n })\n}\n\n/**\n * accented - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ\n * --------------------------------\n *\n * This locale replaces all Latin characters with their accented equivalents, and duplicates some\n * vowels to create roughly 30% longer strings. Strings are wrapped in markers (square brackets),\n * which help with detecting truncation.\n */\nexport function generateENXA(\n msg: string | MessageFormatElement[]\n): MessageFormatElement[] {\n const ast = typeof msg === 'string' ? parse(msg) : msg\n forEachLiteralElement(ast, el => {\n el.value = transformString(ACCENTED_MAP, true, el.value)\n })\n return [\n {type: TYPE.literal, value: '['},\n ...ast,\n {type: TYPE.literal, value: ']'},\n ]\n}\n\n/**\n * bidi - ɥsıʅƃuƎ ıpıԐ\n * -------------------\n *\n * This strategy replaces all Latin characters with their 180 degree rotated versions and enforces\n * right to left text flow using Unicode UAX#9 Explicit Directional Embeddings. In this mode, the UI\n * directionality will also be set to right-to-left.\n */\nexport function generateENXB(\n msg: string | MessageFormatElement[]\n): MessageFormatElement[] {\n const ast = typeof msg === 'string' ? parse(msg) : msg\n forEachLiteralElement(ast, el => {\n el.value = transformString(FLIPPED_MAP, false, el.value)\n })\n return [\n {type: TYPE.literal, value: '\\u202e'},\n ...ast,\n {type: TYPE.literal, value: '\\u202c'},\n ]\n}\n","import {\n type MessageFormatElement,\n parse,\n} from '@formatjs/icu-messageformat-parser'\nimport {outputFile} from 'fs-extra/esm'\nimport {readFile} from 'fs/promises'\nimport * as stringifyNs from 'json-stable-stringify'\nimport {debug, warn, writeStdout} from '#packages/cli-lib/console_utils.js'\nimport {\n type Formatter,\n resolveBuiltinFormatter,\n} from '#packages/cli-lib/formatters/index.js'\nimport {\n generateENXA,\n generateENXB,\n generateXXAC,\n generateXXHA,\n generateXXLS,\n} from '#packages/cli-lib/pseudo_locale.js'\n\nconst stringify = (stringifyNs as any).default || stringifyNs\n\nexport type CompileFn = (msgs: any) => Record<string, string>\n\nexport type PseudoLocale = 'xx-LS' | 'xx-AC' | 'xx-HA' | 'en-XA' | 'en-XB'\n\nexport interface CompileCLIOpts extends Opts {\n /**\n * The target file that contains compiled messages.\n */\n outFile?: string\n /**\n * Whether to follow symbolic links when traversing directories.\n * Defaults to true for compatibility with pnpm symlinked node_modules.\n */\n followLinks?: boolean\n}\nexport interface Opts {\n /**\n * Whether to compile message into AST instead of just string\n */\n ast?: boolean\n /**\n * Whether to continue compiling messages after encountering an error.\n * Any keys with errors will not be included in the output file.\n */\n skipErrors?: boolean\n /**\n * Path to a formatter file that converts <translation_files> to\n * `Record<string, string>` so we can compile.\n */\n format?: string | Formatter<unknown>\n /**\n * Whether to compile to pseudo locale\n */\n pseudoLocale?: PseudoLocale\n /**\n * Whether the parser to treat HTML/XML tags as string literal\n * instead of parsing them as tag token.\n * When this is false we only allow simple tags without\n * any attributes\n */\n ignoreTag?: boolean\n /**\n * An AbortSignal to cancel the compilation\n */\n signal?: AbortSignal\n}\n\n/**\n * Aggregate `inputFiles` into a single JSON blob and compile.\n * Also checks for conflicting IDs.\n * Then returns the serialized result as a `string` since key order\n * makes a difference in some vendor.\n * @param inputFiles Input files\n * @param opts Options\n * @returns serialized result in string format\n */\nexport async function compile(\n inputFiles: string[],\n opts: Opts = {}\n): Promise<string> {\n debug('Compiling files:', inputFiles)\n const {ast, format, pseudoLocale, skipErrors, ignoreTag, signal} = opts\n signal?.throwIfAborted()\n const formatter = await resolveBuiltinFormatter(format)\n\n const messages: Record<string, string> = {}\n const messageAsts: Record<string, MessageFormatElement[]> = {}\n const idsWithFileName: Record<string, string> = {}\n const compiledFiles = await Promise.all(\n inputFiles.map(f =>\n readFile(f, {encoding: 'utf8', signal})\n .then(content => JSON.parse(content))\n .then(formatter.compile)\n )\n )\n debug('Compiled files:', compiledFiles)\n for (let i = 0; i < inputFiles.length; i++) {\n const inputFile = inputFiles[i]\n debug('Processing file:', inputFile)\n const compiled = compiledFiles[i]\n for (const id in compiled) {\n if (messages[id] && messages[id] !== compiled[id]) {\n throw new Error(`Conflicting ID \"${id}\" with different translation found in these 2 files:\nID: ${id}\nMessage from ${idsWithFileName[id]}: ${messages[id]}\nMessage from ${inputFile}: ${compiled[id]}\n`)\n }\n try {\n const msgAst = parse(compiled[id], {ignoreTag})\n messages[id] = compiled[id]\n switch (pseudoLocale) {\n case 'xx-LS':\n messageAsts[id] = generateXXLS(msgAst)\n break\n case 'xx-AC':\n messageAsts[id] = generateXXAC(msgAst)\n break\n case 'xx-HA':\n messageAsts[id] = generateXXHA(msgAst)\n break\n case 'en-XA':\n messageAsts[id] = generateENXA(msgAst)\n break\n case 'en-XB':\n messageAsts[id] = generateENXB(msgAst)\n break\n default:\n messageAsts[id] = msgAst\n break\n }\n idsWithFileName[id] = inputFile\n } catch (e) {\n warn(\n 'Error validating message \"%s\" with ID \"%s\" in file \"%s\"',\n compiled[id],\n id,\n inputFile\n )\n if (!skipErrors) {\n throw e\n }\n }\n }\n }\n\n return (\n stringify(ast ? messageAsts : messages, {\n space: 2,\n cmp: formatter.compareMessages || undefined,\n }) ?? ''\n )\n}\n\n/**\n * Aggregate `inputFiles` into a single JSON blob and compile.\n * Also checks for conflicting IDs and write output to `outFile`.\n * @param inputFiles Input files\n * @param compileOpts options\n * @returns A `Promise` that resolves if file was written successfully\n */\nexport default async function compileAndWrite(\n inputFiles: string[],\n compileOpts: CompileCLIOpts = {}\n): Promise<void> {\n const {outFile, ...opts} = compileOpts\n const serializedResult = (await compile(inputFiles, opts)) + '\\n'\n if (outFile) {\n debug('Writing output file:', outFile)\n return outputFile(outFile, serializedResult)\n }\n await writeStdout(serializedResult)\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formatjs/cli-lib",
|
|
3
3
|
"description": "Lib for CLI for formatjs.",
|
|
4
|
-
"version": "8.5.
|
|
4
|
+
"version": "8.5.2",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Linjie Ding <linjie@airtable.com>",
|
|
7
7
|
"type": "module",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"fs-extra": "^11.3.3",
|
|
22
22
|
"json-stable-stringify": "^1.3.0",
|
|
23
23
|
"loud-rejection": "^2",
|
|
24
|
-
"typescript": "^5.6
|
|
25
|
-
"@formatjs/icu-messageformat-parser": "3.5.
|
|
26
|
-
"@formatjs/
|
|
27
|
-
"@formatjs/
|
|
24
|
+
"typescript": "^5.6 || 6",
|
|
25
|
+
"@formatjs/icu-messageformat-parser": "3.5.4",
|
|
26
|
+
"@formatjs/icu-skeleton-parser": "2.1.4",
|
|
27
|
+
"@formatjs/ts-transformer": "4.4.4"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"@glimmer/syntax": "^0.84.3 || ^0.95.0",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { transformWithTs } from "@formatjs/ts-transformer";
|
|
2
|
+
import { clearLine, cursorTo } from "readline";
|
|
3
|
+
import { format, promisify, styleText } from "node:util";
|
|
4
|
+
import * as ts from "typescript";
|
|
5
|
+
//#region packages/cli-lib/console_utils.ts
|
|
6
|
+
const CLEAR_WHOLE_LINE = 0;
|
|
7
|
+
const writeStderr = promisify(process.stderr.write).bind(process.stderr);
|
|
8
|
+
const writeStdout = promisify(process.stdout.write).bind(process.stdout);
|
|
9
|
+
async function clearLine$1(terminal) {
|
|
10
|
+
if (!terminal.hasColors?.()) {
|
|
11
|
+
if (terminal.isTTY) {
|
|
12
|
+
if (terminal.columns > 0) await writeStderr(`\r${" ".repeat(terminal.columns - 1)}`);
|
|
13
|
+
await writeStderr(`\r`);
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
clearLine(terminal, CLEAR_WHOLE_LINE);
|
|
17
|
+
cursorTo(terminal, 0, void 0);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const LEVEL_COLORS = {
|
|
21
|
+
debug: "green",
|
|
22
|
+
warn: "yellow",
|
|
23
|
+
error: "red"
|
|
24
|
+
};
|
|
25
|
+
function label(level, message) {
|
|
26
|
+
return `[@formatjs/cli] [${styleText(LEVEL_COLORS[level], level.toUpperCase())}] ${message}`;
|
|
27
|
+
}
|
|
28
|
+
async function debug(message, ...args) {
|
|
29
|
+
if (process.env.LOG_LEVEL !== "debug") return;
|
|
30
|
+
await clearLine$1(process.stderr);
|
|
31
|
+
await writeStderr(format(label("debug", message), ...args));
|
|
32
|
+
await writeStderr("\n");
|
|
33
|
+
}
|
|
34
|
+
async function warn(message, ...args) {
|
|
35
|
+
await clearLine$1(process.stderr);
|
|
36
|
+
await writeStderr(format(label("warn", message), ...args));
|
|
37
|
+
await writeStderr("\n");
|
|
38
|
+
}
|
|
39
|
+
function getStdinAsString() {
|
|
40
|
+
let result = "";
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
process.stdin.setEncoding("utf-8");
|
|
43
|
+
process.stdin.on("readable", () => {
|
|
44
|
+
let chunk;
|
|
45
|
+
while (chunk = process.stdin.read()) result += chunk;
|
|
46
|
+
});
|
|
47
|
+
process.stdin.on("end", () => {
|
|
48
|
+
resolve(result);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region packages/cli-lib/parse_script.ts
|
|
54
|
+
/**
|
|
55
|
+
* Invoid TypeScript module transpilation with our TS transformer
|
|
56
|
+
* @param opts Formatjs TS Transformer opt
|
|
57
|
+
* @param fn filename
|
|
58
|
+
*/
|
|
59
|
+
function parseScript(opts, fn) {
|
|
60
|
+
return (source) => {
|
|
61
|
+
let output;
|
|
62
|
+
try {
|
|
63
|
+
debug("Using TS compiler to process file", fn);
|
|
64
|
+
output = ts.transpileModule(source, {
|
|
65
|
+
compilerOptions: {
|
|
66
|
+
allowJs: true,
|
|
67
|
+
target: ts.ScriptTarget.ESNext,
|
|
68
|
+
noEmit: true,
|
|
69
|
+
experimentalDecorators: true
|
|
70
|
+
},
|
|
71
|
+
reportDiagnostics: true,
|
|
72
|
+
fileName: fn,
|
|
73
|
+
transformers: { before: [transformWithTs(ts, opts)] }
|
|
74
|
+
});
|
|
75
|
+
} catch (e) {
|
|
76
|
+
if (e instanceof Error) e.message = `Error processing file ${fn}
|
|
77
|
+
${e.message || ""}`;
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
if (output.diagnostics) {
|
|
81
|
+
const errs = output.diagnostics.filter((d) => d.category === ts.DiagnosticCategory.Error);
|
|
82
|
+
if (errs.length) throw new Error(ts.formatDiagnosticsWithColorAndContext(errs, {
|
|
83
|
+
getCanonicalFileName: (fileName) => fileName,
|
|
84
|
+
getCurrentDirectory: () => process.cwd(),
|
|
85
|
+
getNewLine: () => ts.sys.newLine
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { writeStdout as a, warn as i, debug as n, getStdinAsString as r, parseScript as t };
|
|
92
|
+
|
|
93
|
+
//# sourceMappingURL=parse_script-EspfGgzZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse_script-EspfGgzZ.js","names":["clearLine"],"sources":["../console_utils.ts","../parse_script.ts"],"sourcesContent":["import {\n clearLine as nativeClearLine,\n cursorTo as nativeCursorTo,\n} from 'readline'\nimport {format, promisify, styleText} from 'node:util'\n\nconst CLEAR_WHOLE_LINE = 0\n\nexport const writeStderr: (arg1: string | Uint8Array) => Promise<void> =\n promisify(process.stderr.write).bind(process.stderr)\nexport const writeStdout: (arg1: string | Uint8Array) => Promise<void> =\n promisify(process.stdout.write).bind(process.stdout)\n\n// From:\n// https://github.com/yarnpkg/yarn/blob/53d8004229f543f342833310d5af63a4b6e59c8a/src/reporters/console/util.js\nexport async function clearLine(\n terminal: (typeof process)['stderr']\n): Promise<void> {\n if (!terminal.hasColors?.()) {\n if (terminal.isTTY) {\n // terminal\n if (terminal.columns > 0) {\n await writeStderr(`\\r${' '.repeat(terminal.columns - 1)}`)\n }\n await writeStderr(`\\r`)\n }\n // ignore piping to file\n } else {\n nativeClearLine(terminal, CLEAR_WHOLE_LINE)\n nativeCursorTo(terminal, 0, undefined)\n }\n}\n\ntype LogLevel = 'debug' | 'warn' | 'error'\n\nconst LEVEL_COLORS: Record<LogLevel, 'green' | 'yellow' | 'red'> = {\n debug: 'green',\n warn: 'yellow',\n error: 'red',\n}\n\nfunction label(level: LogLevel, message: string) {\n return `[@formatjs/cli] [${styleText(\n LEVEL_COLORS[level],\n level.toUpperCase()\n )}] ${message}`\n}\n\nexport async function debug(message: string, ...args: any[]): Promise<void> {\n if (process.env.LOG_LEVEL !== 'debug') {\n return\n }\n await clearLine(process.stderr)\n await writeStderr(format(label('debug', message), ...args))\n await writeStderr('\\n')\n}\n\nexport async function warn(message: string, ...args: any[]): Promise<void> {\n await clearLine(process.stderr)\n await writeStderr(format(label('warn', message), ...args))\n await writeStderr('\\n')\n}\n\nexport async function error(message: string, ...args: any[]): Promise<void> {\n await clearLine(process.stderr)\n await writeStderr(format(label('error', message), ...args))\n await writeStderr('\\n')\n}\n\nexport function getStdinAsString(): Promise<string> {\n let result = ''\n return new Promise(resolve => {\n process.stdin.setEncoding('utf-8')\n process.stdin.on('readable', () => {\n let chunk\n while ((chunk = process.stdin.read())) {\n result += chunk\n }\n })\n process.stdin.on('end', () => {\n resolve(result)\n })\n })\n}\n","import {type Opts, transformWithTs} from '@formatjs/ts-transformer'\nimport * as ts from 'typescript'\nimport {debug} from '#packages/cli-lib/console_utils.js'\n/**\n * Invoid TypeScript module transpilation with our TS transformer\n * @param opts Formatjs TS Transformer opt\n * @param fn filename\n */\nexport function parseScript(opts: Opts, fn?: string) {\n return (source: string): void => {\n let output\n try {\n debug('Using TS compiler to process file', fn)\n output = ts.transpileModule(source, {\n compilerOptions: {\n allowJs: true,\n target: ts.ScriptTarget.ESNext,\n noEmit: true,\n experimentalDecorators: true,\n },\n reportDiagnostics: true,\n fileName: fn,\n transformers: {\n before: [transformWithTs(ts, opts)],\n },\n })\n } catch (e) {\n if (e instanceof Error) {\n e.message = `Error processing file ${fn} \n${e.message || ''}`\n }\n throw e\n }\n if (output.diagnostics) {\n const errs = output.diagnostics.filter(\n d => d.category === ts.DiagnosticCategory.Error\n )\n if (errs.length) {\n throw new Error(\n ts.formatDiagnosticsWithColorAndContext(errs, {\n getCanonicalFileName: fileName => fileName,\n getCurrentDirectory: () => process.cwd(),\n getNewLine: () => ts.sys.newLine,\n })\n )\n }\n }\n }\n}\n"],"mappings":";;;;;AAMA,MAAM,mBAAmB;AAEzB,MAAa,cACX,UAAU,QAAQ,OAAO,MAAM,CAAC,KAAK,QAAQ,OAAO;AACtD,MAAa,cACX,UAAU,QAAQ,OAAO,MAAM,CAAC,KAAK,QAAQ,OAAO;AAItD,eAAsBA,YACpB,UACe;AACf,KAAI,CAAC,SAAS,aAAa;MACrB,SAAS,OAAO;AAElB,OAAI,SAAS,UAAU,EACrB,OAAM,YAAY,KAAK,IAAI,OAAO,SAAS,UAAU,EAAE,GAAG;AAE5D,SAAM,YAAY,KAAK;;QAGpB;AACL,YAAgB,UAAU,iBAAiB;AAC3C,WAAe,UAAU,GAAG,KAAA,EAAU;;;AAM1C,MAAM,eAA6D;CACjE,OAAO;CACP,MAAM;CACN,OAAO;CACR;AAED,SAAS,MAAM,OAAiB,SAAiB;AAC/C,QAAO,oBAAoB,UACzB,aAAa,QACb,MAAM,aAAa,CACpB,CAAC,IAAI;;AAGR,eAAsB,MAAM,SAAiB,GAAG,MAA4B;AAC1E,KAAI,QAAQ,IAAI,cAAc,QAC5B;AAEF,OAAMA,YAAU,QAAQ,OAAO;AAC/B,OAAM,YAAY,OAAO,MAAM,SAAS,QAAQ,EAAE,GAAG,KAAK,CAAC;AAC3D,OAAM,YAAY,KAAK;;AAGzB,eAAsB,KAAK,SAAiB,GAAG,MAA4B;AACzE,OAAMA,YAAU,QAAQ,OAAO;AAC/B,OAAM,YAAY,OAAO,MAAM,QAAQ,QAAQ,EAAE,GAAG,KAAK,CAAC;AAC1D,OAAM,YAAY,KAAK;;AASzB,SAAgB,mBAAoC;CAClD,IAAI,SAAS;AACb,QAAO,IAAI,SAAQ,YAAW;AAC5B,UAAQ,MAAM,YAAY,QAAQ;AAClC,UAAQ,MAAM,GAAG,kBAAkB;GACjC,IAAI;AACJ,UAAQ,QAAQ,QAAQ,MAAM,MAAM,CAClC,WAAU;IAEZ;AACF,UAAQ,MAAM,GAAG,aAAa;AAC5B,WAAQ,OAAO;IACf;GACF;;;;;;;;;AC1EJ,SAAgB,YAAY,MAAY,IAAa;AACnD,SAAQ,WAAyB;EAC/B,IAAI;AACJ,MAAI;AACF,SAAM,qCAAqC,GAAG;AAC9C,YAAS,GAAG,gBAAgB,QAAQ;IAClC,iBAAiB;KACf,SAAS;KACT,QAAQ,GAAG,aAAa;KACxB,QAAQ;KACR,wBAAwB;KACzB;IACD,mBAAmB;IACnB,UAAU;IACV,cAAc,EACZ,QAAQ,CAAC,gBAAgB,IAAI,KAAK,CAAC,EACpC;IACF,CAAC;WACK,GAAG;AACV,OAAI,aAAa,MACf,GAAE,UAAU,yBAAyB,GAAG;EAC9C,EAAE,WAAW;AAET,SAAM;;AAER,MAAI,OAAO,aAAa;GACtB,MAAM,OAAO,OAAO,YAAY,QAC9B,MAAK,EAAE,aAAa,GAAG,mBAAmB,MAC3C;AACD,OAAI,KAAK,OACP,OAAM,IAAI,MACR,GAAG,qCAAqC,MAAM;IAC5C,uBAAsB,aAAY;IAClC,2BAA2B,QAAQ,KAAK;IACxC,kBAAkB,GAAG,IAAI;IAC1B,CAAC,CACH"}
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { parse } from "svelte/compiler";
|
|
2
|
+
//#region packages/cli-lib/svelte_extractor.ts
|
|
2
3
|
function walkFragment(fragment, source, parseScriptFn) {
|
|
3
|
-
if (!fragment || !fragment.nodes)
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
for (const node of fragment.nodes) {
|
|
7
|
-
walkNode(node, source, parseScriptFn);
|
|
8
|
-
}
|
|
4
|
+
if (!fragment || !fragment.nodes) return;
|
|
5
|
+
for (const node of fragment.nodes) walkNode(node, source, parseScriptFn);
|
|
9
6
|
}
|
|
10
7
|
function extractExpression(expression, source, parseScriptFn) {
|
|
11
|
-
if (!expression || expression.start == null || expression.end == null)
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
8
|
+
if (!expression || expression.start == null || expression.end == null) return;
|
|
14
9
|
const content = source.slice(expression.start, expression.end);
|
|
15
10
|
try {
|
|
16
11
|
parseScriptFn(`(${content})`);
|
|
@@ -24,7 +19,6 @@ function walkNode(node, source, parseScriptFn) {
|
|
|
24
19
|
extractExpression(node.expression, source, parseScriptFn);
|
|
25
20
|
break;
|
|
26
21
|
case "ConstTag":
|
|
27
|
-
// {@const x = expr} — the declaration contains the expression
|
|
28
22
|
if (node.declaration) {
|
|
29
23
|
const content = source.slice(node.declaration.start, node.declaration.end);
|
|
30
24
|
try {
|
|
@@ -41,9 +35,7 @@ function walkNode(node, source, parseScriptFn) {
|
|
|
41
35
|
break;
|
|
42
36
|
case "EachBlock":
|
|
43
37
|
extractExpression(node.expression, source, parseScriptFn);
|
|
44
|
-
if (node.key)
|
|
45
|
-
extractExpression(node.key, source, parseScriptFn);
|
|
46
|
-
}
|
|
38
|
+
if (node.key) extractExpression(node.key, source, parseScriptFn);
|
|
47
39
|
walkFragment(node.body, source, parseScriptFn);
|
|
48
40
|
walkFragment(node.fallback, source, parseScriptFn);
|
|
49
41
|
break;
|
|
@@ -71,47 +63,34 @@ function walkNode(node, source, parseScriptFn) {
|
|
|
71
63
|
case "SlotElement":
|
|
72
64
|
case "SvelteSelf":
|
|
73
65
|
case "SvelteFragment":
|
|
74
|
-
// Process attributes
|
|
75
66
|
if (node.attributes) {
|
|
76
|
-
for (const attr of node.attributes) {
|
|
77
|
-
if (attr.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
} else if (typeof attr.value === "object" && attr.value !== null && attr.value.type === "ExpressionTag") {
|
|
85
|
-
extractExpression(attr.value.expression, source, parseScriptFn);
|
|
86
|
-
}
|
|
87
|
-
} else if (attr.type === "SpreadAttribute") {
|
|
88
|
-
extractExpression(attr.expression, source, parseScriptFn);
|
|
89
|
-
} else if (attr.expression) {
|
|
90
|
-
// Directive nodes (OnDirective, BindDirective, etc.)
|
|
91
|
-
extractExpression(attr.expression, source, parseScriptFn);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
67
|
+
for (const attr of node.attributes) if (attr.type === "Attribute") {
|
|
68
|
+
if (Array.isArray(attr.value)) {
|
|
69
|
+
for (const v of attr.value) if (v.type === "ExpressionTag") extractExpression(v.expression, source, parseScriptFn);
|
|
70
|
+
} else if (typeof attr.value === "object" && attr.value !== null && attr.value.type === "ExpressionTag") extractExpression(attr.value.expression, source, parseScriptFn);
|
|
71
|
+
} else if (attr.type === "SpreadAttribute") extractExpression(attr.expression, source, parseScriptFn);
|
|
72
|
+
else if (attr.expression) extractExpression(attr.expression, source, parseScriptFn);
|
|
94
73
|
}
|
|
95
|
-
// Process children
|
|
96
74
|
walkFragment(node.fragment, source, parseScriptFn);
|
|
97
75
|
break;
|
|
98
76
|
}
|
|
99
77
|
}
|
|
100
|
-
|
|
78
|
+
function parseFile(source, filename, parseScriptFn) {
|
|
101
79
|
const ast = parse(source, {
|
|
102
80
|
modern: true,
|
|
103
81
|
filename
|
|
104
82
|
});
|
|
105
|
-
// Walk the template fragment
|
|
106
83
|
walkFragment(ast.fragment, source, parseScriptFn);
|
|
107
|
-
// Process <script> block
|
|
108
84
|
if (ast.instance) {
|
|
109
85
|
const program = ast.instance.content;
|
|
110
86
|
parseScriptFn(source.slice(program.start, program.end));
|
|
111
87
|
}
|
|
112
|
-
// Process <script context="module"> / <script module> block
|
|
113
88
|
if (ast.module) {
|
|
114
89
|
const program = ast.module.content;
|
|
115
90
|
parseScriptFn(source.slice(program.start, program.end));
|
|
116
91
|
}
|
|
117
92
|
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { parseFile };
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=svelte_extractor-Fr1Z3JHX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte_extractor-Fr1Z3JHX.js","names":[],"sources":["../svelte_extractor.ts"],"sourcesContent":["import {parse} from 'svelte/compiler'\n\nexport type ScriptParseFn = (source: string) => void\n\ninterface SvelteNode {\n type: string\n start: number\n end: number\n [key: string]: any\n}\n\nfunction walkFragment(\n fragment: SvelteNode | null | undefined,\n source: string,\n parseScriptFn: ScriptParseFn\n): void {\n if (!fragment || !fragment.nodes) {\n return\n }\n for (const node of fragment.nodes) {\n walkNode(node, source, parseScriptFn)\n }\n}\n\nfunction extractExpression(\n expression: SvelteNode | null | undefined,\n source: string,\n parseScriptFn: ScriptParseFn\n): void {\n if (!expression || expression.start == null || expression.end == null) {\n return\n }\n const content = source.slice(expression.start, expression.end)\n try {\n parseScriptFn(`(${content})`)\n } catch (e) {\n console.warn(\n `Failed to parse \"${content}\". Ignore this if content has no extractable message`,\n e\n )\n }\n}\n\nfunction walkNode(\n node: SvelteNode,\n source: string,\n parseScriptFn: ScriptParseFn\n): void {\n switch (node.type) {\n case 'ExpressionTag':\n extractExpression(node.expression, source, parseScriptFn)\n break\n\n case 'ConstTag':\n // {@const x = expr} — the declaration contains the expression\n if (node.declaration) {\n const content = source.slice(\n node.declaration.start,\n node.declaration.end\n )\n try {\n parseScriptFn(content)\n } catch (e) {\n console.warn(\n `Failed to parse const declaration. Ignore this if content has no extractable message`,\n e\n )\n }\n }\n break\n\n case 'IfBlock':\n extractExpression(node.test, source, parseScriptFn)\n walkFragment(node.consequent, source, parseScriptFn)\n walkFragment(node.alternate, source, parseScriptFn)\n break\n\n case 'EachBlock':\n extractExpression(node.expression, source, parseScriptFn)\n if (node.key) {\n extractExpression(node.key, source, parseScriptFn)\n }\n walkFragment(node.body, source, parseScriptFn)\n walkFragment(node.fallback, source, parseScriptFn)\n break\n\n case 'AwaitBlock':\n extractExpression(node.expression, source, parseScriptFn)\n walkFragment(node.pending, source, parseScriptFn)\n walkFragment(node.then, source, parseScriptFn)\n walkFragment(node.catch, source, parseScriptFn)\n break\n\n case 'KeyBlock':\n extractExpression(node.expression, source, parseScriptFn)\n walkFragment(node.fragment, source, parseScriptFn)\n break\n\n case 'SnippetBlock':\n walkFragment(node.body, source, parseScriptFn)\n break\n\n case 'RegularElement':\n case 'Component':\n case 'SvelteComponent':\n case 'SvelteElement':\n case 'SvelteHead':\n case 'SvelteBody':\n case 'SvelteWindow':\n case 'SvelteDocument':\n case 'SlotElement':\n case 'SvelteSelf':\n case 'SvelteFragment':\n // Process attributes\n if (node.attributes) {\n for (const attr of node.attributes) {\n if (attr.type === 'Attribute') {\n if (Array.isArray(attr.value)) {\n for (const v of attr.value) {\n if (v.type === 'ExpressionTag') {\n extractExpression(v.expression, source, parseScriptFn)\n }\n }\n } else if (\n typeof attr.value === 'object' &&\n attr.value !== null &&\n attr.value.type === 'ExpressionTag'\n ) {\n extractExpression(attr.value.expression, source, parseScriptFn)\n }\n } else if (attr.type === 'SpreadAttribute') {\n extractExpression(attr.expression, source, parseScriptFn)\n } else if (attr.expression) {\n // Directive nodes (OnDirective, BindDirective, etc.)\n extractExpression(attr.expression, source, parseScriptFn)\n }\n }\n }\n // Process children\n walkFragment(node.fragment, source, parseScriptFn)\n break\n }\n}\n\nexport function parseFile(\n source: string,\n filename: string,\n parseScriptFn: ScriptParseFn\n): void {\n const ast = parse(source, {modern: true, filename})\n\n // Walk the template fragment\n walkFragment(ast.fragment as unknown as SvelteNode, source, parseScriptFn)\n\n // Process <script> block\n if (ast.instance) {\n const program = ast.instance.content as unknown as SvelteNode\n parseScriptFn(source.slice(program.start, program.end))\n }\n\n // Process <script context=\"module\"> / <script module> block\n if (ast.module) {\n const program = ast.module.content as unknown as SvelteNode\n parseScriptFn(source.slice(program.start, program.end))\n }\n}\n"],"mappings":";;AAWA,SAAS,aACP,UACA,QACA,eACM;AACN,KAAI,CAAC,YAAY,CAAC,SAAS,MACzB;AAEF,MAAK,MAAM,QAAQ,SAAS,MAC1B,UAAS,MAAM,QAAQ,cAAc;;AAIzC,SAAS,kBACP,YACA,QACA,eACM;AACN,KAAI,CAAC,cAAc,WAAW,SAAS,QAAQ,WAAW,OAAO,KAC/D;CAEF,MAAM,UAAU,OAAO,MAAM,WAAW,OAAO,WAAW,IAAI;AAC9D,KAAI;AACF,gBAAc,IAAI,QAAQ,GAAG;UACtB,GAAG;AACV,UAAQ,KACN,oBAAoB,QAAQ,uDAC5B,EACD;;;AAIL,SAAS,SACP,MACA,QACA,eACM;AACN,SAAQ,KAAK,MAAb;EACE,KAAK;AACH,qBAAkB,KAAK,YAAY,QAAQ,cAAc;AACzD;EAEF,KAAK;AAEH,OAAI,KAAK,aAAa;IACpB,MAAM,UAAU,OAAO,MACrB,KAAK,YAAY,OACjB,KAAK,YAAY,IAClB;AACD,QAAI;AACF,mBAAc,QAAQ;aACf,GAAG;AACV,aAAQ,KACN,wFACA,EACD;;;AAGL;EAEF,KAAK;AACH,qBAAkB,KAAK,MAAM,QAAQ,cAAc;AACnD,gBAAa,KAAK,YAAY,QAAQ,cAAc;AACpD,gBAAa,KAAK,WAAW,QAAQ,cAAc;AACnD;EAEF,KAAK;AACH,qBAAkB,KAAK,YAAY,QAAQ,cAAc;AACzD,OAAI,KAAK,IACP,mBAAkB,KAAK,KAAK,QAAQ,cAAc;AAEpD,gBAAa,KAAK,MAAM,QAAQ,cAAc;AAC9C,gBAAa,KAAK,UAAU,QAAQ,cAAc;AAClD;EAEF,KAAK;AACH,qBAAkB,KAAK,YAAY,QAAQ,cAAc;AACzD,gBAAa,KAAK,SAAS,QAAQ,cAAc;AACjD,gBAAa,KAAK,MAAM,QAAQ,cAAc;AAC9C,gBAAa,KAAK,OAAO,QAAQ,cAAc;AAC/C;EAEF,KAAK;AACH,qBAAkB,KAAK,YAAY,QAAQ,cAAc;AACzD,gBAAa,KAAK,UAAU,QAAQ,cAAc;AAClD;EAEF,KAAK;AACH,gBAAa,KAAK,MAAM,QAAQ,cAAc;AAC9C;EAEF,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;AAEH,OAAI,KAAK;SACF,MAAM,QAAQ,KAAK,WACtB,KAAI,KAAK,SAAS;SACZ,MAAM,QAAQ,KAAK,MAAM;WACtB,MAAM,KAAK,KAAK,MACnB,KAAI,EAAE,SAAS,gBACb,mBAAkB,EAAE,YAAY,QAAQ,cAAc;gBAI1D,OAAO,KAAK,UAAU,YACtB,KAAK,UAAU,QACf,KAAK,MAAM,SAAS,gBAEpB,mBAAkB,KAAK,MAAM,YAAY,QAAQ,cAAc;eAExD,KAAK,SAAS,kBACvB,mBAAkB,KAAK,YAAY,QAAQ,cAAc;aAChD,KAAK,WAEd,mBAAkB,KAAK,YAAY,QAAQ,cAAc;;AAK/D,gBAAa,KAAK,UAAU,QAAQ,cAAc;AAClD;;;AAIN,SAAgB,UACd,QACA,UACA,eACM;CACN,MAAM,MAAM,MAAM,QAAQ;EAAC,QAAQ;EAAM;EAAS,CAAC;AAGnD,cAAa,IAAI,UAAmC,QAAQ,cAAc;AAG1E,KAAI,IAAI,UAAU;EAChB,MAAM,UAAU,IAAI,SAAS;AAC7B,gBAAc,OAAO,MAAM,QAAQ,OAAO,QAAQ,IAAI,CAAC;;AAIzD,KAAI,IAAI,QAAQ;EACd,MAAM,UAAU,IAAI,OAAO;AAC3B,gBAAc,OAAO,MAAM,QAAQ,OAAO,QAAQ,IAAI,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NodeTypes } from "@vue/compiler-core";
|
|
2
|
+
import { parse } from "vue/compiler-sfc";
|
|
3
|
+
//#region packages/cli-lib/vue_extractor.ts
|
|
4
|
+
function walk(node, visitor) {
|
|
5
|
+
if (typeof node !== "object" || node == null) return;
|
|
6
|
+
if (node.type === NodeTypes.ROOT) {
|
|
7
|
+
node.children.forEach((n) => walk(n, visitor));
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (node.type !== NodeTypes.ELEMENT && node.type !== NodeTypes.COMPOUND_EXPRESSION && node.type !== NodeTypes.INTERPOLATION) return;
|
|
11
|
+
visitor(node);
|
|
12
|
+
if (node.type === NodeTypes.INTERPOLATION) visitor(node.content);
|
|
13
|
+
else if (node.type === NodeTypes.ELEMENT) {
|
|
14
|
+
node.children.forEach((n) => walk(n, visitor));
|
|
15
|
+
node.props.filter((prop) => prop.type === NodeTypes.DIRECTIVE).filter((prop) => !!prop.exp).forEach((prop) => visitor(prop.exp));
|
|
16
|
+
} else node.children.forEach((n) => walk(n, visitor));
|
|
17
|
+
}
|
|
18
|
+
function templateSimpleExpressionNodeVisitor(parseScriptFn) {
|
|
19
|
+
return (n) => {
|
|
20
|
+
if (typeof n !== "object") return;
|
|
21
|
+
if (n.type !== NodeTypes.SIMPLE_EXPRESSION) return;
|
|
22
|
+
const { content } = n;
|
|
23
|
+
try {
|
|
24
|
+
parseScriptFn(`(${content})`);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.warn(`Failed to parse "${content}". Ignore this if content has no extractable message`, e);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function parseFile(source, filename, parseScriptFn) {
|
|
31
|
+
const { descriptor, errors } = parse(source, { filename });
|
|
32
|
+
if (errors.length) throw errors[0];
|
|
33
|
+
const { script, scriptSetup, template } = descriptor;
|
|
34
|
+
if (template) walk(template.ast, templateSimpleExpressionNodeVisitor(parseScriptFn));
|
|
35
|
+
if (script) parseScriptFn(script.content);
|
|
36
|
+
if (scriptSetup) parseScriptFn(scriptSetup.content);
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { parseFile };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=vue_extractor-Btw05cfG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue_extractor-Btw05cfG.js","names":[],"sources":["../vue_extractor.ts"],"sourcesContent":["import {\n NodeTypes,\n type TextNode,\n type CompoundExpressionNode,\n type DirectiveNode,\n type ElementNode,\n type InterpolationNode,\n type RootNode,\n type SimpleExpressionNode,\n type TemplateChildNode,\n} from '@vue/compiler-core'\nimport {parse} from 'vue/compiler-sfc'\n\nexport type ScriptParseFn = (source: string) => void\n\nfunction walk(\n node:\n | RootNode\n | TemplateChildNode\n | SimpleExpressionNode\n | CompoundExpressionNode\n | InterpolationNode\n | TextNode\n | string\n | symbol\n | undefined,\n visitor: (\n node:\n | ElementNode\n | InterpolationNode\n | CompoundExpressionNode\n | SimpleExpressionNode\n ) => void\n) {\n if (typeof node !== 'object' || node == null) {\n return\n }\n if (node.type === NodeTypes.ROOT) {\n node.children.forEach(n => walk(n, visitor))\n return\n }\n if (\n node.type !== NodeTypes.ELEMENT &&\n node.type !== NodeTypes.COMPOUND_EXPRESSION &&\n node.type !== NodeTypes.INTERPOLATION\n ) {\n return\n }\n visitor(node)\n if (node.type === NodeTypes.INTERPOLATION) {\n visitor(node.content)\n } else if (node.type === NodeTypes.ELEMENT) {\n node.children.forEach(n => walk(n, visitor))\n node.props\n .filter(\n (prop): prop is DirectiveNode => prop.type === NodeTypes.DIRECTIVE\n )\n .filter(prop => !!prop.exp)\n .forEach(prop => visitor(prop.exp!))\n } else {\n node.children.forEach(n => walk(n, visitor))\n }\n}\n\nfunction templateSimpleExpressionNodeVisitor(parseScriptFn: ScriptParseFn) {\n return (\n n:\n | ElementNode\n | CompoundExpressionNode\n | CompoundExpressionNode['children'][0]\n ) => {\n if (typeof n !== 'object') {\n return\n }\n if (n.type !== NodeTypes.SIMPLE_EXPRESSION) {\n return\n }\n\n const {content} = n as SimpleExpressionNode\n // Wrap this in () since a vue comp node attribute can just be\n // an object literal which, by itself is invalid TS\n // but with () it becomes an ExpressionStatement\n try {\n parseScriptFn(`(${content})`)\n } catch (e) {\n console.warn(\n `Failed to parse \"${content}\". Ignore this if content has no extractable message`,\n e\n )\n }\n }\n}\n\nexport function parseFile(\n source: string,\n filename: string,\n parseScriptFn: ScriptParseFn\n): void {\n const {descriptor, errors} = parse(source, {\n filename,\n })\n if (errors.length) {\n throw errors[0]\n }\n const {script, scriptSetup, template} = descriptor\n\n if (template) {\n walk(template.ast, templateSimpleExpressionNodeVisitor(parseScriptFn))\n }\n\n if (script) {\n parseScriptFn(script.content)\n }\n\n if (scriptSetup) {\n parseScriptFn(scriptSetup.content)\n }\n}\n"],"mappings":";;;AAeA,SAAS,KACP,MAUA,SAOA;AACA,KAAI,OAAO,SAAS,YAAY,QAAQ,KACtC;AAEF,KAAI,KAAK,SAAS,UAAU,MAAM;AAChC,OAAK,SAAS,SAAQ,MAAK,KAAK,GAAG,QAAQ,CAAC;AAC5C;;AAEF,KACE,KAAK,SAAS,UAAU,WACxB,KAAK,SAAS,UAAU,uBACxB,KAAK,SAAS,UAAU,cAExB;AAEF,SAAQ,KAAK;AACb,KAAI,KAAK,SAAS,UAAU,cAC1B,SAAQ,KAAK,QAAQ;UACZ,KAAK,SAAS,UAAU,SAAS;AAC1C,OAAK,SAAS,SAAQ,MAAK,KAAK,GAAG,QAAQ,CAAC;AAC5C,OAAK,MACF,QACE,SAAgC,KAAK,SAAS,UAAU,UAC1D,CACA,QAAO,SAAQ,CAAC,CAAC,KAAK,IAAI,CAC1B,SAAQ,SAAQ,QAAQ,KAAK,IAAK,CAAC;OAEtC,MAAK,SAAS,SAAQ,MAAK,KAAK,GAAG,QAAQ,CAAC;;AAIhD,SAAS,oCAAoC,eAA8B;AACzE,SACE,MAIG;AACH,MAAI,OAAO,MAAM,SACf;AAEF,MAAI,EAAE,SAAS,UAAU,kBACvB;EAGF,MAAM,EAAC,YAAW;AAIlB,MAAI;AACF,iBAAc,IAAI,QAAQ,GAAG;WACtB,GAAG;AACV,WAAQ,KACN,oBAAoB,QAAQ,uDAC5B,EACD;;;;AAKP,SAAgB,UACd,QACA,UACA,eACM;CACN,MAAM,EAAC,YAAY,WAAU,MAAM,QAAQ,EACzC,UACD,CAAC;AACF,KAAI,OAAO,OACT,OAAM,OAAO;CAEf,MAAM,EAAC,QAAQ,aAAa,aAAY;AAExC,KAAI,SACF,MAAK,SAAS,KAAK,oCAAoC,cAAc,CAAC;AAGxE,KAAI,OACF,eAAc,OAAO,QAAQ;AAG/B,KAAI,YACF,eAAc,YAAY,QAAQ"}
|
package/main.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/main.js
DELETED
package/src/cli.d.ts
DELETED
package/src/cli.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { program } from "commander";
|
|
2
|
-
import * as glob from "fast-glob";
|
|
3
|
-
const globSync = glob.sync;
|
|
4
|
-
import loudRejection from "loud-rejection";
|
|
5
|
-
import compile from "./compile.js";
|
|
6
|
-
import compileFolder from "./compile_folder.js";
|
|
7
|
-
import { debug } from "./console_utils.js";
|
|
8
|
-
import extract from "./extract.js";
|
|
9
|
-
import { verify } from "./verify/index.js";
|
|
10
|
-
import { resolve } from "path";
|
|
11
|
-
import { readFileSync } from "fs";
|
|
12
|
-
const KNOWN_COMMANDS = ["extract"];
|
|
13
|
-
async function main(argv) {
|
|
14
|
-
loudRejection();
|
|
15
|
-
program.version("5.0.6", "-v, --version").usage("<command> [flags]").action((command) => {
|
|
16
|
-
if (!KNOWN_COMMANDS.includes(command)) {
|
|
17
|
-
program.help();
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
program.command("help", { isDefault: true }).description("Show this help message.").action(() => program.help());
|
|
21
|
-
// Long text wrapping to available terminal columns: https://github.com/tj/commander.js/pull/956
|
|
22
|
-
// NOTE: please keep the help text in sync with babel-plugin-formatjs documentation.
|
|
23
|
-
program.command("extract [files...]").description(`Extract string messages from React components that use react-intl.
|
|
24
|
-
The input language is expected to be TypeScript or ES2017 with JSX.`).option("--format <path>", `Path to a formatter file that controls the shape of JSON file from \`--out-file\`.
|
|
25
|
-
The formatter file must export a function called \`format\` with the signature
|
|
26
|
-
\`\`\`
|
|
27
|
-
type FormatFn = <T = Record<string, MessageDescriptor>>(
|
|
28
|
-
msgs: Record<string, MessageDescriptor>
|
|
29
|
-
) => T
|
|
30
|
-
\`\`\`
|
|
31
|
-
This is especially useful to convert from our extracted format to a TMS-specific format.
|
|
32
|
-
`).option("--in-file <path>", `The file containing list of files to extract from, separated by newlines. This is mainly
|
|
33
|
-
to deal with the case where you have a large number of files to extract from and bash chokes.`).option("--out-file <path>", `The target file path where the plugin will output an aggregated
|
|
34
|
-
\`.json\` file of all the translations from the \`files\` supplied.`).option("--id-interpolation-pattern <pattern>", `If certain message descriptors don't have id, this \`pattern\` will be used to automatically
|
|
35
|
-
generate IDs for them. Default to \`[sha512:contenthash:base64:6]\` where \`contenthash\` is the hash of
|
|
36
|
-
\`defaultMessage\` and \`description\`.
|
|
37
|
-
See https://github.com/webpack/loader-utils#interpolatename for sample patterns`, "[sha512:contenthash:base64:6]").option("--extract-source-location", `Whether the metadata about the location of the message in the source file should be
|
|
38
|
-
extracted. If \`true\`, then \`file\`, \`start\`, and \`end\` fields will exist for each
|
|
39
|
-
extracted message descriptors.`, false).option("--additional-component-names <comma-separated-names>", `Additional component names to extract messages from, e.g: \`'FormattedFooBarMessage'\`.
|
|
40
|
-
**NOTE**: By default we check for the fact that \`FormattedMessage\`
|
|
41
|
-
is imported from \`moduleSourceName\` to make sure variable alias
|
|
42
|
-
works. This option does not do that so it's less safe.`, (val) => val.split(",")).option("--additional-function-names <comma-separated-names>", `Additional function names to extract messages from, e.g: \`'$t'\`.`, (val) => val.split(",")).option("--ignore <files...>", "List of glob paths to **not** extract translations from.").option("--throws", "Whether to throw an exception when we fail to process any file in the batch.").option("--pragma <pragma>", `parse specific additional custom pragma. This allows you to tag certain file with metadata such as \`project\`. For example with this file:
|
|
43
|
-
|
|
44
|
-
\`\`\`
|
|
45
|
-
// @intl-meta project:my-custom-project
|
|
46
|
-
import {FormattedMessage} from 'react-intl';
|
|
47
|
-
|
|
48
|
-
<FormattedMessage defaultMessage="foo" id="bar" />;
|
|
49
|
-
\`\`\`
|
|
50
|
-
|
|
51
|
-
and with option \`{pragma: "intl-meta"}\`, we'll parse out \`// @intl-meta project:my-custom-project\` into \`{project: 'my-custom-project'}\` in the result file.`).option("--preserve-whitespace", "Whether to preserve whitespace and newlines.").option("--flatten", `Whether to hoist selectors & flatten sentences as much as possible. E.g:
|
|
52
|
-
"I have {count, plural, one{a dog} other{many dogs}}"
|
|
53
|
-
becomes "{count, plural, one{I have a dog} other{I have many dogs}}".
|
|
54
|
-
The goal is to provide as many full sentences as possible since fragmented
|
|
55
|
-
sentences are not translator-friendly.`).option("--follow-links", `Whether to follow symbolic links when traversing directories. Defaults to true for compatibility with pnpm symlinked node_modules. Use --no-follow-links to disable.`, true).action(async (filePatterns, cmdObj) => {
|
|
56
|
-
debug("File pattern:", filePatterns);
|
|
57
|
-
debug("Options:", cmdObj);
|
|
58
|
-
const files = [];
|
|
59
|
-
if (filePatterns.length) {
|
|
60
|
-
files.push(...globSync(filePatterns, {
|
|
61
|
-
ignore: cmdObj.ignore,
|
|
62
|
-
followSymbolicLinks: cmdObj.followLinks ?? true
|
|
63
|
-
}));
|
|
64
|
-
}
|
|
65
|
-
if (cmdObj.inFile) {
|
|
66
|
-
debug("Reading inFile:", cmdObj.inFile);
|
|
67
|
-
const inFile = readFileSync(cmdObj.inFile, "utf8");
|
|
68
|
-
files.push(...inFile.split(/\n|\s+/).filter(Boolean).map((f) => resolve(f)));
|
|
69
|
-
}
|
|
70
|
-
debug("Files to extract:", files);
|
|
71
|
-
await extract(files, {
|
|
72
|
-
outFile: cmdObj.outFile,
|
|
73
|
-
idInterpolationPattern: cmdObj.idInterpolationPattern || "[sha1:contenthash:base64:6]",
|
|
74
|
-
extractSourceLocation: cmdObj.extractSourceLocation,
|
|
75
|
-
removeDefaultMessage: cmdObj.removeDefaultMessage,
|
|
76
|
-
additionalComponentNames: cmdObj.additionalComponentNames,
|
|
77
|
-
additionalFunctionNames: cmdObj.additionalFunctionNames,
|
|
78
|
-
throws: cmdObj.throws,
|
|
79
|
-
pragma: cmdObj.pragma,
|
|
80
|
-
format: cmdObj.format,
|
|
81
|
-
readFromStdin: files.length === 0,
|
|
82
|
-
preserveWhitespace: cmdObj.preserveWhitespace,
|
|
83
|
-
flatten: cmdObj.flatten
|
|
84
|
-
});
|
|
85
|
-
process.exit(0);
|
|
86
|
-
});
|
|
87
|
-
program.command("compile [translation_files...]").description(`Compile extracted translation file into react-intl consumable JSON We also verify that the messages are valid ICU and not malformed. <translation_files> can be a glob like "foo/**/en.json"`).option("--format <path>", `Path to a formatter file that converts \`<translation_file>\` to \`Record<string, string>\` so we can compile. The file must export a function named \`compile\` with the signature:
|
|
88
|
-
\`\`\`
|
|
89
|
-
type CompileFn = <T = Record<string, MessageDescriptor>>(
|
|
90
|
-
msgs: T
|
|
91
|
-
) => Record<string, string>;
|
|
92
|
-
\`\`\`
|
|
93
|
-
This is especially useful to convert from a TMS-specific format back to react-intl format
|
|
94
|
-
`).option("--out-file <path>", `Compiled translation output file. If this is not provided, result will be printed to stdout`).option("--ast", `Whether to compile to AST. See https://formatjs.github.io/docs/guides/advanced-usage#pre-parsing-messages for more information`).option("--skip-errors", `Whether to continue compiling messages after encountering an error. Any keys with errors will not be included in the output file.`).option("--pseudo-locale <pseudoLocale>", `Whether to generate pseudo-locale files. See https://formatjs.github.io/docs/tooling/cli#--pseudo-locale-pseudolocale for possible values. "--ast" is required for this to work.`).option("--ignore-tag", `Whether the parser to treat HTML/XML tags as string literal instead of parsing them as tag token. When this is false we only allow simple tags without any attributes.`).option("--follow-links", `Whether to follow symbolic links when traversing directories. Defaults to true for compatibility with pnpm symlinked node_modules. Use --no-follow-links to disable.`, true).action(async (filePatterns, opts) => {
|
|
95
|
-
debug("File pattern:", filePatterns);
|
|
96
|
-
debug("Options:", opts);
|
|
97
|
-
const files = globSync(filePatterns, { followSymbolicLinks: opts.followLinks ?? true });
|
|
98
|
-
if (!files.length) {
|
|
99
|
-
throw new Error(`No input file found with pattern ${filePatterns}`);
|
|
100
|
-
}
|
|
101
|
-
debug("Files to compile:", files);
|
|
102
|
-
await compile(files, opts);
|
|
103
|
-
});
|
|
104
|
-
program.command("compile-folder <folder> <outFolder>").description(`Batch compile all extracted translation JSON files in <folder> to <outFolder> containing react-intl consumable JSON. We also verify that the messages are valid ICU and not malformed.`).option("--format <path>", `Path to a formatter file that converts JSON files in \`<folder>\` to \`Record<string, string>\` so we can compile. The file must export a function named \`compile\` with the signature:
|
|
105
|
-
\`\`\`
|
|
106
|
-
type CompileFn = <T = Record<string, MessageDescriptor>>(
|
|
107
|
-
msgs: T
|
|
108
|
-
) => Record<string, string>;
|
|
109
|
-
\`\`\`
|
|
110
|
-
This is especially useful to convert from a TMS-specific format back to react-intl format
|
|
111
|
-
`).option("--ast", `Whether to compile to AST. See https://formatjs.github.io/docs/guides/advanced-usage#pre-parsing-messages for more information`).option("--skip-errors", `Whether to continue compiling messages after encountering an error. Any keys with errors will not be included in the output file.`).option("--pseudo-locale <pseudoLocale>", `Whether to generate pseudo-locale files. See https://formatjs.github.io/docs/tooling/cli#--pseudo-locale-pseudolocale for possible values. "--ast" is required for this to work.`).option("--ignore-tag", `Whether the parser to treat HTML/XML tags as string literal instead of parsing them as tag token. When this is false we only allow simple tags without any attributes.`).action(async (folder, outFolder, opts) => {
|
|
112
|
-
debug("Folder:", folder);
|
|
113
|
-
debug("Options:", opts);
|
|
114
|
-
// fast-glob expect `/` in Windows as well
|
|
115
|
-
const files = globSync(`${folder}/*.json`);
|
|
116
|
-
if (!files.length) {
|
|
117
|
-
throw new Error(`No JSON file found in ${folder}`);
|
|
118
|
-
}
|
|
119
|
-
debug("Files to compile:", files);
|
|
120
|
-
await compileFolder(files, outFolder, opts);
|
|
121
|
-
});
|
|
122
|
-
program.command("verify [translation_files...]").description(`Run a series of checks on a list of translation files. <translation_files> can be a glob like "foo/**/en.json"`).option("--source-locale <sourceLocale>", `The source locale of the translation files.
|
|
123
|
-
There must be a file named <sourceLocale>.json in the list of translation files.
|
|
124
|
-
This is used as source to verify other translations against.`).option("--ignore <files...>", "List of glob paths to ignore").option("--missing-keys", `Whether to check for missing keys in target locale compared to source locale.
|
|
125
|
-
This basically guarantees that no messages are untranslated.`).option("--extra-keys", `Whether to check that target locales don't have extra keys not present in the source locale.`).option("--structural-equality", `Whether to check for structural equality of messages between source and target locale.
|
|
126
|
-
This makes sure translations are formattable and are not missing any tokens.`).option("--follow-links", `Whether to follow symbolic links when traversing directories. Defaults to true for compatibility with pnpm symlinked node_modules. Use --no-follow-links to disable.`, true).action(async (filePatterns, opts) => {
|
|
127
|
-
debug("File pattern:", filePatterns);
|
|
128
|
-
debug("Options:", opts);
|
|
129
|
-
const files = globSync(filePatterns, {
|
|
130
|
-
ignore: opts.ignore,
|
|
131
|
-
followSymbolicLinks: opts.followLinks ?? true
|
|
132
|
-
});
|
|
133
|
-
if (!files.length) {
|
|
134
|
-
throw new Error(`No input file found with pattern ${filePatterns}`);
|
|
135
|
-
}
|
|
136
|
-
debug("Files to verify:", files);
|
|
137
|
-
await verify(files, opts);
|
|
138
|
-
});
|
|
139
|
-
if (argv.length < 3) {
|
|
140
|
-
program.help();
|
|
141
|
-
} else {
|
|
142
|
-
program.parse(argv);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
export default main;
|
package/src/compile.d.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { type Formatter } from "./formatters/index.js";
|
|
2
|
-
export type CompileFn = (msgs: any) => Record<string, string>;
|
|
3
|
-
export type PseudoLocale = "xx-LS" | "xx-AC" | "xx-HA" | "en-XA" | "en-XB";
|
|
4
|
-
export interface CompileCLIOpts extends Opts {
|
|
5
|
-
/**
|
|
6
|
-
* The target file that contains compiled messages.
|
|
7
|
-
*/
|
|
8
|
-
outFile?: string;
|
|
9
|
-
/**
|
|
10
|
-
* Whether to follow symbolic links when traversing directories.
|
|
11
|
-
* Defaults to true for compatibility with pnpm symlinked node_modules.
|
|
12
|
-
*/
|
|
13
|
-
followLinks?: boolean;
|
|
14
|
-
}
|
|
15
|
-
export interface Opts {
|
|
16
|
-
/**
|
|
17
|
-
* Whether to compile message into AST instead of just string
|
|
18
|
-
*/
|
|
19
|
-
ast?: boolean;
|
|
20
|
-
/**
|
|
21
|
-
* Whether to continue compiling messages after encountering an error.
|
|
22
|
-
* Any keys with errors will not be included in the output file.
|
|
23
|
-
*/
|
|
24
|
-
skipErrors?: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* Path to a formatter file that converts <translation_files> to
|
|
27
|
-
* `Record<string, string>` so we can compile.
|
|
28
|
-
*/
|
|
29
|
-
format?: string | Formatter<unknown>;
|
|
30
|
-
/**
|
|
31
|
-
* Whether to compile to pseudo locale
|
|
32
|
-
*/
|
|
33
|
-
pseudoLocale?: PseudoLocale;
|
|
34
|
-
/**
|
|
35
|
-
* Whether the parser to treat HTML/XML tags as string literal
|
|
36
|
-
* instead of parsing them as tag token.
|
|
37
|
-
* When this is false we only allow simple tags without
|
|
38
|
-
* any attributes
|
|
39
|
-
*/
|
|
40
|
-
ignoreTag?: boolean;
|
|
41
|
-
/**
|
|
42
|
-
* An AbortSignal to cancel the compilation
|
|
43
|
-
*/
|
|
44
|
-
signal?: AbortSignal;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Aggregate `inputFiles` into a single JSON blob and compile.
|
|
48
|
-
* Also checks for conflicting IDs.
|
|
49
|
-
* Then returns the serialized result as a `string` since key order
|
|
50
|
-
* makes a difference in some vendor.
|
|
51
|
-
* @param inputFiles Input files
|
|
52
|
-
* @param opts Options
|
|
53
|
-
* @returns serialized result in string format
|
|
54
|
-
*/
|
|
55
|
-
export declare function compile(inputFiles: string[], opts?: Opts): Promise<string>;
|
|
56
|
-
/**
|
|
57
|
-
* Aggregate `inputFiles` into a single JSON blob and compile.
|
|
58
|
-
* Also checks for conflicting IDs and write output to `outFile`.
|
|
59
|
-
* @param inputFiles Input files
|
|
60
|
-
* @param compileOpts options
|
|
61
|
-
* @returns A `Promise` that resolves if file was written successfully
|
|
62
|
-
*/
|
|
63
|
-
export default function compileAndWrite(inputFiles: string[], compileOpts?: CompileCLIOpts): Promise<void>;
|