RCT 2021-1 Protips and Doc collection

陳奕安
6 min readMay 6, 2021

ProTip: How To Split Large Branches Into Small Pull Requests

ref: https://medium.com/@groksrc/protip-how-to-split-large-branches-into-small-pull-requests-81d607660c05

# Step 1: Create a patch against your base branch

$git diff develop feature/player-management > ../my_pr_patch

# Step 2: Checkout a new branch

$ git checkout develop

$ git checkout -b xxxxxxxxxxxxx

# Step 3: Apply Changes Selectively

$git apply — ignore-space-change — ignore-whitespace ../my_pr_patch

# Step 4: Stash the Remaining Changes

Doc — private npm: how to document for each version

How to build a release note

*Azure Pipelines

Write code on Azure DevOps with Azure Functions

Other references

Github automated

Use custom tools (gren & vuepress)

Q: How to align those versions?

  • NX multiple library & ProGet package versions & Azure DevOps release tag version
  • Git release & Publish to ProGet auto or manually?
  • Options:

1.Like angular update all lib to the same version?

2.Mention lib name & version in git commit messages and version it one by one?

  • What’s the format? (eg. xxxxlib-0.1.1 message……… )

Handlebars templates:

https://github.com/rfennell/AzurePipelines/tree/main/SampleTemplates/XplatGenerateReleaseNotes%20(Node%20based)/Version%203 — Connect to preview

workItems, relatedWorkItems: (Azure Boards)

Using work items to track user stories, & more — Azure Boards

Ngx-translate relative

Ngx-translate plugins

ngx-translate/core

  • ngx-translate-zombies
  • by @seveves: A vscode extension that finds unused translation keys and shows them in a diff view (so called zombies).
  • ngx-translate-lint
  • by @svoboda-rabstvo: Simple CLI tools for check ngx-translate keys in whole app

Tool for vs code

  • Lingua
  • by @chr33z: A visual studio code extension to help managing translations for ngx-translate — featuring inline translation lookup and in-place translation creation and editing.

Program to find and replace text:

Javascript,Nodejs: search for a specific word string in files

get translate rule from Lingua

