fix: Clean up list command output, adding --varCounts argument (#3)

This commit is contained in:
Eemeli Aro 2022-03-08 19:32:32 -06:00
Родитель b17f2217fa
Коммит 82066cd09d
2 изменённых файлов: 107 добавлений и 94 удалений

157
cli.js
Просмотреть файл

@ -3,86 +3,84 @@
import chalk from 'chalk'
import { extname } from 'path'
import yargs from 'yargs'
import { forEachPropertiesFile, getInfo } from './lib/get-info.js'
import { listPropertiesFiles } from './lib/list-properties-files.js'
import { transformJs } from './lib/transform-js.js'
import { transformProperties } from './lib/transform-properties.js'
yargs(process.argv.slice(2))
.options({
all: {
alias: 'a',
default: false,
desc: 'When given a JS file, migrate all messages in .properties file',
type: 'boolean'
},
attrDot: {
alias: 'd',
default: 'last',
describe: 'Consider a dot in a property key to start an attribute name',
choices: ['first', 'last', 'none']
},
bug: {
alias: 'b',
desc: 'Bugzilla bug id',
requiresArg: true,
type: 'string'
},
exclude: {
alias: 'e',
default: [],
desc: '.properties files to exclude; should match the file path end',
requiresArg: true,
type: 'array'
},
format: {
alias: 'f',
desc: "Command for Python code formatter. Set to '' to disable.",
default: './mach lint --fix',
type: 'string'
},
ftlPath: {
alias: 'p',
desc: 'Path to target FTL file',
requiresArg: true,
type: 'string'
},
ftlPrefix: {
alias: 'x',
desc: 'Prefix for Fluent message keys',
requiresArg: true,
type: 'string'
},
include: {
alias: 'i',
default: [],
desc: '.properties files to include; should match the file path end',
requiresArg: true,
type: 'array'
},
'js-only': {
alias: 'j',
default: false,
desc: 'Only migrate JS file',
type: 'boolean'
},
root: {
alias: 'r',
desc: 'Root of mozilla-central (usually autodetected)',
requiresArg: true,
type: 'string'
},
strict: {
alias: 's',
default: false,
desc: 'In JS, require string literals matching message keys to be detected as known method arguments',
type: 'boolean'
}
})
.command(
'$0 <path>',
'Convert files to use Fluent Localization rather than string bundles',
{},
{
all: {
alias: 'a',
default: false,
desc: 'When given a JS file, migrate all messages in .properties file',
type: 'boolean'
},
attrDot: {
alias: 'd',
default: 'last',
describe: 'Consider a dot in a property key to start an attribute name',
choices: ['first', 'last', 'none']
},
bug: {
alias: 'b',
desc: 'Bugzilla bug id',
requiresArg: true,
type: 'string'
},
exclude: {
alias: 'e',
default: [],
desc: '.properties files to exclude; should match the file path end',
requiresArg: true,
type: 'array'
},
format: {
alias: 'f',
desc: "Command for Python code formatter. Set to '' to disable.",
default: './mach lint --fix',
type: 'string'
},
ftlPath: {
alias: 'p',
desc: 'Path to target FTL file',
requiresArg: true,
type: 'string'
},
ftlPrefix: {
alias: 'x',
desc: 'Prefix for Fluent message keys',
requiresArg: true,
type: 'string'
},
include: {
alias: 'i',
default: [],
desc: '.properties files to include; should match the file path end',
requiresArg: true,
type: 'array'
},
'js-only': {
alias: 'j',
default: false,
desc: 'Only migrate JS file',
type: 'boolean'
},
root: {
alias: 'r',
desc: 'Root of mozilla-central (usually autodetected)',
requiresArg: true,
type: 'string'
},
strict: {
alias: 's',
default: false,
desc: 'In JS, require string literals matching message keys to be detected as known method arguments',
type: 'boolean'
}
},
({ path, ...options }) =>
extname(path) === '.properties'
? transformProperties(path, options)
@ -91,9 +89,16 @@ yargs(process.argv.slice(2))
.command(
'list [filename..]',
'Show information about .properties files',
{},
(args) => forEachPropertiesFile(args.filename, getInfo)
'Show information about .properties files in the given files or directories.',
{
varCounts: {
alias: 'c',
default: false,
desc: 'For each .properties file, list message counts by number of variables [no vars, 1 var, 2 vars, ...]',
type: 'boolean'
}
},
listPropertiesFiles
)
.help()

Просмотреть файл

@ -2,34 +2,42 @@ import { parse } from 'dot-properties'
import { readdir, readFile, stat } from 'fs/promises'
import { relative, resolve } from 'path'
export async function getInfo(path) {
const src = await readFile(path, 'utf8')
const info = [0, 0, 0, 0]
for (const msg of Object.values(parse(src))) {
const m = msg.match(/%(\d\$)?|\$/g)
const idx = m ? Math.min(m.length, 3) : 0
info[idx] += 1
}
return info
}
export async function forEachPropertiesFile(roots, cb) {
for (const root of roots) {
export async function listPropertiesFiles({ filename = ['.'], varCounts }) {
for (const root of filename) {
const path = resolve(root)
const stats = await stat(path)
if (stats.isFile()) {
const res = await cb(path)
if (res) console.log(relative('.', path), res)
const relPath = relative('.', path)
if (varCounts) console.log(relPath, await getInfo(path))
else console.log(relPath)
} else if (stats.isDirectory()) {
console.log(relative('.', path))
const relDir = relative('.', path) || '.'
console.log(`${relDir}/`)
for await (const pf of findPropertiesFiles(path)) {
const res = await cb(pf)
if (res) console.log(' ', relative(path, pf), res)
const relPath = relative(path, pf)
if (varCounts) console.log(' ', relPath, await getInfo(pf))
else console.log(' ', relPath)
}
} else throw new Error(`Not a file or directory: ${path}`)
}
}
async function getInfo(path) {
const src = await readFile(path, 'utf8')
const info = [0]
for (const msg of Object.values(parse(src))) {
const m = msg.match(/%(\d\$)?|\$/g)
const varCount = m?.length ?? 0
if (info.length < varCount + 1) {
const prevLen = info.length
info.length = varCount + 1
info.fill(0, prevLen)
}
info[varCount] += 1
}
return info
}
async function* findPropertiesFiles(root) {
for (const ent of await readdir(root, { withFileTypes: true })) {
if (ent.isDirectory()) {