@dittowords/cli 4.2.1 → 4.3.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/bin/pull.js +7 -4
- package/bin/pull.js.map +1 -1
- package/bin/pull.test.js +55 -127
- package/bin/pull.test.js.map +1 -1
- package/bin/types.js +2 -2
- package/bin/types.js.map +1 -1
- package/bin/utils/cleanFileName.js +8 -3
- package/bin/utils/cleanFileName.js.map +1 -1
- package/bin/utils/cleanFileName.test.js +15 -0
- package/bin/utils/cleanFileName.test.js.map +1 -0
- package/lib/pull.test.ts +67 -136
- package/lib/pull.ts +10 -3
- package/lib/types.ts +3 -0
- package/lib/utils/cleanFileName.test.ts +11 -0
- package/lib/utils/cleanFileName.ts +6 -4
- package/package.json +2 -2
package/bin/pull.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3cdae5df-bbfa-57e1-8775-9447478223bf")}catch(e){}}();
|
|
3
3
|
|
|
4
4
|
var __create = Object.create;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
@@ -92,6 +92,7 @@ const ensureEndsWithNewLine = (str) => str + (/[\r\n]$/.test(str) ? "" : "\n");
|
|
|
92
92
|
const writeFile = (path2, data) => new Promise((r) => import_fs.default.writeFile(path2, ensureEndsWithNewLine(data), r));
|
|
93
93
|
const SUPPORTED_FORMATS = [
|
|
94
94
|
"flat",
|
|
95
|
+
"nested",
|
|
95
96
|
"structured",
|
|
96
97
|
"android",
|
|
97
98
|
"ios-strings",
|
|
@@ -99,7 +100,7 @@ const SUPPORTED_FORMATS = [
|
|
|
99
100
|
"icu"
|
|
100
101
|
];
|
|
101
102
|
const IOS_FORMATS = ["ios-strings", "ios-stringsdict"];
|
|
102
|
-
const JSON_FORMATS = ["flat", "structured", "icu"];
|
|
103
|
+
const JSON_FORMATS = ["flat", "nested", "structured", "icu"];
|
|
103
104
|
const getJsonFormat = (formats) => {
|
|
104
105
|
const jsonFormats = formats.filter(
|
|
105
106
|
(f) => JSON_FORMATS.includes(f)
|
|
@@ -108,6 +109,7 @@ const getJsonFormat = (formats) => {
|
|
|
108
109
|
};
|
|
109
110
|
const FORMAT_EXTENSIONS = {
|
|
110
111
|
flat: ".json",
|
|
112
|
+
nested: ".json",
|
|
111
113
|
structured: ".json",
|
|
112
114
|
android: ".xml",
|
|
113
115
|
"ios-strings": ".strings",
|
|
@@ -125,6 +127,7 @@ const getJsonFormatIsValid = (data) => {
|
|
|
125
127
|
};
|
|
126
128
|
const getFormatDataIsValid = {
|
|
127
129
|
flat: getJsonFormatIsValid,
|
|
130
|
+
nested: getJsonFormatIsValid,
|
|
128
131
|
structured: getJsonFormatIsValid,
|
|
129
132
|
icu: getJsonFormatIsValid,
|
|
130
133
|
android: (data) => data.includes("<string"),
|
|
@@ -378,7 +381,7 @@ Fetching the latest text from ${(0, import_sourcesToText.default)(
|
|
|
378
381
|
});
|
|
379
382
|
const nameExt = getFormatExtension(format);
|
|
380
383
|
const nameBase = "components";
|
|
381
|
-
const nameFolder = `__${componentFolder.name}`;
|
|
384
|
+
const nameFolder = `__${(0, import_cleanFileName.cleanFileName)(componentFolder.name)}`;
|
|
382
385
|
const namePostfix = `__${variantApiId || "base"}`;
|
|
383
386
|
const fileName = (0, import_cleanFileName.cleanFileName)(
|
|
384
387
|
`${nameBase}${nameFolder}${namePostfix}${nameExt}`
|
|
@@ -544,4 +547,4 @@ var pull_default = {
|
|
|
544
547
|
});
|
|
545
548
|
//# sourceMappingURL=pull.js.map
|
|
546
549
|
|
|
547
|
-
//# debugId=
|
|
550
|
+
//# debugId=3cdae5df-bbfa-57e1-8775-9447478223bf
|
package/bin/pull.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/pull.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nimport ora from \"ora\";\nimport * as Sentry from \"@sentry/node\";\n\nimport { createApiClient } from \"./api\";\nimport config from \"./config\";\nimport consts from \"./consts\";\nimport output from \"./output\";\nimport { collectAndSaveToken } from \"./init/token\";\nimport sourcesToText from \"./utils/sourcesToText\";\nimport { generateJsDriver } from \"./utils/generateJsDriver\";\nimport { cleanFileName } from \"./utils/cleanFileName\";\nimport {\n SourceInformation,\n Token,\n Project,\n SupportedFormat,\n ComponentFolder,\n ComponentSource,\n Source,\n} from \"./types\";\nimport { fetchVariants } from \"./http/fetchVariants\";\nimport { quit } from \"./utils/quit\";\nimport { AxiosError } from \"axios\";\nimport { fetchComponentFolders } from \"./http/fetchComponentFolders\";\nimport { generateSwiftDriver } from \"./utils/generateSwiftDriver\";\nimport { generateIOSBundles } from \"./utils/generateIOSBundles\";\n\ninterface IRequestOptions {\n projects: Project[];\n format: SupportedFormat;\n status: string | undefined;\n richText?: boolean | undefined;\n token?: Token;\n options?: PullOptions;\n}\n\ninterface IRequestOptionsWithVariants extends IRequestOptions {\n variants: { apiID: string }[];\n}\n\nconst ensureEndsWithNewLine = (str: string) =>\n str + (/[\\r\\n]$/.test(str) ? \"\" : \"\\n\");\n\nexport const writeFile = (path: string, data: string) =>\n new Promise((r) => fs.writeFile(path, ensureEndsWithNewLine(data), r));\n\nconst SUPPORTED_FORMATS: SupportedFormat[] = [\n \"flat\",\n \"structured\",\n \"android\",\n \"ios-strings\",\n \"ios-stringsdict\",\n \"icu\",\n];\n\nexport type JSONFormat = \"flat\" | \"nested\" | \"structured\" | \"icu\";\n\nconst IOS_FORMATS: SupportedFormat[] = [\"ios-strings\", \"ios-stringsdict\"];\nconst JSON_FORMATS: JSONFormat[] = [\"flat\", \"structured\", \"icu\"];\n\nconst getJsonFormat = (formats: string[]): JSONFormat => {\n // edge case: multiple json formats specified\n // we should grab the last one\n const jsonFormats = formats.filter((f) =>\n JSON_FORMATS.includes(f as JSONFormat)\n ) as JSONFormat[];\n\n return jsonFormats[jsonFormats.length - 1] || \"flat\";\n};\n\nconst FORMAT_EXTENSIONS = {\n flat: \".json\",\n structured: \".json\",\n android: \".xml\",\n \"ios-strings\": \".strings\",\n \"ios-stringsdict\": \".stringsdict\",\n icu: \".json\",\n};\n\nconst getJsonFormatIsValid = (data: string) => {\n try {\n return Object.keys(JSON.parse(data)).some(\n (k) => !k.startsWith(\"__variant\")\n );\n } catch {\n return false;\n }\n};\n\n// exported for test usage only\nexport const getFormatDataIsValid = {\n flat: getJsonFormatIsValid,\n structured: getJsonFormatIsValid,\n icu: getJsonFormatIsValid,\n android: (data: string) => data.includes(\"<string\"),\n \"ios-strings\": (data: string) => data.includes(`\" = \"`),\n \"ios-stringsdict\": (data: string) => data.includes(\"<key>\"),\n};\n\nconst getFormat = (\n formatFromSource: string | string[] | undefined\n): SupportedFormat[] => {\n const formats = (\n Array.isArray(formatFromSource) ? formatFromSource : [formatFromSource]\n ).filter((format) =>\n SUPPORTED_FORMATS.includes(format as SupportedFormat)\n ) as SupportedFormat[];\n\n if (formats.length) {\n return formats;\n }\n\n return [\"flat\"];\n};\n\nconst getFormatExtension = (format: SupportedFormat) => {\n return FORMAT_EXTENSIONS[format];\n};\n\nconst DEFAULT_FORMAT_KEYS = [\"projects\", \"exported_at\"];\nconst hasVariantData = (data: any) => {\n const hasTopLevelKeys =\n Object.keys(data).filter((key) => !DEFAULT_FORMAT_KEYS.includes(key))\n .length > 0;\n\n const hasProjectKeys = data.projects && Object.keys(data.projects).length > 0;\n\n return hasTopLevelKeys || hasProjectKeys;\n};\n\nasync function askForAnotherToken() {\n config.deleteToken(consts.CONFIG_FILE, consts.API_HOST);\n const message =\n \"Looks like the API key you have saved no longer works. Please enter another one.\";\n await collectAndSaveToken(message);\n}\n\n/**\n * For a given variant:\n * - if format is unspecified, fetch data for all projects from `/projects` and\n * save in `{variantApiId}.json`\n * - if format is `flat` or `structured`, fetch data for each project from `/project/:project_id` and\n * save in `{projectName}-${variantApiId}.json`\n */\nasync function downloadAndSaveVariant(\n variantApiId: string | null,\n requestOptions: IRequestOptions\n) {\n const { projects, format, status, richText, token } = requestOptions;\n const api = createApiClient();\n const params: Record<string, string | null> = { variant: variantApiId };\n if (format) params.format = format;\n if (richText) params.includeRichText = richText.toString();\n\n // Root-level status gets set as the default if specified\n if (status) params.status = status;\n\n const savedMessages = await Promise.all(\n projects.map(async (project) => {\n const projectParams = { ...params };\n // If project-level status is specified, overrides root-level status\n if (project.status) projectParams.status = project.status;\n if (project.exclude_components)\n projectParams.exclude_components = String(project.exclude_components);\n\n const { data } = await api.get(`/v1/projects/${project.id}`, {\n params: projectParams,\n headers: { Authorization: `token ${token}` },\n });\n\n if (!hasVariantData(data)) {\n return \"\";\n }\n\n const extension = getFormatExtension(format);\n\n const filename = cleanFileName(\n project.fileName + (\"__\" + (variantApiId || \"base\")) + extension\n );\n const filepath = path.join(consts.TEXT_DIR, filename);\n\n let dataString = data;\n if (extension === \".json\") {\n dataString = JSON.stringify(data, null, 2);\n }\n\n const dataIsValid = getFormatDataIsValid[format];\n if (!dataIsValid(dataString)) {\n return \"\";\n }\n\n await writeFile(filepath, dataString);\n return getSavedMessage(filename);\n })\n );\n\n return savedMessages.join(\"\");\n}\n\nasync function downloadAndSaveVariants(\n requestOptions: IRequestOptionsWithVariants\n) {\n const messages = await Promise.all([\n downloadAndSaveVariant(null, requestOptions),\n ...requestOptions.variants.map(({ apiID }: { apiID: string }) =>\n downloadAndSaveVariant(apiID, requestOptions)\n ),\n ]);\n\n return messages.join(\"\");\n}\n\nasync function downloadAndSaveBase(requestOptions: IRequestOptions) {\n const { projects, format, status, richText, token, options } = requestOptions;\n\n const api = createApiClient();\n const params = { ...options?.meta };\n if (format) params.format = format;\n if (richText) params.includeRichText = richText.toString();\n\n // Root-level status gets set as the default if specified\n if (status) params.status = status;\n\n const savedMessages = await Promise.all(\n projects.map(async (project) => {\n const projectParams = { ...params };\n // If project-level status is specified, overrides root-level status\n if (project.status) projectParams.status = project.status;\n if (project.exclude_components)\n projectParams.exclude_components = String(project.exclude_components);\n\n const { data } = await api.get(`/v1/projects/${project.id}`, {\n params: projectParams,\n headers: { Authorization: `token ${token}` },\n });\n\n const extension = getFormatExtension(format);\n const filename = cleanFileName(`${project.fileName}__base${extension}`);\n const filepath = path.join(consts.TEXT_DIR, filename);\n\n let dataString = data;\n if (extension === \".json\") {\n dataString = JSON.stringify(data, null, 2);\n }\n\n const dataIsValid = getFormatDataIsValid[format];\n if (!dataIsValid(dataString)) {\n return \"\";\n }\n\n await writeFile(filepath, dataString);\n return getSavedMessage(filename);\n })\n );\n\n return savedMessages.join(\"\");\n}\n\nfunction getSavedMessage(file: string) {\n return `Successfully saved to ${output.info(file)}\\n`;\n}\n\nfunction cleanOutputFiles() {\n if (!fs.existsSync(consts.TEXT_DIR)) {\n fs.mkdirSync(consts.TEXT_DIR);\n }\n\n const directoryContents = fs.readdirSync(consts.TEXT_DIR, {\n withFileTypes: true,\n });\n\n directoryContents.forEach((item) => {\n if (item.isDirectory() && /\\.lproj$/.test(item.name)) {\n return fs.rmSync(path.resolve(consts.TEXT_DIR, item.name), {\n recursive: true,\n force: true,\n });\n }\n\n if (\n item.isFile() &&\n /\\.js(on)?|\\.xml|\\.strings(dict)?$|\\.swift$/.test(item.name)\n ) {\n return fs.unlinkSync(path.resolve(consts.TEXT_DIR, item.name));\n }\n });\n\n return \"Cleaning old output files..\\n\";\n}\n\nasync function downloadAndSave(\n source: SourceInformation,\n token?: Token,\n options?: PullOptions\n) {\n const api = createApiClient();\n const {\n validProjects,\n format: formatFromSource,\n shouldFetchComponentLibrary,\n status,\n richText,\n componentFolders: specifiedComponentFolders,\n componentRoot,\n localeByVariantApiId,\n } = source;\n\n const formats = getFormat(formatFromSource);\n\n const hasJSONFormat = formats.some((f) =>\n JSON_FORMATS.includes(f as JSONFormat)\n );\n const hasIOSFormat = formats.some((f) => IOS_FORMATS.includes(f));\n const shouldGenerateIOSBundles = hasIOSFormat && localeByVariantApiId;\n\n const shouldLogOutputFiles = !shouldGenerateIOSBundles;\n\n let msg = \"\";\n const spinner = ora(msg);\n spinner.start();\n\n const [variants, allComponentFoldersResponse] = await Promise.all([\n fetchVariants(source),\n fetchComponentFolders({}),\n ]);\n\n const allComponentFolders = Object.entries(\n allComponentFoldersResponse\n ).reduce(\n (acc, [id, name]) => acc.concat([{ id, name }]),\n [] as ComponentFolder[]\n );\n\n try {\n msg += cleanOutputFiles();\n msg += `\\nFetching the latest text from ${sourcesToText(\n validProjects,\n shouldFetchComponentLibrary\n )}\\n`;\n\n const meta = options ? options.meta : {};\n\n const rootRequest = {\n id: \"__root__\",\n name: \"Root\",\n // componentRoot can be a boolean or an object\n status:\n typeof source.componentRoot === \"object\"\n ? source.componentRoot.status\n : undefined,\n };\n\n let componentFolderRequests: ComponentFolder[] = [];\n\n // there's a lot of complex logic here, and it's tempting to want to\n // simplify it. however, it's difficult to get rid of the complexity\n // without sacrificing specificity and expressiveness.\n //\n // if folders specified..\n if (specifiedComponentFolders) {\n switch (componentRoot) {\n // .. and no root specified, you only get components in the specified folders\n case undefined:\n case false:\n componentFolderRequests.push(...specifiedComponentFolders);\n break;\n // .. and root specified, you get components in folders and the root\n default:\n componentFolderRequests.push(...specifiedComponentFolders);\n componentFolderRequests.push(rootRequest);\n break;\n }\n }\n // if no folders specified..\n else {\n switch (componentRoot) {\n // .. and no root specified, you get all components including those in folders\n case undefined:\n componentFolderRequests.push(...allComponentFolders);\n componentFolderRequests.push(rootRequest);\n break;\n // .. and root specified as false, you only get components in folders\n case false:\n componentFolderRequests.push(...allComponentFolders);\n break;\n // .. and root specified as true or config object, you only get components in the root\n default:\n componentFolderRequests.push(rootRequest);\n break;\n }\n }\n\n // this array is populated while fetching from the component library and is used when\n // generating the index.js driver file\n const componentSources: ComponentSource[] = [];\n\n async function fetchComponentLibrary(format: SupportedFormat) {\n // Always include a variant with an apiID of undefined to ensure that we\n // fetch the base text for the component library.\n const componentVariants = [{ apiID: undefined }, ...(variants || [])];\n\n const params = new URLSearchParams();\n if (options?.meta)\n Object.entries(options.meta).forEach(([k, v]) => params.append(k, v));\n if (format) params.append(\"format\", format);\n if (richText) params.append(\"includeRichText\", richText.toString());\n\n // Root-level status gets set as the default if specified\n if (status) params.append(\"status\", status);\n\n const messagePromises: Promise<string>[] = [];\n\n componentVariants.forEach(({ apiID: variantApiId }) => {\n messagePromises.push(\n ...componentFolderRequests.map(async (componentFolder) => {\n const componentFolderParams = new URLSearchParams(params);\n\n if (variantApiId)\n componentFolderParams.append(\"variant\", variantApiId);\n\n // If folder-level status is specified, overrides root-level status\n if (componentFolder.status)\n componentFolderParams.append(\"status\", componentFolder.status);\n\n const url =\n componentFolder.id === \"__root__\"\n ? \"/v1/components?root_only=true\"\n : `/v1/component-folders/${componentFolder.id}/components`;\n\n const { data } = await api.get(url, {\n params: componentFolderParams,\n });\n\n const nameExt = getFormatExtension(format);\n const nameBase = \"components\";\n const nameFolder = `__${componentFolder.name}`;\n const namePostfix = `__${variantApiId || \"base\"}`;\n\n const fileName = cleanFileName(\n `${nameBase}${nameFolder}${namePostfix}${nameExt}`\n );\n const filePath = path.join(consts.TEXT_DIR, fileName);\n\n let dataString = data;\n if (nameExt === \".json\") {\n dataString = JSON.stringify(data, null, 2);\n }\n\n const dataIsValid = getFormatDataIsValid[format];\n if (!dataIsValid(dataString)) {\n return \"\";\n }\n\n await writeFile(filePath, dataString);\n\n componentSources.push({\n type: \"components\",\n id: \"ditto_component_library\",\n name: \"ditto_component_library\",\n fileName,\n variant: variantApiId || \"base\",\n });\n\n return getSavedMessage(fileName);\n })\n );\n });\n\n const messages = await Promise.all(messagePromises);\n if (shouldLogOutputFiles) {\n msg += messages.join(\"\");\n }\n }\n\n if (shouldFetchComponentLibrary) {\n for (const format of formats) {\n await fetchComponentLibrary(format);\n }\n }\n\n async function fetchProjects(format: SupportedFormat) {\n let result = \"\";\n if (variants) {\n result = await downloadAndSaveVariants({\n variants,\n projects: validProjects,\n format,\n status,\n richText,\n token,\n });\n } else {\n result = await downloadAndSaveBase({\n projects: validProjects,\n format,\n status,\n richText,\n token,\n options: {\n meta,\n },\n });\n }\n\n if (shouldLogOutputFiles) {\n msg += result;\n }\n }\n\n if (validProjects.length) {\n for (const format of formats) {\n await fetchProjects(format);\n }\n }\n\n const sources: Source[] = [...validProjects, ...componentSources];\n\n if (hasJSONFormat) msg += generateJsDriver(sources, getJsonFormat(formats));\n\n if (shouldGenerateIOSBundles) {\n msg += \"iOS locale information detected, generating bundles..\\n\\n\";\n msg += await generateIOSBundles(localeByVariantApiId);\n msg += await generateSwiftDriver(source);\n }\n\n msg += `\\n\\n${output.success(\"Done\")}!`;\n\n spinner.stop();\n return console.log(msg);\n } catch (e: any) {\n console.error(e);\n\n spinner.stop();\n let error = e.message;\n if (e.response && e.response.status === 404) {\n await askForAnotherToken();\n pull();\n return;\n }\n if (e.response && e.response.status === 401) {\n error = \"You don't have access to the selected projects\";\n msg = `${output.errorText(error)}.\\nChoose others using the ${output.info(\n \"project\"\n )} command, or update your API key.`;\n return console.log(msg);\n }\n if (e.response && e.response.status === 403) {\n error =\n \"One or more of the requested projects don't have Developer Mode enabled\";\n msg = `${output.errorText(\n error\n )}.\\nPlease choose different projects using the ${output.info(\n \"project\"\n )} command, or turn on Developer Mode for all selected projects. Learn more here: ${output.subtle(\n \"https://www.dittowords.com/docs/ditto-developer-mode\"\n )}.`;\n return console.log(msg);\n }\n if (e.response && e.response.status === 400) {\n error = \"projects not found\";\n }\n msg = `We hit an error fetching text from the projects: ${output.errorText(\n error\n )}.\\nChoose others using the ${output.info(\"project\")} command.`;\n return console.log(msg);\n }\n}\n\nexport interface PullOptions {\n meta?: Record<string, string>;\n}\n\nexport const pull = async (options?: PullOptions) => {\n const meta = options ? options.meta : {};\n const token = config.getToken(consts.CONFIG_FILE, consts.API_HOST);\n const sourceInformation = config.parseSourceInformation();\n\n try {\n return await downloadAndSave(sourceInformation, token, { meta });\n } catch (e) {\n const eventId = Sentry.captureException(e);\n const eventStr = `\\n\\nError ID: ${output.info(eventId)}`;\n if (e instanceof AxiosError) {\n return quit(\n output.errorText(\n \"Something went wrong connecting to Ditto servers. Please contact support or try again later.\"\n ) + eventStr\n );\n }\n\n return quit(\n output.errorText(\n \"Something went wrong. Please contact support or try again later.\"\n ) + eventStr\n );\n }\n};\n\nexport default {\n pull,\n _testing: {\n cleanOutputFiles,\n downloadAndSaveVariant,\n downloadAndSaveVariants,\n downloadAndSaveBase,\n },\n};\n"],"names":["path","fs","config","consts","output","ora","sourcesToText"],"mappings":"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","debug_id":"c8688159-ec02-55a6-900a-28847c915aa3"}
|
|
1
|
+
{"version":3,"sources":["../lib/pull.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nimport ora from \"ora\";\nimport * as Sentry from \"@sentry/node\";\n\nimport { createApiClient } from \"./api\";\nimport config from \"./config\";\nimport consts from \"./consts\";\nimport output from \"./output\";\nimport { collectAndSaveToken } from \"./init/token\";\nimport sourcesToText from \"./utils/sourcesToText\";\nimport { generateJsDriver } from \"./utils/generateJsDriver\";\nimport { cleanFileName } from \"./utils/cleanFileName\";\nimport {\n SourceInformation,\n Token,\n Project,\n SupportedFormat,\n SupportedExtension,\n ComponentFolder,\n ComponentSource,\n Source,\n} from \"./types\";\nimport { fetchVariants } from \"./http/fetchVariants\";\nimport { quit } from \"./utils/quit\";\nimport { AxiosError } from \"axios\";\nimport { fetchComponentFolders } from \"./http/fetchComponentFolders\";\nimport { generateSwiftDriver } from \"./utils/generateSwiftDriver\";\nimport { generateIOSBundles } from \"./utils/generateIOSBundles\";\n\ninterface IRequestOptions {\n projects: Project[];\n format: SupportedFormat;\n status: string | undefined;\n richText?: boolean | undefined;\n token?: Token;\n options?: PullOptions;\n}\n\ninterface IRequestOptionsWithVariants extends IRequestOptions {\n variants: { apiID: string }[];\n}\n\nconst ensureEndsWithNewLine = (str: string) =>\n str + (/[\\r\\n]$/.test(str) ? \"\" : \"\\n\");\n\nexport const writeFile = (path: string, data: string) =>\n new Promise((r) => fs.writeFile(path, ensureEndsWithNewLine(data), r));\n\nconst SUPPORTED_FORMATS: SupportedFormat[] = [\n \"flat\",\n \"nested\",\n \"structured\",\n \"android\",\n \"ios-strings\",\n \"ios-stringsdict\",\n \"icu\",\n];\n\nexport type JSONFormat = \"flat\" | \"nested\" | \"structured\" | \"icu\";\n\nconst IOS_FORMATS: SupportedFormat[] = [\"ios-strings\", \"ios-stringsdict\"];\nconst JSON_FORMATS: JSONFormat[] = [\"flat\", \"nested\", \"structured\", \"icu\"];\n\nconst getJsonFormat = (formats: string[]): JSONFormat => {\n // edge case: multiple json formats specified\n // we should grab the last one\n const jsonFormats = formats.filter((f) =>\n JSON_FORMATS.includes(f as JSONFormat)\n ) as JSONFormat[];\n\n return jsonFormats[jsonFormats.length - 1] || \"flat\";\n};\n\nconst FORMAT_EXTENSIONS: Record<SupportedFormat, SupportedExtension> = {\n flat: \".json\",\n nested: \".json\",\n structured: \".json\",\n android: \".xml\",\n \"ios-strings\": \".strings\",\n \"ios-stringsdict\": \".stringsdict\",\n icu: \".json\",\n};\n\nconst getJsonFormatIsValid = (data: string) => {\n try {\n return Object.keys(JSON.parse(data)).some(\n (k) => !k.startsWith(\"__variant\")\n );\n } catch {\n return false;\n }\n};\n\n// exported for test usage only\nexport const getFormatDataIsValid = {\n flat: getJsonFormatIsValid,\n nested: getJsonFormatIsValid,\n structured: getJsonFormatIsValid,\n icu: getJsonFormatIsValid,\n android: (data: string) => data.includes(\"<string\"),\n \"ios-strings\": (data: string) => data.includes(`\" = \"`),\n \"ios-stringsdict\": (data: string) => data.includes(\"<key>\"),\n};\n\nconst getFormat = (\n formatFromSource: string | string[] | undefined\n): SupportedFormat[] => {\n const formats = (\n Array.isArray(formatFromSource) ? formatFromSource : [formatFromSource]\n ).filter((format) =>\n SUPPORTED_FORMATS.includes(format as SupportedFormat)\n ) as SupportedFormat[];\n\n if (formats.length) {\n return formats;\n }\n\n return [\"flat\"];\n};\n\nconst getFormatExtension = (format: SupportedFormat) => {\n return FORMAT_EXTENSIONS[format];\n};\n\nconst DEFAULT_FORMAT_KEYS = [\"projects\", \"exported_at\"];\nconst hasVariantData = (data: any) => {\n const hasTopLevelKeys =\n Object.keys(data).filter((key) => !DEFAULT_FORMAT_KEYS.includes(key))\n .length > 0;\n\n const hasProjectKeys = data.projects && Object.keys(data.projects).length > 0;\n\n return hasTopLevelKeys || hasProjectKeys;\n};\n\nasync function askForAnotherToken() {\n config.deleteToken(consts.CONFIG_FILE, consts.API_HOST);\n const message =\n \"Looks like the API key you have saved no longer works. Please enter another one.\";\n await collectAndSaveToken(message);\n}\n\n/**\n * For a given variant:\n * - if format is unspecified, fetch data for all projects from `/projects` and\n * save in `{variantApiId}.json`\n * - if format is `flat` or `structured`, fetch data for each project from `/project/:project_id` and\n * save in `{projectName}-${variantApiId}.json`\n */\nasync function downloadAndSaveVariant(\n variantApiId: string | null,\n requestOptions: IRequestOptions\n) {\n const { projects, format, status, richText, token } = requestOptions;\n const api = createApiClient();\n const params: Record<string, string | null> = { variant: variantApiId };\n if (format) params.format = format;\n if (richText) params.includeRichText = richText.toString();\n\n // Root-level status gets set as the default if specified\n if (status) params.status = status;\n\n const savedMessages = await Promise.all(\n projects.map(async (project) => {\n const projectParams = { ...params };\n // If project-level status is specified, overrides root-level status\n if (project.status) projectParams.status = project.status;\n if (project.exclude_components)\n projectParams.exclude_components = String(project.exclude_components);\n\n const { data } = await api.get(`/v1/projects/${project.id}`, {\n params: projectParams,\n headers: { Authorization: `token ${token}` },\n });\n\n if (!hasVariantData(data)) {\n return \"\";\n }\n\n const extension = getFormatExtension(format);\n\n const filename = cleanFileName(\n project.fileName + (\"__\" + (variantApiId || \"base\")) + extension\n );\n const filepath = path.join(consts.TEXT_DIR, filename);\n\n let dataString = data;\n if (extension === \".json\") {\n dataString = JSON.stringify(data, null, 2);\n }\n\n const dataIsValid = getFormatDataIsValid[format];\n if (!dataIsValid(dataString)) {\n return \"\";\n }\n\n await writeFile(filepath, dataString);\n return getSavedMessage(filename);\n })\n );\n\n return savedMessages.join(\"\");\n}\n\nasync function downloadAndSaveVariants(\n requestOptions: IRequestOptionsWithVariants\n) {\n const messages = await Promise.all([\n downloadAndSaveVariant(null, requestOptions),\n ...requestOptions.variants.map(({ apiID }: { apiID: string }) =>\n downloadAndSaveVariant(apiID, requestOptions)\n ),\n ]);\n\n return messages.join(\"\");\n}\n\nasync function downloadAndSaveBase(requestOptions: IRequestOptions) {\n const { projects, format, status, richText, token, options } = requestOptions;\n\n const api = createApiClient();\n const params = { ...options?.meta };\n if (format) params.format = format;\n if (richText) params.includeRichText = richText.toString();\n\n // Root-level status gets set as the default if specified\n if (status) params.status = status;\n\n const savedMessages = await Promise.all(\n projects.map(async (project) => {\n const projectParams = { ...params };\n // If project-level status is specified, overrides root-level status\n if (project.status) projectParams.status = project.status;\n if (project.exclude_components)\n projectParams.exclude_components = String(project.exclude_components);\n\n const { data } = await api.get(`/v1/projects/${project.id}`, {\n params: projectParams,\n headers: { Authorization: `token ${token}` },\n });\n\n const extension = getFormatExtension(format);\n const filename = cleanFileName(`${project.fileName}__base${extension}`);\n const filepath = path.join(consts.TEXT_DIR, filename);\n\n let dataString = data;\n if (extension === \".json\") {\n dataString = JSON.stringify(data, null, 2);\n }\n\n const dataIsValid = getFormatDataIsValid[format];\n if (!dataIsValid(dataString)) {\n return \"\";\n }\n\n await writeFile(filepath, dataString);\n return getSavedMessage(filename);\n })\n );\n\n return savedMessages.join(\"\");\n}\n\nfunction getSavedMessage(file: string) {\n return `Successfully saved to ${output.info(file)}\\n`;\n}\n\nfunction cleanOutputFiles() {\n if (!fs.existsSync(consts.TEXT_DIR)) {\n fs.mkdirSync(consts.TEXT_DIR);\n }\n\n const directoryContents = fs.readdirSync(consts.TEXT_DIR, {\n withFileTypes: true,\n });\n\n directoryContents.forEach((item) => {\n if (item.isDirectory() && /\\.lproj$/.test(item.name)) {\n return fs.rmSync(path.resolve(consts.TEXT_DIR, item.name), {\n recursive: true,\n force: true,\n });\n }\n\n if (\n item.isFile() &&\n /\\.js(on)?|\\.xml|\\.strings(dict)?$|\\.swift$/.test(item.name)\n ) {\n return fs.unlinkSync(path.resolve(consts.TEXT_DIR, item.name));\n }\n });\n\n return \"Cleaning old output files..\\n\";\n}\n\nasync function downloadAndSave(\n source: SourceInformation,\n token?: Token,\n options?: PullOptions\n) {\n const api = createApiClient();\n const {\n validProjects,\n format: formatFromSource,\n shouldFetchComponentLibrary,\n status,\n richText,\n componentFolders: specifiedComponentFolders,\n componentRoot,\n localeByVariantApiId,\n } = source;\n\n const formats = getFormat(formatFromSource);\n\n const hasJSONFormat = formats.some((f) =>\n JSON_FORMATS.includes(f as JSONFormat)\n );\n const hasIOSFormat = formats.some((f) => IOS_FORMATS.includes(f));\n const shouldGenerateIOSBundles = hasIOSFormat && localeByVariantApiId;\n\n const shouldLogOutputFiles = !shouldGenerateIOSBundles;\n\n let msg = \"\";\n const spinner = ora(msg);\n spinner.start();\n\n const [variants, allComponentFoldersResponse] = await Promise.all([\n fetchVariants(source),\n fetchComponentFolders({}),\n ]);\n\n const allComponentFolders = Object.entries(\n allComponentFoldersResponse\n ).reduce(\n (acc, [id, name]) => acc.concat([{ id, name }]),\n [] as ComponentFolder[]\n );\n\n try {\n msg += cleanOutputFiles();\n msg += `\\nFetching the latest text from ${sourcesToText(\n validProjects,\n shouldFetchComponentLibrary\n )}\\n`;\n\n const meta = options ? options.meta : {};\n\n const rootRequest = {\n id: \"__root__\",\n name: \"Root\",\n // componentRoot can be a boolean or an object\n status:\n typeof source.componentRoot === \"object\"\n ? source.componentRoot.status\n : undefined,\n };\n\n let componentFolderRequests: ComponentFolder[] = [];\n\n // there's a lot of complex logic here, and it's tempting to want to\n // simplify it. however, it's difficult to get rid of the complexity\n // without sacrificing specificity and expressiveness.\n //\n // if folders specified..\n if (specifiedComponentFolders) {\n switch (componentRoot) {\n // .. and no root specified, you only get components in the specified folders\n case undefined:\n case false:\n componentFolderRequests.push(...specifiedComponentFolders);\n break;\n // .. and root specified, you get components in folders and the root\n default:\n componentFolderRequests.push(...specifiedComponentFolders);\n componentFolderRequests.push(rootRequest);\n break;\n }\n }\n // if no folders specified..\n else {\n switch (componentRoot) {\n // .. and no root specified, you get all components including those in folders\n case undefined:\n componentFolderRequests.push(...allComponentFolders);\n componentFolderRequests.push(rootRequest);\n break;\n // .. and root specified as false, you only get components in folders\n case false:\n componentFolderRequests.push(...allComponentFolders);\n break;\n // .. and root specified as true or config object, you only get components in the root\n default:\n componentFolderRequests.push(rootRequest);\n break;\n }\n }\n\n // this array is populated while fetching from the component library and is used when\n // generating the index.js driver file\n const componentSources: ComponentSource[] = [];\n\n async function fetchComponentLibrary(format: SupportedFormat) {\n // Always include a variant with an apiID of undefined to ensure that we\n // fetch the base text for the component library.\n const componentVariants = [{ apiID: undefined }, ...(variants || [])];\n\n const params = new URLSearchParams();\n if (options?.meta)\n Object.entries(options.meta).forEach(([k, v]) => params.append(k, v));\n if (format) params.append(\"format\", format);\n if (richText) params.append(\"includeRichText\", richText.toString());\n\n // Root-level status gets set as the default if specified\n if (status) params.append(\"status\", status);\n\n const messagePromises: Promise<string>[] = [];\n\n componentVariants.forEach(({ apiID: variantApiId }) => {\n messagePromises.push(\n ...componentFolderRequests.map(async (componentFolder) => {\n const componentFolderParams = new URLSearchParams(params);\n\n if (variantApiId)\n componentFolderParams.append(\"variant\", variantApiId);\n\n // If folder-level status is specified, overrides root-level status\n if (componentFolder.status)\n componentFolderParams.append(\"status\", componentFolder.status);\n\n const url =\n componentFolder.id === \"__root__\"\n ? \"/v1/components?root_only=true\"\n : `/v1/component-folders/${componentFolder.id}/components`;\n\n const { data } = await api.get(url, {\n params: componentFolderParams,\n });\n\n const nameExt = getFormatExtension(format);\n const nameBase = \"components\";\n\n // we need to clean the folder name by itself first, otherwise we can\n // end up with \"empty\" words and weird hyphenation.\n const nameFolder = `__${cleanFileName(componentFolder.name)}`;\n const namePostfix = `__${variantApiId || \"base\"}`;\n\n const fileName = cleanFileName(\n `${nameBase}${nameFolder}${namePostfix}${nameExt}`\n );\n const filePath = path.join(consts.TEXT_DIR, fileName);\n\n let dataString = data;\n if (nameExt === \".json\") {\n dataString = JSON.stringify(data, null, 2);\n }\n\n const dataIsValid = getFormatDataIsValid[format];\n if (!dataIsValid(dataString)) {\n return \"\";\n }\n\n await writeFile(filePath, dataString);\n\n componentSources.push({\n type: \"components\",\n id: \"ditto_component_library\",\n name: \"ditto_component_library\",\n fileName,\n variant: variantApiId || \"base\",\n });\n\n return getSavedMessage(fileName);\n })\n );\n });\n\n const messages = await Promise.all(messagePromises);\n if (shouldLogOutputFiles) {\n msg += messages.join(\"\");\n }\n }\n\n if (shouldFetchComponentLibrary) {\n for (const format of formats) {\n await fetchComponentLibrary(format);\n }\n }\n\n async function fetchProjects(format: SupportedFormat) {\n let result = \"\";\n if (variants) {\n result = await downloadAndSaveVariants({\n variants,\n projects: validProjects,\n format,\n status,\n richText,\n token,\n });\n } else {\n result = await downloadAndSaveBase({\n projects: validProjects,\n format,\n status,\n richText,\n token,\n options: {\n meta,\n },\n });\n }\n\n if (shouldLogOutputFiles) {\n msg += result;\n }\n }\n\n if (validProjects.length) {\n for (const format of formats) {\n await fetchProjects(format);\n }\n }\n\n const sources: Source[] = [...validProjects, ...componentSources];\n\n if (hasJSONFormat) msg += generateJsDriver(sources, getJsonFormat(formats));\n\n if (shouldGenerateIOSBundles) {\n msg += \"iOS locale information detected, generating bundles..\\n\\n\";\n msg += await generateIOSBundles(localeByVariantApiId);\n msg += await generateSwiftDriver(source);\n }\n\n msg += `\\n\\n${output.success(\"Done\")}!`;\n\n spinner.stop();\n return console.log(msg);\n } catch (e: any) {\n console.error(e);\n\n spinner.stop();\n let error = e.message;\n if (e.response && e.response.status === 404) {\n await askForAnotherToken();\n pull();\n return;\n }\n if (e.response && e.response.status === 401) {\n error = \"You don't have access to the selected projects\";\n msg = `${output.errorText(error)}.\\nChoose others using the ${output.info(\n \"project\"\n )} command, or update your API key.`;\n return console.log(msg);\n }\n if (e.response && e.response.status === 403) {\n error =\n \"One or more of the requested projects don't have Developer Mode enabled\";\n msg = `${output.errorText(\n error\n )}.\\nPlease choose different projects using the ${output.info(\n \"project\"\n )} command, or turn on Developer Mode for all selected projects. Learn more here: ${output.subtle(\n \"https://www.dittowords.com/docs/ditto-developer-mode\"\n )}.`;\n return console.log(msg);\n }\n if (e.response && e.response.status === 400) {\n error = \"projects not found\";\n }\n msg = `We hit an error fetching text from the projects: ${output.errorText(\n error\n )}.\\nChoose others using the ${output.info(\"project\")} command.`;\n return console.log(msg);\n }\n}\n\nexport interface PullOptions {\n meta?: Record<string, string>;\n}\n\nexport const pull = async (options?: PullOptions) => {\n const meta = options ? options.meta : {};\n const token = config.getToken(consts.CONFIG_FILE, consts.API_HOST);\n const sourceInformation = config.parseSourceInformation();\n\n try {\n return await downloadAndSave(sourceInformation, token, { meta });\n } catch (e) {\n const eventId = Sentry.captureException(e);\n const eventStr = `\\n\\nError ID: ${output.info(eventId)}`;\n if (e instanceof AxiosError) {\n return quit(\n output.errorText(\n \"Something went wrong connecting to Ditto servers. Please contact support or try again later.\"\n ) + eventStr\n );\n }\n\n return quit(\n output.errorText(\n \"Something went wrong. Please contact support or try again later.\"\n ) + eventStr\n );\n }\n};\n\nexport default {\n pull,\n _testing: {\n cleanOutputFiles,\n downloadAndSaveVariant,\n downloadAndSaveVariants,\n downloadAndSaveBase,\n },\n};\n"],"names":["path","fs","config","consts","output","ora","sourcesToText"],"mappings":"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","debug_id":"3cdae5df-bbfa-57e1-8775-9447478223bf"}
|
package/bin/pull.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3d53a7f0-4cb6-57db-948f-db309cf2611a")}catch(e){}}();
|
|
3
3
|
|
|
4
4
|
var __create = Object.create;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
@@ -103,127 +103,20 @@ describe("downloadAndSaveBase", () => {
|
|
|
103
103
|
beforeEach(() => {
|
|
104
104
|
cleanOutputDir();
|
|
105
105
|
});
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
get: () => ({ data: mockData })
|
|
112
|
-
}));
|
|
113
|
-
const output = yield downloadAndSaveBase({
|
|
114
|
-
projects: testProjects,
|
|
115
|
-
format: "flat"
|
|
116
|
-
});
|
|
117
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
118
|
-
const directoryContents = import_fs.default.readdirSync(import_consts.default.TEXT_DIR);
|
|
119
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
120
|
-
expect(directoryContents.every((f) => f.endsWith(".json"))).toBe(true);
|
|
121
|
-
expect(
|
|
122
|
-
JSON.parse(
|
|
123
|
-
import_fs.default.readFileSync(
|
|
124
|
-
import_path.default.resolve(import_consts.default.TEXT_DIR, directoryContents[0]),
|
|
125
|
-
"utf8"
|
|
126
|
-
)
|
|
127
|
-
)
|
|
128
|
-
).toEqual(mockData);
|
|
129
|
-
}));
|
|
130
|
-
it("writes the structured format to disk", () => __async(exports, null, function* () {
|
|
131
|
-
const mockData = {
|
|
132
|
-
hello: { text: "world" }
|
|
133
|
-
};
|
|
134
|
-
import_api.createApiClient.mockImplementation(() => ({
|
|
135
|
-
get: () => ({ data: mockData })
|
|
136
|
-
}));
|
|
137
|
-
const output = yield downloadAndSaveBase({
|
|
138
|
-
projects: testProjects,
|
|
139
|
-
format: "structured"
|
|
140
|
-
});
|
|
141
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
142
|
-
const directoryContents = import_fs.default.readdirSync(import_consts.default.TEXT_DIR);
|
|
143
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
144
|
-
expect(directoryContents.every((f) => f.endsWith(".json"))).toBe(true);
|
|
145
|
-
expect(
|
|
146
|
-
JSON.parse(
|
|
147
|
-
import_fs.default.readFileSync(
|
|
148
|
-
import_path.default.resolve(import_consts.default.TEXT_DIR, directoryContents[0]),
|
|
149
|
-
"utf8"
|
|
150
|
-
)
|
|
151
|
-
)
|
|
152
|
-
).toEqual(mockData);
|
|
153
|
-
}));
|
|
154
|
-
it("writes the icu format to disk", () => __async(exports, null, function* () {
|
|
155
|
-
const mockData = {
|
|
156
|
-
hello: "world"
|
|
157
|
-
};
|
|
158
|
-
import_api.createApiClient.mockImplementation(() => ({
|
|
159
|
-
get: () => ({ data: mockData })
|
|
160
|
-
}));
|
|
161
|
-
const output = yield downloadAndSaveBase({
|
|
162
|
-
projects: testProjects,
|
|
163
|
-
format: "icu"
|
|
164
|
-
});
|
|
165
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
166
|
-
const directoryContents = import_fs.default.readdirSync(import_consts.default.TEXT_DIR);
|
|
167
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
168
|
-
expect(directoryContents.every((f) => f.endsWith(".json"))).toBe(true);
|
|
169
|
-
expect(
|
|
170
|
-
JSON.parse(
|
|
171
|
-
import_fs.default.readFileSync(
|
|
172
|
-
import_path.default.resolve(import_consts.default.TEXT_DIR, directoryContents[0]),
|
|
173
|
-
"utf8"
|
|
174
|
-
)
|
|
175
|
-
)
|
|
176
|
-
).toEqual(mockData);
|
|
177
|
-
}));
|
|
178
|
-
it("writes the android format to disk", () => __async(exports, null, function* () {
|
|
179
|
-
const mockData = `
|
|
106
|
+
const mockDataFlat = { hello: "world" };
|
|
107
|
+
const mockDataNested = { hello: { text: "world" } };
|
|
108
|
+
const mockDataStructured = { hello: { text: "world" } };
|
|
109
|
+
const mockDataIcu = { hello: "world" };
|
|
110
|
+
const mockDataAndroid = `
|
|
180
111
|
<?xml version="1.0" encoding="utf-8"?>
|
|
181
112
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
|
182
113
|
<string name="hello-world" ditto_api_id="hello-world">Hello World</string>
|
|
183
114
|
</resources>
|
|
184
115
|
`;
|
|
185
|
-
|
|
186
|
-
get: () => ({ data: mockData })
|
|
187
|
-
}));
|
|
188
|
-
const output = yield downloadAndSaveBase({
|
|
189
|
-
projects: testProjects,
|
|
190
|
-
format: "android"
|
|
191
|
-
});
|
|
192
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
193
|
-
const directoryContents = import_fs.default.readdirSync(import_consts.default.TEXT_DIR);
|
|
194
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
195
|
-
expect(directoryContents.every((f) => f.endsWith(".xml"))).toBe(true);
|
|
196
|
-
expect(
|
|
197
|
-
import_fs.default.readFileSync(
|
|
198
|
-
import_path.default.resolve(import_consts.default.TEXT_DIR, directoryContents[0]),
|
|
199
|
-
"utf8"
|
|
200
|
-
).replace(/\s/g, "")
|
|
201
|
-
).toEqual(mockData.replace(/\s/g, ""));
|
|
202
|
-
}));
|
|
203
|
-
it("writes the ios-strings format to disk", () => __async(exports, null, function* () {
|
|
204
|
-
const mockData = `
|
|
116
|
+
const mockDataIosStrings = `
|
|
205
117
|
"hello" = "world";
|
|
206
118
|
`;
|
|
207
|
-
|
|
208
|
-
get: () => ({ data: mockData })
|
|
209
|
-
}));
|
|
210
|
-
const output = yield downloadAndSaveBase({
|
|
211
|
-
projects: testProjects,
|
|
212
|
-
format: "ios-strings"
|
|
213
|
-
});
|
|
214
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
215
|
-
const directoryContents = import_fs.default.readdirSync(import_consts.default.TEXT_DIR);
|
|
216
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
217
|
-
expect(directoryContents.every((f) => f.endsWith(".strings"))).toBe(true);
|
|
218
|
-
expect(
|
|
219
|
-
import_fs.default.readFileSync(
|
|
220
|
-
import_path.default.resolve(import_consts.default.TEXT_DIR, directoryContents[0]),
|
|
221
|
-
"utf8"
|
|
222
|
-
).replace(/\s/g, "")
|
|
223
|
-
).toEqual(mockData.replace(/\s/g, ""));
|
|
224
|
-
}));
|
|
225
|
-
it("writes the ios-stringsdict format to disk", () => __async(exports, null, function* () {
|
|
226
|
-
const mockData = `
|
|
119
|
+
const mockDataIosStringsDict = `
|
|
227
120
|
<?xml version="1.0" encoding="utf-8"?>
|
|
228
121
|
<plist version="1.0">
|
|
229
122
|
<dict>
|
|
@@ -244,26 +137,61 @@ describe("downloadAndSaveBase", () => {
|
|
|
244
137
|
</dict>
|
|
245
138
|
</plist>
|
|
246
139
|
`;
|
|
140
|
+
const formats = [
|
|
141
|
+
{ format: "flat", data: mockDataFlat, ext: ".json" },
|
|
142
|
+
{ format: "nested", data: mockDataNested, ext: ".json" },
|
|
143
|
+
{ format: "structured", data: mockDataStructured, ext: ".json" },
|
|
144
|
+
{ format: "icu", data: mockDataIcu, ext: ".json" },
|
|
145
|
+
{ format: "android", data: mockDataAndroid, ext: ".xml" },
|
|
146
|
+
{ format: "ios-strings", data: mockDataIosStrings, ext: ".strings" },
|
|
147
|
+
{
|
|
148
|
+
format: "ios-stringsdict",
|
|
149
|
+
data: mockDataIosStringsDict,
|
|
150
|
+
ext: ".stringsdict"
|
|
151
|
+
}
|
|
152
|
+
];
|
|
153
|
+
const mockApiCall = (data) => {
|
|
247
154
|
import_api.createApiClient.mockImplementation(() => ({
|
|
248
|
-
get: () => ({ data
|
|
155
|
+
get: () => ({ data })
|
|
249
156
|
}));
|
|
157
|
+
};
|
|
158
|
+
const verifySavedData = (format, data, ext) => __async(exports, null, function* () {
|
|
250
159
|
const output = yield downloadAndSaveBase({
|
|
251
160
|
projects: testProjects,
|
|
252
|
-
format
|
|
161
|
+
format
|
|
253
162
|
});
|
|
254
163
|
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
255
164
|
const directoryContents = import_fs.default.readdirSync(import_consts.default.TEXT_DIR);
|
|
256
165
|
expect(directoryContents.length).toEqual(testProjects.length);
|
|
257
|
-
expect(directoryContents.every((f) => f.endsWith(
|
|
258
|
-
|
|
166
|
+
expect(directoryContents.every((f) => f.endsWith(ext))).toBe(true);
|
|
167
|
+
const fileDataString = import_fs.default.readFileSync(
|
|
168
|
+
import_path.default.resolve(import_consts.default.TEXT_DIR, directoryContents[0]),
|
|
169
|
+
"utf8"
|
|
259
170
|
);
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
171
|
+
switch (format) {
|
|
172
|
+
case "android":
|
|
173
|
+
case "ios-strings":
|
|
174
|
+
case "ios-stringsdict":
|
|
175
|
+
expect(typeof data).toBe("string");
|
|
176
|
+
expect(fileDataString.replace(/\s/g, "")).toEqual(
|
|
177
|
+
data.replace(/\s/g, "")
|
|
178
|
+
);
|
|
179
|
+
break;
|
|
180
|
+
case "flat":
|
|
181
|
+
case "nested":
|
|
182
|
+
case "structured":
|
|
183
|
+
case "icu":
|
|
184
|
+
default:
|
|
185
|
+
expect(JSON.parse(fileDataString)).toEqual(data);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
formats.forEach(({ format, data, ext }) => {
|
|
190
|
+
it(`writes the ${format} format to disk`, () => __async(exports, null, function* () {
|
|
191
|
+
mockApiCall(data);
|
|
192
|
+
yield verifySavedData(format, data, ext);
|
|
193
|
+
}));
|
|
194
|
+
});
|
|
267
195
|
});
|
|
268
196
|
describe("getFormatDataIsValid", () => {
|
|
269
197
|
it("handles flat format appropriately", () => {
|
|
@@ -409,4 +337,4 @@ describe("getFormatDataIsValid", () => {
|
|
|
409
337
|
});
|
|
410
338
|
//# sourceMappingURL=pull.test.js.map
|
|
411
339
|
|
|
412
|
-
//# debugId=
|
|
340
|
+
//# debugId=3d53a7f0-4cb6-57db-948f-db309cf2611a
|
package/bin/pull.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/pull.test.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\njest.mock(\"./api\", () => ({\n createApiClient: jest.fn(), // this needs to be mocked in each test that requires it\n}));\nimport { createApiClient } from \"./api\";\n\nconst testProjects: Project[] = [\n {\n id: \"1\",\n name: \"Project 1\",\n fileName: \"Project 1\",\n },\n { id: \"2\", name: \"Project 2\", fileName: \"Project 2\" },\n];\n\n// TODO: all tests in this file currently failing because we're re-instantiating the api client\n// everywhere and are unable to mock the return type separately for each instance of usage.\n// We need to refactor to share one api client everywhere instead of always re-creating it.\nconst mockApi = createApiClient() as any as jest.Mocked<\n ReturnType<typeof createApiClient>\n>;\n\njest.mock(\"./consts\", () => ({\n TEXT_DIR: \".testing\",\n API_HOST: \"https://api.dittowords.com\",\n CONFIG_FILE: \".testing/ditto\",\n PROJECT_CONFIG_FILE: \".testing/config.yml\",\n TEXT_FILE: \".testing/text.json\",\n}));\n\nimport consts from \"./consts\";\nimport allPull, { getFormatDataIsValid } from \"./pull\";\nimport { Project } from \"./types\";\n\nconst {\n _testing: { cleanOutputFiles, downloadAndSaveVariant, downloadAndSaveBase },\n} = allPull;\nconst variant = \"english\";\n\nconst cleanOutputDir = () => {\n if (fs.existsSync(consts.TEXT_DIR))\n fs.rmSync(consts.TEXT_DIR, { recursive: true, force: true });\n\n fs.mkdirSync(consts.TEXT_DIR);\n};\n\nafterAll(() => {\n fs.rmSync(consts.TEXT_DIR, { force: true, recursive: true });\n});\n\ndescribe(\"cleanOutputFiles\", () => {\n it(\"removes .js, .json, .xml, .strings, .stringsdict files\", () => {\n cleanOutputDir();\n\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.json\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.js\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.xml\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.strings\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.stringsdict\"), \"test\");\n // this file shouldn't be deleted\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.txt\"), \"test\");\n\n expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(6);\n\n cleanOutputFiles();\n\n expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(1);\n });\n});\n\ndescribe(\"downloadAndSaveBase\", () => {\n beforeAll(() => {\n if (!fs.existsSync(consts.TEXT_DIR)) {\n fs.mkdirSync(consts.TEXT_DIR);\n }\n });\n\n beforeEach(() => {\n cleanOutputDir();\n });\n\n it(\"writes the flat format to disk\", async () => {\n const mockData = {\n hello: \"world\",\n };\n (createApiClient as any).mockImplementation(() => ({\n get: () => ({ data: mockData }),\n }));\n const output = await downloadAndSaveBase({\n projects: testProjects,\n format: \"flat\",\n } as any);\n expect(/successfully saved/i.test(output)).toEqual(true);\n const directoryContents = fs.readdirSync(consts.TEXT_DIR);\n expect(directoryContents.length).toEqual(testProjects.length);\n expect(directoryContents.every((f) => f.endsWith(\".json\"))).toBe(true);\n expect(\n JSON.parse(\n fs.readFileSync(\n path.resolve(consts.TEXT_DIR, directoryContents[0]),\n \"utf8\"\n )\n )\n ).toEqual(mockData);\n });\n\n it(\"writes the structured format to disk\", async () => {\n const mockData = {\n hello: { text: \"world\" },\n };\n (createApiClient as any).mockImplementation(() => ({\n get: () => ({ data: mockData }),\n }));\n const output = await downloadAndSaveBase({\n projects: testProjects,\n format: \"structured\",\n } as any);\n expect(/successfully saved/i.test(output)).toEqual(true);\n const directoryContents = fs.readdirSync(consts.TEXT_DIR);\n expect(directoryContents.length).toEqual(testProjects.length);\n expect(directoryContents.every((f) => f.endsWith(\".json\"))).toBe(true);\n expect(\n JSON.parse(\n fs.readFileSync(\n path.resolve(consts.TEXT_DIR, directoryContents[0]),\n \"utf8\"\n )\n )\n ).toEqual(mockData);\n });\n\n it(\"writes the icu format to disk\", async () => {\n const mockData = {\n hello: \"world\",\n };\n (createApiClient as any).mockImplementation(() => ({\n get: () => ({ data: mockData }),\n }));\n const output = await downloadAndSaveBase({\n projects: testProjects,\n format: \"icu\",\n } as any);\n expect(/successfully saved/i.test(output)).toEqual(true);\n const directoryContents = fs.readdirSync(consts.TEXT_DIR);\n expect(directoryContents.length).toEqual(testProjects.length);\n expect(directoryContents.every((f) => f.endsWith(\".json\"))).toBe(true);\n expect(\n JSON.parse(\n fs.readFileSync(\n path.resolve(consts.TEXT_DIR, directoryContents[0]),\n \"utf8\"\n )\n )\n ).toEqual(mockData);\n });\n\n it(\"writes the android format to disk\", async () => {\n const mockData = `\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n <string name=\"hello-world\" ditto_api_id=\"hello-world\">Hello World</string>\n </resources>\n `;\n (createApiClient as any).mockImplementation(() => ({\n get: () => ({ data: mockData }),\n }));\n const output = await downloadAndSaveBase({\n projects: testProjects,\n format: \"android\",\n } as any);\n expect(/successfully saved/i.test(output)).toEqual(true);\n const directoryContents = fs.readdirSync(consts.TEXT_DIR);\n expect(directoryContents.length).toEqual(testProjects.length);\n expect(directoryContents.every((f) => f.endsWith(\".xml\"))).toBe(true);\n expect(\n fs\n .readFileSync(\n path.resolve(consts.TEXT_DIR, directoryContents[0]),\n \"utf8\"\n )\n .replace(/\\s/g, \"\")\n ).toEqual(mockData.replace(/\\s/g, \"\"));\n });\n\n it(\"writes the ios-strings format to disk\", async () => {\n const mockData = `\n \"hello\" = \"world\"; \n `;\n (createApiClient as any).mockImplementation(() => ({\n get: () => ({ data: mockData }),\n }));\n const output = await downloadAndSaveBase({\n projects: testProjects,\n format: \"ios-strings\",\n } as any);\n expect(/successfully saved/i.test(output)).toEqual(true);\n const directoryContents = fs.readdirSync(consts.TEXT_DIR);\n expect(directoryContents.length).toEqual(testProjects.length);\n expect(directoryContents.every((f) => f.endsWith(\".strings\"))).toBe(true);\n expect(\n fs\n .readFileSync(\n path.resolve(consts.TEXT_DIR, directoryContents[0]),\n \"utf8\"\n )\n .replace(/\\s/g, \"\")\n ).toEqual(mockData.replace(/\\s/g, \"\"));\n });\n\n it(\"writes the ios-stringsdict format to disk\", async () => {\n const mockData = `\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <plist version=\"1.0\">\n <dict>\n <key>hello-world</key>\n <dict>\n <key>NSStringLocalizedFormatKey</key>\n <string>%1$#@count@</string>\n <key>count</key>\n <dict>\n <key>NSStringFormatSpecTypeKey</key>\n <string>NSStringPluralRuleType</string>\n <key>NSStringFormatValueTypeKey</key>\n <string>d</string>\n <key>other</key>\n <string>espanol</string>\n </dict>\n </dict>\n </dict>\n </plist>\n `;\n (createApiClient as any).mockImplementation(() => ({\n get: () => ({ data: mockData }),\n }));\n const output = await downloadAndSaveBase({\n projects: testProjects,\n format: \"ios-stringsdict\",\n } as any);\n expect(/successfully saved/i.test(output)).toEqual(true);\n const directoryContents = fs.readdirSync(consts.TEXT_DIR);\n expect(directoryContents.length).toEqual(testProjects.length);\n expect(directoryContents.every((f) => f.endsWith(\".stringsdict\"))).toBe(\n true\n );\n expect(\n fs\n .readFileSync(\n path.resolve(consts.TEXT_DIR, directoryContents[0]),\n \"utf8\"\n )\n .replace(/\\s/g, \"\")\n ).toEqual(mockData.replace(/\\s/g, \"\"));\n });\n});\n\ndescribe(\"getFormatDataIsValid\", () => {\n it(\"handles flat format appropriately\", () => {\n expect(getFormatDataIsValid.flat(\"{}\")).toBe(false);\n expect(getFormatDataIsValid.flat(`{ \"hello\": \"world\" }`)).toBe(true);\n expect(\n getFormatDataIsValid.flat(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\"\n }`)\n ).toBe(false);\n expect(\n getFormatDataIsValid.flat(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\",\n \"hello\": \"world\"\n }`)\n ).toBe(true);\n });\n it(\"handles structured format appropriately\", () => {\n expect(getFormatDataIsValid.structured(\"{}\")).toBe(false);\n expect(\n getFormatDataIsValid.structured(`{ \"hello\": { \"text\": \"world\" } }`)\n ).toBe(true);\n expect(\n getFormatDataIsValid.structured(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\"\n }`)\n ).toBe(false);\n expect(\n getFormatDataIsValid.structured(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\",\n \"hello\": { \"text\": \"world\" }\n }`)\n ).toBe(true);\n });\n it(\"handles icu format appropriately\", () => {\n expect(getFormatDataIsValid.icu(\"{}\")).toBe(false);\n expect(getFormatDataIsValid.icu(`{ \"hello\": \"world\" }`)).toBe(true);\n expect(\n getFormatDataIsValid.icu(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\"\n }`)\n ).toBe(false);\n expect(\n getFormatDataIsValid.icu(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\",\n \"hello\": \"world\"\n }`)\n ).toBe(true);\n });\n it(\"handles android format appropriately\", () => {\n expect(\n getFormatDataIsValid.android(`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"/>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid.android(`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"/>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid.android(`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n <string name=\"hello-world\" ditto_api_id=\"hello-world\">Hello World</string>\n </resources>\n `)\n ).toBe(true);\n });\n it(\"handles ios-strings format appropriately\", () => {\n expect(getFormatDataIsValid[\"ios-strings\"](\"\")).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-strings\"](`\n /* Variant Name: English */\n /* Variant Description: */\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-strings\"](`\n /* Variant Name: English */\n /* Variant Description: */\n \"Hello\" = \"World\";\n `)\n ).toBe(true);\n });\n it(\"handles ios-stringsdict format appropriately\", () => {\n expect(\n getFormatDataIsValid[\"ios-stringsdict\"](`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <plist version=\"1.0\">\n <dict/>\n </plist>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-stringsdict\"](`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <plist version=\"1.0\">\n <dict/>\n </plist>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-stringsdict\"](`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <plist version=\"1.0\">\n <dict>\n <key>Hello World</key>\n <dict>\n <key>NSStringLocalizedFormatKey</key>\n <string>%1$#@count@</string>\n <key>count</key>\n <dict>\n <key>NSStringFormatSpecTypeKey</key>\n <string>NSStringPluralRuleType</string>\n <key>NSStringFormatValueTypeKey</key>\n <string>d</string>\n <key>other</key>\n <string>espanol</string>\n </dict>\n </dict>\n </dict>\n </plist>\n `)\n ).toBe(true);\n });\n});\n"],"names":["allPull","fs","consts","path"],"mappings":"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","debug_id":"587c3043-a9e4-58d4-9ce1-1464ba0c94e3"}
|
|
1
|
+
{"version":3,"sources":["../lib/pull.test.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\njest.mock(\"./api\", () => ({\n createApiClient: jest.fn(), // this needs to be mocked in each test that requires it\n}));\nimport { createApiClient } from \"./api\";\n\nconst testProjects: Project[] = [\n {\n id: \"1\",\n name: \"Project 1\",\n fileName: \"Project 1\",\n },\n { id: \"2\", name: \"Project 2\", fileName: \"Project 2\" },\n];\n\n// TODO: all tests in this file currently failing because we're re-instantiating the api client\n// everywhere and are unable to mock the return type separately for each instance of usage.\n// We need to refactor to share one api client everywhere instead of always re-creating it.\nconst mockApi = createApiClient() as any as jest.Mocked<\n ReturnType<typeof createApiClient>\n>;\n\njest.mock(\"./consts\", () => ({\n TEXT_DIR: \".testing\",\n API_HOST: \"https://api.dittowords.com\",\n CONFIG_FILE: \".testing/ditto\",\n PROJECT_CONFIG_FILE: \".testing/config.yml\",\n TEXT_FILE: \".testing/text.json\",\n}));\n\nimport consts from \"./consts\";\nimport allPull, { getFormatDataIsValid } from \"./pull\";\nimport { Project, SupportedExtension, SupportedFormat } from \"./types\";\n\nconst {\n _testing: { cleanOutputFiles, downloadAndSaveVariant, downloadAndSaveBase },\n} = allPull;\nconst variant = \"english\";\n\nconst cleanOutputDir = () => {\n if (fs.existsSync(consts.TEXT_DIR))\n fs.rmSync(consts.TEXT_DIR, { recursive: true, force: true });\n\n fs.mkdirSync(consts.TEXT_DIR);\n};\n\nafterAll(() => {\n fs.rmSync(consts.TEXT_DIR, { force: true, recursive: true });\n});\n\ndescribe(\"cleanOutputFiles\", () => {\n it(\"removes .js, .json, .xml, .strings, .stringsdict files\", () => {\n cleanOutputDir();\n\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.json\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.js\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.xml\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.strings\"), \"test\");\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.stringsdict\"), \"test\");\n // this file shouldn't be deleted\n fs.writeFileSync(path.resolve(consts.TEXT_DIR, \"test.txt\"), \"test\");\n\n expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(6);\n\n cleanOutputFiles();\n\n expect(fs.readdirSync(consts.TEXT_DIR).length).toEqual(1);\n });\n});\n\ndescribe(\"downloadAndSaveBase\", () => {\n beforeAll(() => {\n if (!fs.existsSync(consts.TEXT_DIR)) {\n fs.mkdirSync(consts.TEXT_DIR);\n }\n });\n\n beforeEach(() => {\n cleanOutputDir();\n });\n\n const mockDataFlat = { hello: \"world\" };\n const mockDataNested = { hello: { text: \"world\" } };\n const mockDataStructured = { hello: { text: \"world\" } };\n const mockDataIcu = { hello: \"world\" };\n const mockDataAndroid = `\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n <string name=\"hello-world\" ditto_api_id=\"hello-world\">Hello World</string>\n </resources>\n `;\n const mockDataIosStrings = `\n \"hello\" = \"world\"; \n `;\n const mockDataIosStringsDict = `\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <plist version=\"1.0\">\n <dict>\n <key>hello-world</key>\n <dict>\n <key>NSStringLocalizedFormatKey</key>\n <string>%1$#@count@</string>\n <key>count</key>\n <dict>\n <key>NSStringFormatSpecTypeKey</key>\n <string>NSStringPluralRuleType</string>\n <key>NSStringFormatValueTypeKey</key>\n <string>d</string>\n <key>other</key>\n <string>espanol</string>\n </dict>\n </dict>\n </dict>\n </plist>\n `;\n\n const formats: Array<{\n format: SupportedFormat;\n data: Object;\n ext: SupportedExtension;\n }> = [\n { format: \"flat\", data: mockDataFlat, ext: \".json\" },\n { format: \"nested\", data: mockDataNested, ext: \".json\" },\n { format: \"structured\", data: mockDataStructured, ext: \".json\" },\n { format: \"icu\", data: mockDataIcu, ext: \".json\" },\n { format: \"android\", data: mockDataAndroid, ext: \".xml\" },\n { format: \"ios-strings\", data: mockDataIosStrings, ext: \".strings\" },\n {\n format: \"ios-stringsdict\",\n data: mockDataIosStringsDict,\n ext: \".stringsdict\",\n },\n ];\n\n const mockApiCall = (data: unknown) => {\n (createApiClient as any).mockImplementation(() => ({\n get: () => ({ data }),\n }));\n };\n\n const verifySavedData = async (\n format: SupportedFormat,\n data: unknown,\n ext: SupportedExtension\n ) => {\n const output = await downloadAndSaveBase({\n projects: testProjects,\n format: format,\n } as any);\n expect(/successfully saved/i.test(output)).toEqual(true);\n const directoryContents = fs.readdirSync(consts.TEXT_DIR);\n expect(directoryContents.length).toEqual(testProjects.length);\n expect(directoryContents.every((f) => f.endsWith(ext))).toBe(true);\n const fileDataString = fs.readFileSync(\n path.resolve(consts.TEXT_DIR, directoryContents[0]),\n \"utf8\"\n );\n\n switch (format) {\n case \"android\":\n case \"ios-strings\":\n case \"ios-stringsdict\":\n expect(typeof data).toBe(\"string\");\n expect(fileDataString.replace(/\\s/g, \"\")).toEqual(\n (data as string).replace(/\\s/g, \"\")\n );\n break;\n\n case \"flat\":\n case \"nested\":\n case \"structured\":\n case \"icu\":\n default:\n expect(JSON.parse(fileDataString)).toEqual(data);\n break;\n }\n };\n\n formats.forEach(({ format, data, ext }) => {\n it(`writes the ${format} format to disk`, async () => {\n mockApiCall(data);\n await verifySavedData(format, data, ext);\n });\n });\n});\n\ndescribe(\"getFormatDataIsValid\", () => {\n it(\"handles flat format appropriately\", () => {\n expect(getFormatDataIsValid.flat(\"{}\")).toBe(false);\n expect(getFormatDataIsValid.flat(`{ \"hello\": \"world\" }`)).toBe(true);\n expect(\n getFormatDataIsValid.flat(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\"\n }`)\n ).toBe(false);\n expect(\n getFormatDataIsValid.flat(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\",\n \"hello\": \"world\"\n }`)\n ).toBe(true);\n });\n it(\"handles structured format appropriately\", () => {\n expect(getFormatDataIsValid.structured(\"{}\")).toBe(false);\n expect(\n getFormatDataIsValid.structured(`{ \"hello\": { \"text\": \"world\" } }`)\n ).toBe(true);\n expect(\n getFormatDataIsValid.structured(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\"\n }`)\n ).toBe(false);\n expect(\n getFormatDataIsValid.structured(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\",\n \"hello\": { \"text\": \"world\" }\n }`)\n ).toBe(true);\n });\n it(\"handles icu format appropriately\", () => {\n expect(getFormatDataIsValid.icu(\"{}\")).toBe(false);\n expect(getFormatDataIsValid.icu(`{ \"hello\": \"world\" }`)).toBe(true);\n expect(\n getFormatDataIsValid.icu(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\"\n }`)\n ).toBe(false);\n expect(\n getFormatDataIsValid.icu(`{\n \"__variant-name\": \"English\",\n \"__variant-description\": \"\",\n \"hello\": \"world\"\n }`)\n ).toBe(true);\n });\n it(\"handles android format appropriately\", () => {\n expect(\n getFormatDataIsValid.android(`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"/>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid.android(`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"/>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid.android(`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n <string name=\"hello-world\" ditto_api_id=\"hello-world\">Hello World</string>\n </resources>\n `)\n ).toBe(true);\n });\n it(\"handles ios-strings format appropriately\", () => {\n expect(getFormatDataIsValid[\"ios-strings\"](\"\")).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-strings\"](`\n /* Variant Name: English */\n /* Variant Description: */\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-strings\"](`\n /* Variant Name: English */\n /* Variant Description: */\n \"Hello\" = \"World\";\n `)\n ).toBe(true);\n });\n it(\"handles ios-stringsdict format appropriately\", () => {\n expect(\n getFormatDataIsValid[\"ios-stringsdict\"](`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <plist version=\"1.0\">\n <dict/>\n </plist>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-stringsdict\"](`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <plist version=\"1.0\">\n <dict/>\n </plist>\n `)\n ).toBe(false);\n expect(\n getFormatDataIsValid[\"ios-stringsdict\"](`\n <?xml version=\"1.0\" encoding=\"utf-8\"?>\n <!--Variant Name: English-->\n <!--Variant Description: -->\n <plist version=\"1.0\">\n <dict>\n <key>Hello World</key>\n <dict>\n <key>NSStringLocalizedFormatKey</key>\n <string>%1$#@count@</string>\n <key>count</key>\n <dict>\n <key>NSStringFormatSpecTypeKey</key>\n <string>NSStringPluralRuleType</string>\n <key>NSStringFormatValueTypeKey</key>\n <string>d</string>\n <key>other</key>\n <string>espanol</string>\n </dict>\n </dict>\n </dict>\n </plist>\n `)\n ).toBe(true);\n });\n});\n"],"names":["allPull","fs","consts","path"],"mappings":"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","debug_id":"3d53a7f0-4cb6-57db-948f-db309cf2611a"}
|
package/bin/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="adad285b-cb83-5dca-a453-8ef2b50a6237")}catch(e){}}();
|
|
3
3
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -18,4 +18,4 @@ var types_exports = {};
|
|
|
18
18
|
module.exports = __toCommonJS(types_exports);
|
|
19
19
|
//# sourceMappingURL=types.js.map
|
|
20
20
|
|
|
21
|
-
//# debugId=
|
|
21
|
+
//# debugId=adad285b-cb83-5dca-a453-8ef2b50a6237
|
package/bin/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/types.ts"],"sourcesContent":["export interface Project {\n name: string;\n id: string;\n url?: string;\n fileName?: string;\n status?: string;\n exclude_components?: boolean;\n}\n\nexport type ComponentSource = ComponentFolder & {\n type: \"components\";\n fileName: string;\n variant: string;\n};\n\nexport type Source = (Project & { type?: undefined }) | ComponentSource;\n\nexport interface ComponentFolder {\n id: string;\n name: string;\n status?: string;\n}\n\nexport type SupportedFormat =\n | \"flat\"\n | \"structured\"\n | \"android\"\n | \"ios-strings\"\n | \"ios-stringsdict\"\n | \"icu\";\n\ntype ComponentsSourceBool = boolean;\ntype ComponentsSourceConfig = {\n root?: boolean | { status: string };\n folders?: ComponentFolder[];\n};\ntype ComponentsSource = ComponentsSourceBool | ComponentsSourceConfig;\n\nexport interface ConfigYAML {\n sources?: {\n components?: ComponentsSource;\n projects?: Project[];\n };\n format?: SupportedFormat;\n status?: string;\n variants?: boolean;\n richText?: boolean;\n\n // TODO: might want to rename this at some point\n iosLocales?: Record<string, string>[];\n\n // these are legacy fields - if they exist, we should output\n // a deprecation error, and suggest that they nest them under\n // a top-level `sources` property\n components?: boolean;\n projects?: Project[];\n}\n\nexport interface SourceInformation {\n hasSourceData: boolean;\n hasTopLevelProjectsField: boolean;\n hasTopLevelComponentsField: boolean;\n hasComponentLibraryInProjects: boolean;\n validProjects: Project[];\n shouldFetchComponentLibrary: boolean;\n variants: boolean;\n format: string | string[] | undefined;\n status: string | undefined;\n richText: boolean | undefined;\n componentRoot: boolean | { status: string } | undefined;\n componentFolders: ComponentFolder[] | undefined;\n localeByVariantApiId: Record<string, string> | undefined;\n}\n\nexport type Token = string | undefined;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA","debug_id":"
|
|
1
|
+
{"version":3,"sources":["../lib/types.ts"],"sourcesContent":["export interface Project {\n name: string;\n id: string;\n url?: string;\n fileName?: string;\n status?: string;\n exclude_components?: boolean;\n}\n\nexport type ComponentSource = ComponentFolder & {\n type: \"components\";\n fileName: string;\n variant: string;\n};\n\nexport type Source = (Project & { type?: undefined }) | ComponentSource;\n\nexport interface ComponentFolder {\n id: string;\n name: string;\n status?: string;\n}\n\nexport type SupportedFormat =\n | \"flat\"\n | \"nested\"\n | \"structured\"\n | \"android\"\n | \"ios-strings\"\n | \"ios-stringsdict\"\n | \"icu\";\n\nexport type SupportedExtension = \".json\" | \".xml\" | \".strings\" | \".stringsdict\";\n\ntype ComponentsSourceBool = boolean;\ntype ComponentsSourceConfig = {\n root?: boolean | { status: string };\n folders?: ComponentFolder[];\n};\ntype ComponentsSource = ComponentsSourceBool | ComponentsSourceConfig;\n\nexport interface ConfigYAML {\n sources?: {\n components?: ComponentsSource;\n projects?: Project[];\n };\n format?: SupportedFormat;\n status?: string;\n variants?: boolean;\n richText?: boolean;\n\n // TODO: might want to rename this at some point\n iosLocales?: Record<string, string>[];\n\n // these are legacy fields - if they exist, we should output\n // a deprecation error, and suggest that they nest them under\n // a top-level `sources` property\n components?: boolean;\n projects?: Project[];\n}\n\nexport interface SourceInformation {\n hasSourceData: boolean;\n hasTopLevelProjectsField: boolean;\n hasTopLevelComponentsField: boolean;\n hasComponentLibraryInProjects: boolean;\n validProjects: Project[];\n shouldFetchComponentLibrary: boolean;\n variants: boolean;\n format: string | string[] | undefined;\n status: string | undefined;\n richText: boolean | undefined;\n componentRoot: boolean | { status: string } | undefined;\n componentFolders: ComponentFolder[] | undefined;\n localeByVariantApiId: Record<string, string> | undefined;\n}\n\nexport type Token = string | undefined;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA","debug_id":"adad285b-cb83-5dca-a453-8ef2b50a6237"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2c3d33c0-3502-5a27-a98a-460ccebb5e74")}catch(e){}}();
|
|
3
3
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -24,7 +24,12 @@ __export(cleanFileName_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(cleanFileName_exports);
|
|
26
26
|
function cleanFileName(fileName) {
|
|
27
|
-
|
|
27
|
+
let parts = fileName.split(/\s{1,}/g);
|
|
28
|
+
parts = parts.map(
|
|
29
|
+
(part) => part.replace(/[^a-zA-Z0-9-_.]/g, "").toLowerCase()
|
|
30
|
+
);
|
|
31
|
+
parts = parts.filter((part) => part !== "");
|
|
32
|
+
return parts.join("-");
|
|
28
33
|
}
|
|
29
34
|
// Annotate the CommonJS export names for ESM import in node:
|
|
30
35
|
0 && (module.exports = {
|
|
@@ -32,4 +37,4 @@ function cleanFileName(fileName) {
|
|
|
32
37
|
});
|
|
33
38
|
//# sourceMappingURL=cleanFileName.js.map
|
|
34
39
|
|
|
35
|
-
//# debugId=
|
|
40
|
+
//# debugId=2c3d33c0-3502-5a27-a98a-460ccebb5e74
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../lib/utils/cleanFileName.ts"],"sourcesContent":["export function cleanFileName(fileName: string): string {\n
|
|
1
|
+
{"version":3,"sources":["../../lib/utils/cleanFileName.ts"],"sourcesContent":["export function cleanFileName(fileName: string): string {\n let parts = fileName.split(/\\s{1,}/g);\n parts = parts.map((part) =>\n part.replace(/[^a-zA-Z0-9-_.]/g, \"\").toLowerCase()\n );\n parts = parts.filter((part) => part !== \"\");\n return parts.join(\"-\");\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,SAAS,cAAc,UAA0B;AACtD,MAAI,QAAQ,SAAS,MAAM,SAAS;AACpC,UAAQ,MAAM;AAAA,IAAI,CAAC,SACjB,KAAK,QAAQ,oBAAoB,EAAE,EAAE,YAAY;AAAA,EACnD;AACA,UAAQ,MAAM,OAAO,CAAC,SAAS,SAAS,EAAE;AAC1C,SAAO,MAAM,KAAK,GAAG;AACvB","debug_id":"2c3d33c0-3502-5a27-a98a-460ccebb5e74"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6d536038-ec46-5c46-a8f3-18d15a529002")}catch(e){}}();
|
|
3
|
+
|
|
4
|
+
var import_cleanFileName = require("./cleanFileName");
|
|
5
|
+
describe("cleanFileName tests", () => {
|
|
6
|
+
it("correctly cleans emojis", () => {
|
|
7
|
+
const folderName = "\u{1F44D}Good Folder";
|
|
8
|
+
expect((0, import_cleanFileName.cleanFileName)(folderName)).toEqual("good-folder");
|
|
9
|
+
const fileName = "\u{1F44D} Good Folder";
|
|
10
|
+
expect((0, import_cleanFileName.cleanFileName)(fileName)).toEqual("good-folder");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=cleanFileName.test.js.map
|
|
14
|
+
|
|
15
|
+
//# debugId=6d536038-ec46-5c46-a8f3-18d15a529002
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../lib/utils/cleanFileName.test.ts"],"sourcesContent":["import { cleanFileName } from \"./cleanFileName\";\n\ndescribe(\"cleanFileName tests\", () => {\n it(\"correctly cleans emojis\", () => {\n const folderName = \"👍Good Folder\";\n expect(cleanFileName(folderName)).toEqual(\"good-folder\");\n\n const fileName = \"👍 Good Folder\";\n expect(cleanFileName(fileName)).toEqual(\"good-folder\");\n });\n});\n"],"names":[],"mappings":";;;AAAA,2BAA8B;AAE9B,SAAS,uBAAuB,MAAM;AACpC,KAAG,2BAA2B,MAAM;AAClC,UAAM,aAAa;AACnB,eAAO,oCAAc,UAAU,CAAC,EAAE,QAAQ,aAAa;AAEvD,UAAM,WAAW;AACjB,eAAO,oCAAc,QAAQ,CAAC,EAAE,QAAQ,aAAa;AAAA,EACvD,CAAC;AACH,CAAC","debug_id":"6d536038-ec46-5c46-a8f3-18d15a529002"}
|
package/lib/pull.test.ts
CHANGED
|
@@ -32,7 +32,7 @@ jest.mock("./consts", () => ({
|
|
|
32
32
|
|
|
33
33
|
import consts from "./consts";
|
|
34
34
|
import allPull, { getFormatDataIsValid } from "./pull";
|
|
35
|
-
import { Project } from "./types";
|
|
35
|
+
import { Project, SupportedExtension, SupportedFormat } from "./types";
|
|
36
36
|
|
|
37
37
|
const {
|
|
38
38
|
_testing: { cleanOutputFiles, downloadAndSaveVariant, downloadAndSaveBase },
|
|
@@ -81,136 +81,20 @@ describe("downloadAndSaveBase", () => {
|
|
|
81
81
|
cleanOutputDir();
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
get: () => ({ data: mockData }),
|
|
90
|
-
}));
|
|
91
|
-
const output = await downloadAndSaveBase({
|
|
92
|
-
projects: testProjects,
|
|
93
|
-
format: "flat",
|
|
94
|
-
} as any);
|
|
95
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
96
|
-
const directoryContents = fs.readdirSync(consts.TEXT_DIR);
|
|
97
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
98
|
-
expect(directoryContents.every((f) => f.endsWith(".json"))).toBe(true);
|
|
99
|
-
expect(
|
|
100
|
-
JSON.parse(
|
|
101
|
-
fs.readFileSync(
|
|
102
|
-
path.resolve(consts.TEXT_DIR, directoryContents[0]),
|
|
103
|
-
"utf8"
|
|
104
|
-
)
|
|
105
|
-
)
|
|
106
|
-
).toEqual(mockData);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("writes the structured format to disk", async () => {
|
|
110
|
-
const mockData = {
|
|
111
|
-
hello: { text: "world" },
|
|
112
|
-
};
|
|
113
|
-
(createApiClient as any).mockImplementation(() => ({
|
|
114
|
-
get: () => ({ data: mockData }),
|
|
115
|
-
}));
|
|
116
|
-
const output = await downloadAndSaveBase({
|
|
117
|
-
projects: testProjects,
|
|
118
|
-
format: "structured",
|
|
119
|
-
} as any);
|
|
120
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
121
|
-
const directoryContents = fs.readdirSync(consts.TEXT_DIR);
|
|
122
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
123
|
-
expect(directoryContents.every((f) => f.endsWith(".json"))).toBe(true);
|
|
124
|
-
expect(
|
|
125
|
-
JSON.parse(
|
|
126
|
-
fs.readFileSync(
|
|
127
|
-
path.resolve(consts.TEXT_DIR, directoryContents[0]),
|
|
128
|
-
"utf8"
|
|
129
|
-
)
|
|
130
|
-
)
|
|
131
|
-
).toEqual(mockData);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("writes the icu format to disk", async () => {
|
|
135
|
-
const mockData = {
|
|
136
|
-
hello: "world",
|
|
137
|
-
};
|
|
138
|
-
(createApiClient as any).mockImplementation(() => ({
|
|
139
|
-
get: () => ({ data: mockData }),
|
|
140
|
-
}));
|
|
141
|
-
const output = await downloadAndSaveBase({
|
|
142
|
-
projects: testProjects,
|
|
143
|
-
format: "icu",
|
|
144
|
-
} as any);
|
|
145
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
146
|
-
const directoryContents = fs.readdirSync(consts.TEXT_DIR);
|
|
147
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
148
|
-
expect(directoryContents.every((f) => f.endsWith(".json"))).toBe(true);
|
|
149
|
-
expect(
|
|
150
|
-
JSON.parse(
|
|
151
|
-
fs.readFileSync(
|
|
152
|
-
path.resolve(consts.TEXT_DIR, directoryContents[0]),
|
|
153
|
-
"utf8"
|
|
154
|
-
)
|
|
155
|
-
)
|
|
156
|
-
).toEqual(mockData);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("writes the android format to disk", async () => {
|
|
160
|
-
const mockData = `
|
|
84
|
+
const mockDataFlat = { hello: "world" };
|
|
85
|
+
const mockDataNested = { hello: { text: "world" } };
|
|
86
|
+
const mockDataStructured = { hello: { text: "world" } };
|
|
87
|
+
const mockDataIcu = { hello: "world" };
|
|
88
|
+
const mockDataAndroid = `
|
|
161
89
|
<?xml version="1.0" encoding="utf-8"?>
|
|
162
90
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
|
163
91
|
<string name="hello-world" ditto_api_id="hello-world">Hello World</string>
|
|
164
92
|
</resources>
|
|
165
93
|
`;
|
|
166
|
-
|
|
167
|
-
get: () => ({ data: mockData }),
|
|
168
|
-
}));
|
|
169
|
-
const output = await downloadAndSaveBase({
|
|
170
|
-
projects: testProjects,
|
|
171
|
-
format: "android",
|
|
172
|
-
} as any);
|
|
173
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
174
|
-
const directoryContents = fs.readdirSync(consts.TEXT_DIR);
|
|
175
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
176
|
-
expect(directoryContents.every((f) => f.endsWith(".xml"))).toBe(true);
|
|
177
|
-
expect(
|
|
178
|
-
fs
|
|
179
|
-
.readFileSync(
|
|
180
|
-
path.resolve(consts.TEXT_DIR, directoryContents[0]),
|
|
181
|
-
"utf8"
|
|
182
|
-
)
|
|
183
|
-
.replace(/\s/g, "")
|
|
184
|
-
).toEqual(mockData.replace(/\s/g, ""));
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("writes the ios-strings format to disk", async () => {
|
|
188
|
-
const mockData = `
|
|
94
|
+
const mockDataIosStrings = `
|
|
189
95
|
"hello" = "world";
|
|
190
96
|
`;
|
|
191
|
-
|
|
192
|
-
get: () => ({ data: mockData }),
|
|
193
|
-
}));
|
|
194
|
-
const output = await downloadAndSaveBase({
|
|
195
|
-
projects: testProjects,
|
|
196
|
-
format: "ios-strings",
|
|
197
|
-
} as any);
|
|
198
|
-
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
199
|
-
const directoryContents = fs.readdirSync(consts.TEXT_DIR);
|
|
200
|
-
expect(directoryContents.length).toEqual(testProjects.length);
|
|
201
|
-
expect(directoryContents.every((f) => f.endsWith(".strings"))).toBe(true);
|
|
202
|
-
expect(
|
|
203
|
-
fs
|
|
204
|
-
.readFileSync(
|
|
205
|
-
path.resolve(consts.TEXT_DIR, directoryContents[0]),
|
|
206
|
-
"utf8"
|
|
207
|
-
)
|
|
208
|
-
.replace(/\s/g, "")
|
|
209
|
-
).toEqual(mockData.replace(/\s/g, ""));
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("writes the ios-stringsdict format to disk", async () => {
|
|
213
|
-
const mockData = `
|
|
97
|
+
const mockDataIosStringsDict = `
|
|
214
98
|
<?xml version="1.0" encoding="utf-8"?>
|
|
215
99
|
<plist version="1.0">
|
|
216
100
|
<dict>
|
|
@@ -231,27 +115,74 @@ describe("downloadAndSaveBase", () => {
|
|
|
231
115
|
</dict>
|
|
232
116
|
</plist>
|
|
233
117
|
`;
|
|
118
|
+
|
|
119
|
+
const formats: Array<{
|
|
120
|
+
format: SupportedFormat;
|
|
121
|
+
data: Object;
|
|
122
|
+
ext: SupportedExtension;
|
|
123
|
+
}> = [
|
|
124
|
+
{ format: "flat", data: mockDataFlat, ext: ".json" },
|
|
125
|
+
{ format: "nested", data: mockDataNested, ext: ".json" },
|
|
126
|
+
{ format: "structured", data: mockDataStructured, ext: ".json" },
|
|
127
|
+
{ format: "icu", data: mockDataIcu, ext: ".json" },
|
|
128
|
+
{ format: "android", data: mockDataAndroid, ext: ".xml" },
|
|
129
|
+
{ format: "ios-strings", data: mockDataIosStrings, ext: ".strings" },
|
|
130
|
+
{
|
|
131
|
+
format: "ios-stringsdict",
|
|
132
|
+
data: mockDataIosStringsDict,
|
|
133
|
+
ext: ".stringsdict",
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
const mockApiCall = (data: unknown) => {
|
|
234
138
|
(createApiClient as any).mockImplementation(() => ({
|
|
235
|
-
get: () => ({ data
|
|
139
|
+
get: () => ({ data }),
|
|
236
140
|
}));
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const verifySavedData = async (
|
|
144
|
+
format: SupportedFormat,
|
|
145
|
+
data: unknown,
|
|
146
|
+
ext: SupportedExtension
|
|
147
|
+
) => {
|
|
237
148
|
const output = await downloadAndSaveBase({
|
|
238
149
|
projects: testProjects,
|
|
239
|
-
format:
|
|
150
|
+
format: format,
|
|
240
151
|
} as any);
|
|
241
152
|
expect(/successfully saved/i.test(output)).toEqual(true);
|
|
242
153
|
const directoryContents = fs.readdirSync(consts.TEXT_DIR);
|
|
243
154
|
expect(directoryContents.length).toEqual(testProjects.length);
|
|
244
|
-
expect(directoryContents.every((f) => f.endsWith(
|
|
245
|
-
|
|
155
|
+
expect(directoryContents.every((f) => f.endsWith(ext))).toBe(true);
|
|
156
|
+
const fileDataString = fs.readFileSync(
|
|
157
|
+
path.resolve(consts.TEXT_DIR, directoryContents[0]),
|
|
158
|
+
"utf8"
|
|
246
159
|
);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
)
|
|
253
|
-
.replace(/\s/g, "")
|
|
254
|
-
|
|
160
|
+
|
|
161
|
+
switch (format) {
|
|
162
|
+
case "android":
|
|
163
|
+
case "ios-strings":
|
|
164
|
+
case "ios-stringsdict":
|
|
165
|
+
expect(typeof data).toBe("string");
|
|
166
|
+
expect(fileDataString.replace(/\s/g, "")).toEqual(
|
|
167
|
+
(data as string).replace(/\s/g, "")
|
|
168
|
+
);
|
|
169
|
+
break;
|
|
170
|
+
|
|
171
|
+
case "flat":
|
|
172
|
+
case "nested":
|
|
173
|
+
case "structured":
|
|
174
|
+
case "icu":
|
|
175
|
+
default:
|
|
176
|
+
expect(JSON.parse(fileDataString)).toEqual(data);
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
formats.forEach(({ format, data, ext }) => {
|
|
182
|
+
it(`writes the ${format} format to disk`, async () => {
|
|
183
|
+
mockApiCall(data);
|
|
184
|
+
await verifySavedData(format, data, ext);
|
|
185
|
+
});
|
|
255
186
|
});
|
|
256
187
|
});
|
|
257
188
|
|
package/lib/pull.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
Token,
|
|
18
18
|
Project,
|
|
19
19
|
SupportedFormat,
|
|
20
|
+
SupportedExtension,
|
|
20
21
|
ComponentFolder,
|
|
21
22
|
ComponentSource,
|
|
22
23
|
Source,
|
|
@@ -49,6 +50,7 @@ export const writeFile = (path: string, data: string) =>
|
|
|
49
50
|
|
|
50
51
|
const SUPPORTED_FORMATS: SupportedFormat[] = [
|
|
51
52
|
"flat",
|
|
53
|
+
"nested",
|
|
52
54
|
"structured",
|
|
53
55
|
"android",
|
|
54
56
|
"ios-strings",
|
|
@@ -59,7 +61,7 @@ const SUPPORTED_FORMATS: SupportedFormat[] = [
|
|
|
59
61
|
export type JSONFormat = "flat" | "nested" | "structured" | "icu";
|
|
60
62
|
|
|
61
63
|
const IOS_FORMATS: SupportedFormat[] = ["ios-strings", "ios-stringsdict"];
|
|
62
|
-
const JSON_FORMATS: JSONFormat[] = ["flat", "structured", "icu"];
|
|
64
|
+
const JSON_FORMATS: JSONFormat[] = ["flat", "nested", "structured", "icu"];
|
|
63
65
|
|
|
64
66
|
const getJsonFormat = (formats: string[]): JSONFormat => {
|
|
65
67
|
// edge case: multiple json formats specified
|
|
@@ -71,8 +73,9 @@ const getJsonFormat = (formats: string[]): JSONFormat => {
|
|
|
71
73
|
return jsonFormats[jsonFormats.length - 1] || "flat";
|
|
72
74
|
};
|
|
73
75
|
|
|
74
|
-
const FORMAT_EXTENSIONS = {
|
|
76
|
+
const FORMAT_EXTENSIONS: Record<SupportedFormat, SupportedExtension> = {
|
|
75
77
|
flat: ".json",
|
|
78
|
+
nested: ".json",
|
|
76
79
|
structured: ".json",
|
|
77
80
|
android: ".xml",
|
|
78
81
|
"ios-strings": ".strings",
|
|
@@ -93,6 +96,7 @@ const getJsonFormatIsValid = (data: string) => {
|
|
|
93
96
|
// exported for test usage only
|
|
94
97
|
export const getFormatDataIsValid = {
|
|
95
98
|
flat: getJsonFormatIsValid,
|
|
99
|
+
nested: getJsonFormatIsValid,
|
|
96
100
|
structured: getJsonFormatIsValid,
|
|
97
101
|
icu: getJsonFormatIsValid,
|
|
98
102
|
android: (data: string) => data.includes("<string"),
|
|
@@ -436,7 +440,10 @@ async function downloadAndSave(
|
|
|
436
440
|
|
|
437
441
|
const nameExt = getFormatExtension(format);
|
|
438
442
|
const nameBase = "components";
|
|
439
|
-
|
|
443
|
+
|
|
444
|
+
// we need to clean the folder name by itself first, otherwise we can
|
|
445
|
+
// end up with "empty" words and weird hyphenation.
|
|
446
|
+
const nameFolder = `__${cleanFileName(componentFolder.name)}`;
|
|
440
447
|
const namePostfix = `__${variantApiId || "base"}`;
|
|
441
448
|
|
|
442
449
|
const fileName = cleanFileName(
|
package/lib/types.ts
CHANGED
|
@@ -23,12 +23,15 @@ export interface ComponentFolder {
|
|
|
23
23
|
|
|
24
24
|
export type SupportedFormat =
|
|
25
25
|
| "flat"
|
|
26
|
+
| "nested"
|
|
26
27
|
| "structured"
|
|
27
28
|
| "android"
|
|
28
29
|
| "ios-strings"
|
|
29
30
|
| "ios-stringsdict"
|
|
30
31
|
| "icu";
|
|
31
32
|
|
|
33
|
+
export type SupportedExtension = ".json" | ".xml" | ".strings" | ".stringsdict";
|
|
34
|
+
|
|
32
35
|
type ComponentsSourceBool = boolean;
|
|
33
36
|
type ComponentsSourceConfig = {
|
|
34
37
|
root?: boolean | { status: string };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { cleanFileName } from "./cleanFileName";
|
|
2
|
+
|
|
3
|
+
describe("cleanFileName tests", () => {
|
|
4
|
+
it("correctly cleans emojis", () => {
|
|
5
|
+
const folderName = "👍Good Folder";
|
|
6
|
+
expect(cleanFileName(folderName)).toEqual("good-folder");
|
|
7
|
+
|
|
8
|
+
const fileName = "👍 Good Folder";
|
|
9
|
+
expect(cleanFileName(fileName)).toEqual("good-folder");
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export function cleanFileName(fileName: string): string {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
.replace(/[^a-zA-Z0-9-_.]/g, "")
|
|
5
|
-
|
|
2
|
+
let parts = fileName.split(/\s{1,}/g);
|
|
3
|
+
parts = parts.map((part) =>
|
|
4
|
+
part.replace(/[^a-zA-Z0-9-_.]/g, "").toLowerCase()
|
|
5
|
+
);
|
|
6
|
+
parts = parts.filter((part) => part !== "");
|
|
7
|
+
return parts.join("-");
|
|
6
8
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dittowords/cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "Command Line Interface for Ditto (dittowords.com).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "bin/index.js",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@sentry/node": "^7.64.0",
|
|
62
62
|
"@types/babel-traverse": "^6.25.7",
|
|
63
63
|
"@types/fs-extra": "^11.0.1",
|
|
64
|
-
"axios": "^
|
|
64
|
+
"axios": "^1.6.0",
|
|
65
65
|
"boxen": "^5.1.2",
|
|
66
66
|
"chalk": "^4.1.0",
|
|
67
67
|
"commander": "^6.1.0",
|