From c26a41ce70a56c73571c51e1fb4aca5a5b4de225 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Sat, 27 Mar 2021 13:55:15 +0100 Subject: pogen WIP --- packages/pogen/example/proj1/package.json | 11 ++ packages/pogen/example/proj1/src/sample.ts | 0 packages/pogen/example/proj1/tsconfig.json | 27 +++++ packages/pogen/example/test.ts | 3 +- packages/pogen/package.json | 4 +- packages/pogen/po2.js | 32 ++++++ packages/pogen/pogen.ts | 172 ++++++++++++++++++----------- packages/pogen/tsconfig.json | 4 +- packages/taler-util/README.md | 5 +- 9 files changed, 186 insertions(+), 72 deletions(-) create mode 100644 packages/pogen/example/proj1/package.json create mode 100644 packages/pogen/example/proj1/src/sample.ts create mode 100644 packages/pogen/example/proj1/tsconfig.json create mode 100644 packages/pogen/po2.js (limited to 'packages') diff --git a/packages/pogen/example/proj1/package.json b/packages/pogen/example/proj1/package.json new file mode 100644 index 000000000..954139ecf --- /dev/null +++ b/packages/pogen/example/proj1/package.json @@ -0,0 +1,11 @@ +{ + "name": "proj1", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/packages/pogen/example/proj1/src/sample.ts b/packages/pogen/example/proj1/src/sample.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/pogen/example/proj1/tsconfig.json b/packages/pogen/example/proj1/tsconfig.json new file mode 100644 index 000000000..30cb65e1d --- /dev/null +++ b/packages/pogen/example/proj1/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "composite": true, + "declaration": true, + "declarationMap": false, + "target": "ES6", + "module": "ESNext", + "moduleResolution": "node", + "sourceMap": true, + "lib": ["es6"], + "types": ["node"], + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "strict": true, + "strictPropertyInitialization": false, + "outDir": "lib", + "noImplicitAny": true, + "noImplicitThis": true, + "incremental": true, + "esModuleInterop": true, + "importHelpers": true, + "rootDir": "./src", + "typeRoots": ["./node_modules/@types"] + }, + "include": ["src/**/*"] +} diff --git a/packages/pogen/example/test.ts b/packages/pogen/example/test.ts index d7d5d88d6..021f3f328 100644 --- a/packages/pogen/example/test.ts +++ b/packages/pogen/example/test.ts @@ -19,9 +19,10 @@ It has multiple lines, and a trailing empty line. */ console.log(/*lol*/i18n.foo`Hello7,${123} World${42}`); - +// @ts-expect-error i18n.plural(i18n`one ${"foo"}`, i18`many ${"bar"}`); +// @ts-expect-error i18n.plural(i18n.foo`one bla ${"foo"}`, i18.foo`many bla ${"bar"}`); let x = 42; diff --git a/packages/pogen/package.json b/packages/pogen/package.json index b5997abf9..d023632e4 100644 --- a/packages/pogen/package.json +++ b/packages/pogen/package.json @@ -1,7 +1,9 @@ { "name": "@gnu-taler/pogen", "version": "0.0.5", - "main": "bin/pogen.js", + "bin": { + "pogen": "lib/pogen.js" + }, "author": "Florian Dold", "license": "GPL-2.0+", "scripts": { diff --git a/packages/pogen/po2.js b/packages/pogen/po2.js new file mode 100644 index 000000000..532a1522f --- /dev/null +++ b/packages/pogen/po2.js @@ -0,0 +1,32 @@ +const ts = require("typescript"); + +const configPath = ts.findConfigFile( + /*searchPath*/ "./", + ts.sys.fileExists, + "tsconfig.json" + ); +if (!configPath) { + throw new Error("Could not find a valid 'tsconfig.json'."); +} + +console.log(configPath); + +const cmdline = ts.getParsedCommandLineOfConfigFile(configPath, {}, { + fileExists: ts.sys.fileExists, + getCurrentDirectory: ts.sys.getCurrentDirectory, + onUnRecoverableConfigFileDiagnostic: (e) => console.log(e), + readDirectory: ts.sys.readDirectory, + readFile: ts.sys.readFile, + useCaseSensitiveFileNames: true, +}) + +console.log(cmdline); + +const prog = ts.createProgram({ + options: cmdline.options, + rootNames: cmdline.fileNames, +}); + +const allFiles = prog.getSourceFiles(); + +console.log(allFiles.map(x => x.path)); \ No newline at end of file diff --git a/packages/pogen/pogen.ts b/packages/pogen/pogen.ts index 98ecdf785..972623645 100644 --- a/packages/pogen/pogen.ts +++ b/packages/pogen/pogen.ts @@ -14,7 +14,6 @@ TALER; see the file COPYING. If not, see */ - /** * Generate .po file from list of source files. * @@ -24,15 +23,15 @@ * @author Florian Dold */ -"use strict"; - -import {readFileSync} from "fs"; +/** + * Imports. + */ +import { readFileSync } from "fs"; import * as ts from "typescript"; - function wordwrap(str: string, width: number = 80): string[] { - var regex = '.{1,' + width + '}(\\s|$)|\\S+(\\s|$)'; - return str.match(RegExp(regex, 'g')); + var regex = ".{1," + width + "}(\\s|$)|\\S+(\\s|$)"; + return str.match(RegExp(regex, "g")); } export function processFile(sourceFile: ts.SourceFile) { @@ -48,10 +47,10 @@ export function processFile(sourceFile: ts.SourceFile) { let te = node; let textFragments = [te.head.text]; for (let tsp of te.templateSpans) { - textFragments.push(`%${(textFragments.length-1)/2+1}$s`); + textFragments.push(`%${(textFragments.length - 1) / 2 + 1}$s`); textFragments.push(tsp.literal.text.replace(/%/g, "%%")); } - return textFragments.join(''); + return textFragments.join(""); default: return "(pogen.ts: unable to parse)"; } @@ -70,8 +69,11 @@ export function processFile(sourceFile: ts.SourceFile) { if (!lastComments) { return; } - let candidate = lastComments[lastComments.length-1]; - let candidateEndLine = ts.getLineAndCharacterOfPosition(sourceFile, candidate.end).line; + let candidate = lastComments[lastComments.length - 1]; + let candidateEndLine = ts.getLineAndCharacterOfPosition( + sourceFile, + candidate.end, + ).line; if (candidateEndLine != lc.line - 1) { return; } @@ -85,8 +87,8 @@ export function processFile(sourceFile: ts.SourceFile) { // Remove comment leader and trailer, // handling white space just like xgettext. text = text - .replace(/^[/][*](\s*?\n|\s*)?/, "") - .replace(/(\n[ \t]*?)?[*][/]$/, ""); + .replace(/^[/][*](\s*?\n|\s*)?/, "") + .replace(/(\n[ \t]*?)?[*][/]$/, ""); break; } return text; @@ -123,13 +125,15 @@ export function processFile(sourceFile: ts.SourceFile) { line: number; } - function processTaggedTemplateExpression(tte: ts.TaggedTemplateExpression): TemplateResult { + function processTaggedTemplateExpression( + tte: ts.TaggedTemplateExpression, + ): TemplateResult { let lc = ts.getLineAndCharacterOfPosition(sourceFile, tte.pos); if (lc.line != lastTokLine) { preLastTokLine = lastTokLine; lastTokLine = lc.line; } - let path = getPath(tte.tag) + let path = getPath(tte.tag); let res: TemplateResult = { path, line: lc.line, @@ -141,21 +145,21 @@ export function processFile(sourceFile: ts.SourceFile) { function formatMsgComment(line: number, comment?: string) { if (comment) { - for (let cl of comment.split('\n')) { + for (let cl of comment.split("\n")) { console.log(`#. ${cl}`); } } - console.log(`#: ${sourceFile.fileName}:${line+1}`); + console.log(`#: ${sourceFile.fileName}:${line + 1}`); console.log(`#, c-format`); } function formatMsgLine(head: string, msg: string) { // Do escaping, wrap break at newlines let parts = msg - .match(/(.*\n|.+$)/g) - .map((x) => x.replace(/\n/g, '\\n')) - .map((p) => wordwrap(p)) - .reduce((a,b) => a.concat(b)); + .match(/(.*\n|.+$)/g) + .map((x) => x.replace(/\n/g, "\\n")) + .map((p) => wordwrap(p)) + .reduce((a, b) => a.concat(b)); if (parts.length == 1) { console.log(`${head} "${parts[0]}"`); } else { @@ -166,18 +170,13 @@ export function processFile(sourceFile: ts.SourceFile) { } } - interface JsxProcessingContext { - - } - function getJsxElementPath(node: ts.Node) { let path; let process = (childNode) => { switch (childNode.kind) { - case ts.SyntaxKind.JsxOpeningElement: - { + case ts.SyntaxKind.JsxOpeningElement: { let e = childNode as ts.JsxOpeningElement; - return path = getPath(e.tagName); + return (path = getPath(e.tagName)); } default: break; @@ -188,15 +187,14 @@ export function processFile(sourceFile: ts.SourceFile) { } function translateJsxExpression(node: ts.Node, h) { - switch (node.kind) { - case ts.SyntaxKind.StringLiteral: - { - let e = node as ts.StringLiteral; - return e.text; - } - default: - return `%${h[0]++}$s`; + switch (node.kind) { + case ts.SyntaxKind.StringLiteral: { + let e = node as ts.StringLiteral; + return e.text; } + default: + return `%${h[0]++}$s`; + } } function trim(s) { @@ -208,8 +206,7 @@ export function processFile(sourceFile: ts.SourceFile) { let holeNum = [1]; let process = (childNode) => { switch (childNode.kind) { - case ts.SyntaxKind.JsxText: - { + case ts.SyntaxKind.JsxText: { let e = childNode as ts.JsxText; let s = e.getFullText(); let t = s.split("\n").map(trim).join(" "); @@ -226,8 +223,7 @@ export function processFile(sourceFile: ts.SourceFile) { case ts.SyntaxKind.JsxElement: fragments.push(`%${holeNum[0]++}$s`); break; - case ts.SyntaxKind.JsxExpression: - { + case ts.SyntaxKind.JsxExpression: { let e = childNode as ts.JsxExpression; fragments.push(translateJsxExpression(e.expression, holeNum)); break; @@ -235,8 +231,17 @@ export function processFile(sourceFile: ts.SourceFile) { case ts.SyntaxKind.JsxClosingElement: break; default: - let lc = ts.getLineAndCharacterOfPosition(childNode.getSourceFile(), childNode.getStart()); - console.error(`unrecognized syntax in JSX Element ${ts.SyntaxKind[childNode.kind]} (${childNode.getSourceFile().fileName}:${lc.line+1}:${lc.character+1}`); + let lc = ts.getLineAndCharacterOfPosition( + childNode.getSourceFile(), + childNode.getStart(), + ); + console.error( + `unrecognized syntax in JSX Element ${ + ts.SyntaxKind[childNode.kind] + } (${childNode.getSourceFile().fileName}:${lc.line + 1}:${ + lc.character + 1 + }`, + ); break; } }; @@ -248,8 +253,7 @@ export function processFile(sourceFile: ts.SourceFile) { let res; let process = (childNode) => { switch (childNode.kind) { - case ts.SyntaxKind.JsxElement: - { + case ts.SyntaxKind.JsxElement: { let path = getJsxElementPath(childNode); if (arrayEq(path, ["i18n", "TranslateSingular"])) { res = getJsxContent(childNode); @@ -267,8 +271,7 @@ export function processFile(sourceFile: ts.SourceFile) { let res; let process = (childNode) => { switch (childNode.kind) { - case ts.SyntaxKind.JsxElement: - { + case ts.SyntaxKind.JsxElement: { let path = getJsxElementPath(childNode); if (arrayEq(path, ["i18n", "TranslatePlural"])) { res = getJsxContent(childNode); @@ -281,7 +284,6 @@ export function processFile(sourceFile: ts.SourceFile) { ts.forEachChild(node, process); return res; } - function processNode(node: ts.Node) { switch (node.kind) { @@ -289,7 +291,7 @@ export function processFile(sourceFile: ts.SourceFile) { let path = getJsxElementPath(node); if (arrayEq(path, ["i18n", "Translate"])) { let content = getJsxContent(node); - let {line} = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); + let { line } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); let comment = getComment(node); formatMsgComment(line, comment); formatMsgLine("msgid", content); @@ -298,7 +300,7 @@ export function processFile(sourceFile: ts.SourceFile) { return; } if (arrayEq(path, ["i18n", "TranslateSwitch"])) { - let {line} = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); + let { line } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); let comment = getComment(node); formatMsgComment(line, comment); let singularForm = getJsxSingular(node); @@ -319,8 +321,7 @@ export function processFile(sourceFile: ts.SourceFile) { return; } break; - case ts.SyntaxKind.CallExpression: - { + case ts.SyntaxKind.CallExpression: { // might be i18n.plural(i18n[.X]`...`, i18n[.X]`...`) let ce = node; let path = getPath(ce.expression); @@ -333,9 +334,13 @@ export function processFile(sourceFile: ts.SourceFile) { if (ce.arguments[1].kind != ts.SyntaxKind.TaggedTemplateExpression) { break; } - let {line} = ts.getLineAndCharacterOfPosition(sourceFile, ce.pos); - let t1 = processTaggedTemplateExpression(ce.arguments[0]); - let t2 = processTaggedTemplateExpression(ce.arguments[1]); + let { line } = ts.getLineAndCharacterOfPosition(sourceFile, ce.pos); + let t1 = processTaggedTemplateExpression( + ce.arguments[0], + ); + let t2 = processTaggedTemplateExpression( + ce.arguments[1], + ); let comment = getComment(ce); formatMsgComment(line, comment); @@ -348,10 +353,11 @@ export function processFile(sourceFile: ts.SourceFile) { // Important: no processing for child i18n expressions here return; } - case ts.SyntaxKind.TaggedTemplateExpression: - { + case ts.SyntaxKind.TaggedTemplateExpression: { let tte = node; - let {comment, template, line, path} = processTaggedTemplateExpression(tte); + let { comment, template, line, path } = processTaggedTemplateExpression( + tte, + ); if (path[0] != "i18n") { break; } @@ -367,10 +373,36 @@ export function processFile(sourceFile: ts.SourceFile) { } } -const fileNames = process.argv.slice(2); +function main() { + const configPath = ts.findConfigFile( + /*searchPath*/ "./", + ts.sys.fileExists, + "tsconfig.json", + ); + if (!configPath) { + throw new Error("Could not find a valid 'tsconfig.json'."); + } -console.log( -`# SOME DESCRIPTIVE TITLE. + const cmdline = ts.getParsedCommandLineOfConfigFile( + configPath, + {}, + { + fileExists: ts.sys.fileExists, + getCurrentDirectory: ts.sys.getCurrentDirectory, + onUnRecoverableConfigFileDiagnostic: (e) => console.log(e), + readDirectory: ts.sys.readDirectory, + readFile: ts.sys.readFile, + useCaseSensitiveFileNames: true, + }, + ); + + const fileNames = cmdline.fileNames; + + fileNames.sort(); + + const outChunks: string[] = []; + + outChunks.push(`# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. @@ -388,11 +420,19 @@ msgstr "" "MIME-Version: 1.0\\n" "Content-Type: text/plain; charset=UTF-8\\n" "Content-Transfer-Encoding: 8bit\\n"`); -console.log() -fileNames.sort(); + fileNames.forEach((fileName) => { + let sourceFile = ts.createSourceFile( + fileName, + readFileSync(fileName).toString(), + ts.ScriptTarget.ES2016, + /*setParentNodes */ true, + ); + processFile(sourceFile); + }); + + const out = outChunks.join(""); + console.log(out); +} -fileNames.forEach(fileName => { - let sourceFile = ts.createSourceFile(fileName, readFileSync(fileName).toString(), ts.ScriptTarget.ES2016, /*setParentNodes */ true); - processFile(sourceFile); -}); +main(); diff --git a/packages/pogen/tsconfig.json b/packages/pogen/tsconfig.json index c4a2154ea..d61e5595a 100644 --- a/packages/pogen/tsconfig.json +++ b/packages/pogen/tsconfig.json @@ -4,8 +4,8 @@ "target": "es5", "noImplicitAny": false, "sourceMap": false, - "outDir": "bin", - "incremental": true, + "outDir": "lib", + "incremental": true }, "files": [ "pogen.ts" diff --git a/packages/taler-util/README.md b/packages/taler-util/README.md index 07d74eeeb..e3f00bd79 100644 --- a/packages/taler-util/README.md +++ b/packages/taler-util/README.md @@ -5,5 +5,6 @@ This package implements various utility functionality for GNU Taler. ## When should something be moved to this package? -The ``@gnu-taler/taler-util`` package should have minimal dependencies -and as few platform-specific functionality as possible. +The ``@gnu-taler/taler-util`` package should have minimal dependencies and +should not be platform specific. + -- cgit v1.2.3