UNPKG

travelm-agency

Version:

Generate type-safe accessors and decoders for your i18n files

186 lines (185 loc) 8.38 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import cp from "child_process"; import fs from "fs"; import intl_proxy from "intl-proxy"; import path, { dirname } from "path"; import { promisify } from "util"; import { Elm, } from "./elm.min.js"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const readFile = (filePath) => promisify(fs.readFile)(filePath, { encoding: "utf-8" }); const readDir = promisify(fs.readdir); const writeFile = (filePath, data) => __awaiter(void 0, void 0, void 0, function* () { yield promisify(fs.mkdir)(path.parse(filePath).dir, { recursive: true }); yield promisify(fs.writeFile)(filePath, data, { encoding: "utf-8" }); }); const getPluginVersion = () => { const file = path.resolve(__dirname, "..", "package.json"); const nodeJson = JSON.parse(fs.readFileSync(file, { encoding: "utf-8" })); return nodeJson.version; }; export function createInstance() { let ports; const withElmApp = (consumer_1, ...args_1) => __awaiter(this, [consumer_1, ...args_1], void 0, function* (consumer, devMode = false) { let error; if (!ports) { const version = getPluginVersion(); ({ ports } = Elm.Main.init({ flags: { version, intl: intl_proxy, devMode }, })); const throwOnError = (response) => { if (response.error) { error = response.error; } }; ports.sendResponse.subscribe(throwOnError); } return consumer(ports).then((res) => { const previousError = error; if (previousError) { error = undefined; throw new Error(previousError); } return res; }); }); const sendTranslations = (filePaths, devMode = false) => withElmApp((ports) => __awaiter(this, void 0, void 0, function* () { yield Promise.all(filePaths.map((filePath) => __awaiter(this, void 0, void 0, function* () { const fileName = path.parse(filePath).base; const fileContent = yield readFile(filePath); ports.receiveRequest.send({ type: "translation", fileName, fileContent, }); }))); }), devMode); const finishModule = ({ elmPath, generatorMode = null, addContentHash, devMode = false, i18nArgFirst = false, prefixFileIdentifier = false, customHtmlModule = "Html", customHtmlAttributesModule, }) => withElmApp((ports) => __awaiter(this, void 0, void 0, function* () { const elmModuleName = yield elmPathToModuleName(elmPath); return new Promise((resolve, reject) => { const responseHandler = (res) => __awaiter(this, void 0, void 0, function* () { ports.sendResponse.unsubscribe(responseHandler); if (res.error) { reject(res.error); } if (!res.content) { reject(new Error("Received neither error nor content from Elm.")); } else { resolve(res.content); } }); ports.sendResponse.subscribe(responseHandler); ports.receiveRequest.send({ type: "finish", elmModuleName, generatorMode, addContentHash, i18nArgFirst, prefixFileIdentifier, customHtmlAttributesModule: customHtmlAttributesModule !== null && customHtmlAttributesModule !== void 0 ? customHtmlAttributesModule : `${customHtmlModule}.Attributes`, customHtmlModule: customHtmlModule, }); }); }), devMode).then((_a) => __awaiter(this, [_a], void 0, function* ({ elmFile, optimizedJson }) { return ({ elmFile: yield runElmFormat(elmFile), optimizedJson, }); })); return { withElmApp, sendTranslations, finishModule, }; } // This function should not be necessary once https://github.com/the-sett/elm-syntax-dsl/issues/42 is fixed. const runElmFormat = (code) => __awaiter(void 0, void 0, void 0, function* () { return new Promise((resolve, reject) => { const elmFormatProcess = cp.spawn("npm", ["exec", "elm-format", "--", "--stdin"], { shell: true, stdio: "pipe" }); elmFormatProcess.stderr.pipe(process.stderr); let formattedCode = ""; elmFormatProcess.stdout.on("data", (chunk) => { formattedCode += chunk; }); elmFormatProcess.stdout.on("end", () => { elmFormatProcess.kill(); resolve(formattedCode); }); elmFormatProcess.on("error", (err) => { reject(err); }); elmFormatProcess.stdin.write(code); elmFormatProcess.stdin.end(); }); }); export const run = (options) => __awaiter(void 0, void 0, void 0, function* () { const { elmPath, translationDir, generatorMode, addContentHash, devMode, i18nArgFirst, prefixFileIdentifier, customHtmlModule, customHtmlAttributesModule, } = options; const translationFilePaths = (yield readDir(translationDir)).map((fileName) => path.resolve(translationDir, fileName)); if (translationFilePaths.length === 0) { throw new Error(`Given translation directory ${translationDir} does not contain any files`); } const instance = createInstance(); yield instance.sendTranslations(translationFilePaths, devMode); const { elmFile, optimizedJson } = yield instance.finishModule({ elmPath, generatorMode, addContentHash, devMode, i18nArgFirst, prefixFileIdentifier, customHtmlModule, customHtmlAttributesModule, }); const elmPromise = writeFile(elmPath, elmFile); let jsonPromises = []; if (options.generatorMode === "dynamic") { jsonPromises = optimizedJson.map(({ filename, content }) => writeFile(path.join(options.jsonPath, filename), content)); } yield Promise.all([elmPromise, ...jsonPromises]); }); let elmConfig; const elmPathToModuleName = (elmPath) => __awaiter(void 0, void 0, void 0, function* () { const absoluteElmPath = path.resolve(elmPath); const elmJsonPath = lookForElmJsonRecursively(path.dirname(absoluteElmPath)); const elmJsonDir = path.dirname(elmJsonPath); if (!elmConfig) { elmConfig = yield JSON.parse(yield readFile(elmJsonPath)); } const elmPathRelativeToElmJson = path.relative(elmJsonDir, absoluteElmPath); const possibleSourceDirs = elmConfig["source-directories"] .map((srcDir) => srcDir.split(path.posix.sep).join(path.sep)) .filter((srcDir) => elmPathRelativeToElmJson.startsWith(srcDir)); if (possibleSourceDirs.length == 0) { throw new Error("Could not determine elm module name"); } if (possibleSourceDirs.length > 1) { throw new Error("Multiple matching source directories: " + possibleSourceDirs.join(",")); } return elmPathRelativeToElmJson .replace(".elm", "") .replace(possibleSourceDirs[0], "") .split(path.sep) .filter((s) => s) .join("."); }); const lookForElmJsonRecursively = (directory, level = 0) => { const attempt = path.join(directory, "elm.json"); if (fs.existsSync(attempt)) { return attempt; } // avoid recursing infinitely because of symlinks or something if (level > 10) { throw new Error("Tried to find elm.json recursively but could not find it."); } return lookForElmJsonRecursively(path.dirname(directory), level + 1); };