const regEx = /['|"|`]([a-zA-Z0-9\.\_\-]+)['|"|`]/gm;
const text = editor.document.getText();
const translationDecorations: DecorationOptions[] = [];
const identifierDecorations: DecorationOptions[] = [];
const maxTranslationLength = Configuration.maxTranslationLength();
const showInLineTranslation = Configuration.showInlineTranslation();
let match;
while ((match = regEx.exec(text))) {
let path = match[0].replace(/['|"|`]/g, '').trim();
path = path
.split('.')
.filter((seg) => seg.length > 0)
.join('.');
const translation = translationSet.getTranslation(path);
const isPartialTranslation = translationSet.isPartialMatch(path);
const startPos = editor.document.positionAt(match.index + 1);
const endPos = editor.document.positionAt(match.index + match[0].length);
const n = maxTranslationLength;
if (translation) {
// Translation availble
const shortTranslation = translation.length > n ? translation.substr(0, n - 1) + '...' : translation;
const decoration = getDecoration(showInLineTranslation, startPos, endPos, shortTranslation);
translationDecorations.push(decoration);
} else if (isPartialTranslation) {
// Partial translation
const shortPath = path.length > n ? path.substr(0, n - 1) + '...' : path;
const decoration = getDecoration(false, startPos, endPos, 'Translations available: ' + shortPath + ' ...');
translationDecorations.push(decoration);
}
}

I18n.js replace JSON key format

Working on branch/i18n-json

Config setting

const appPath = "src/app";
const fileExtensions = [".ts", ".html"];
const regEx = /['|"|`]([a-zA-Z0-9\.\_\-]+)['|"|`]/gm;
const translationKey = {
all: new Set(),
full: new Set(),
partial: new Set(),
zombie: new Set(),
};

First start with replaceAppI18n, we use en as our translation key provider and use findJsonKeys to get all and partial keys.

function replaceAppI18n() {
// read json file and key
const jsonPath = "src/assets/i18n/en-US.json";
let rawData = fs.readFileSync(jsonPath);
let enJson = JSON.parse(rawData);
findJsonKeys("", enJson);
// console.log(translationKeySet);
// search
searchFilesInDirectory();
console.log("check zombie", translationKey.zombie);
}
function findJsonKeys(path, json) {
if (!!path) {
translationKey.all.add(path);
if (typeof json === "string") {
translationKey.full.add(path);
translationKey.zombie.add(path);
} else {
translationKey.partial.add(path);
}
path += ".";
}
if (typeof json !== "object") {
return;
}
Object.keys(json).forEach((x) => {
findJsonKeys(`${path}${x}`, json[x]);
});
}

Then we call searchFilesInDirectory and use getFilesInDirectory to list all file paths within the folder.

function searchFilesInDirectory() {
if (!fs.existsSync(appPath)) {
console.log(`Specified directory: ${dir} does not exist`);
return;
}
const files = getFilesInDirectory(appPath);
files.forEach((file) => {
let fileContent = fs.readFileSync(file).toString("utf8");
// We want full words, so we use full word boundary in regex.
let match;
while ((match = regEx.exec(fileContent))) {
let path = match[0].replace(/['|"|`]/g, "").trim();
path = path
.split(".")
.filter((seg) => seg.length > 0)
.join(".");
if (translationKey.all.has(path)) {
const newPath = capitalizeFirstLetter(path);
const re = new RegExp(path, "g");
fileContent = fileContent.replace(re, newPath);
console.log("From:", path);
console.log("To: ", newPath);
// check zombie
translationKey.zombie.delete(path);
}
}
// write file back
fs.writeFile(file, fileContent, "utf8", function (err) {
if (err) return console.log(err);
});
});
}
// Using recursion, we find every file with the desired extention, even if its deeply nested in subfolders.
function getFilesInDirectory(dir) {
if (!fs.existsSync(dir)) {
console.log(`Specified directory: ${dir} does not exist`);
return;
}
let files = [];
fs.readdirSync(dir).forEach((file) => {
const filePath = path.join(dir, file);
const stat = fs.lstatSync(filePath);
// If we hit a directory, apply our function to that dir. If we hit a file, add it to the array of files.
if (stat.isDirectory()) {
const nestedFiles = getFilesInDirectory(filePath);
files = files.concat(nestedFiles);
} else {
if (fileExtensions.includes(path.extname(file))) {
files.push(filePath);
}
}
});
return files;
}
function findJsonKeys(path, json) {
if (!!path) {
translationKey.all.add(path);
if (typeof json === "string") {
translationKey.full.add(path);
translationKey.zombie.add(path);
} else {
translationKey.partial.add(path);
}
path += ".";
}
if (typeof json !== "object") {
return;
}
Object.keys(json).forEach((x) => {
findJsonKeys(`${path}${x}`, json[x]);
});
}

We scant all file that contains the string eg: “string“, ‘string’, `string`, and find is it match to translationKey.

If so we start to replace text with capitalizeFirstLetter Rule.

function capitalizeFirstLetter(path) {
// Only transfer all upper case
if (path !== path.toUpperCase()) {
return path;
}
const capital = (string) =>
string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
let keyList = path.split(".").filter((x) => x.length > 0);
keyList = keyList.map((key) => {
key = key.split("_").filter((x) => x.length > 0);
key = key.map((x) => capital(x));
return key.join("");
});
return keyList.join(".");
}

Finally, we call replaceJsonKey , to format others JSON file’s keys

function replaceJsonKey() {
// read json file and key
const jsonPathList = [
"src/assets/i18n/en-US.json",
"src/assets/i18n/ja-JP.json",
"src/assets/i18n/zh-CN.json",
];
const regExJsonKey = /"([^"]+?)"\s*:/gm;
jsonPathList.forEach((file) => {
let fileContent = fs.readFileSync(file).toString("utf8");
let match;
while ((match = regExJsonKey.exec(fileContent))) {
let key = match[0].replace(/['|"|`|:]/g, "").trim();;
const newKey = capitalizeFirstLetter(key);
fileContent = fileContent.replace(key, newKey);
console.log("From:", key);
console.log("To: ", newKey);
}
// write file back
fs.writeFile(file, fileContent, "utf8", function (err) {
if (err) return console.log(err);
});
});
}

--

--