Skip to content

Commit

Permalink
feat(cli): updated cli and added progress batr
Browse files Browse the repository at this point in the history
  • Loading branch information
skolmer committed Nov 11, 2016
1 parent 7df7df8 commit ee115b6
Show file tree
Hide file tree
Showing 16 changed files with 389 additions and 229 deletions.
2 changes: 1 addition & 1 deletion __tests__/exportTranslationKeys.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('exportTranslationKeys', () => {

it('should report export progress', async () => {
const rootPath = path.resolve(__dirname, './data')
let last = 0
let last = -1
await exportTranslationKeys({
rootPath,
logger: { toConsole: true },
Expand Down
2 changes: 1 addition & 1 deletion __tests__/generateTranslationSchema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ describe('i18n-tag-schema', () => {
})

it('should report schema progress', async () => {
let last = 0
let last = -1
const filter = '\\.jsx?$'
const rootPath = path.resolve(__dirname, './data')
await generateTranslationSchema({
Expand Down
2 changes: 1 addition & 1 deletion __tests__/validateTranslations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('validateTranslations', () => {
})

it('should report validation progress', async () => {
let last = 0
let last = -1
const schemaPath = path.resolve(__dirname, './data/schema.json')
const rootPath = path.resolve(__dirname, './data')
try {
Expand Down
188 changes: 108 additions & 80 deletions cli/index.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,73 @@
#!/usr/bin/env node
var program = require('commander');
var i18nTagSchemaModule = require('../dist/lib');
var i18nTagSchema = i18nTagSchemaModule.default;
var validateSchema = i18nTagSchemaModule.validateSchema;
var generateTranslationSchema = i18nTagSchemaModule.generateTranslationSchema;
var validateTranslations = i18nTagSchemaModule.validateTranslations;
var exportTranslationKeys = i18nTagSchemaModule.exportTranslationKeys;
var colors = require('colors');
var chalk = require('chalk');
var pathLib = require('path');
var fs = require('fs');
var ProgressBar = require('progress')

const isWin32 = process.platform === 'win32';
const errorSymbol = isWin32 ? '×' : '✖';
const successSymbol = isWin32 ? '√' : '✔';
const progressComplete = isWin32 ? chalk.green('#') : chalk.green.inverse(' ');
const progressIncomplete = isWin32 ? chalk.white(' ') : chalk.white.inverse(' ')

let log = []

const logger = {
clear: () => log = [],
info: (message) => log.push(' ' + chalk.bgWhite(chalk.black('info:')) + ' ' + message),
warn: (message) => log.push(' ' + chalk.bgYellow(chalk.black('warn:')) + ' ' + message),
error: (message) => log.push(' ' + chalk.bgRed(chalk.white('error:')) + ' ' + message),
flush: () => console.log('') || log.forEach((message) => console.log(message))
}

const progressBar = () => {
let progress
return (current, total, name) => {
if(!progress) {
progress = new ProgressBar(':bar :percent :etas :name', { total: total, complete: progressComplete, incomplete: progressIncomplete, width: 20 });
} else {
progress.update(current / total, {
name: name
})
}
}
}

function formatResult(message) {
message = message.replace(/(\d+)\s*%\s*translated/g, (str, value) => {
var val = Number.parseInt(value);
if (val < 50) {
return colors.bgRed(colors.white(str));
return chalk.bgRed(chalk.white(str));
} else if (val < 100) {
return colors.bgYellow(colors.black(str));
return chalk.bgYellow(chalk.black(str));
} else {
return colors.bgGreen(colors.black(str));
return chalk.bgGreen(chalk.black(str));
}
});
message = message.replace(/(\d+)\s*keys/g, (str) => {
return colors.bgWhite(colors.black(str));
return chalk.bgWhite(chalk.black(str));
});
message = message.replace(/(\d+)\s*added/g, (str) => {
return colors.bgGreen(colors.black(str));
return chalk.bgGreen(chalk.black(str));
});
message = message.replace(/(\d+)\s*removed/g, (str) => {
return colors.bgRed(colors.white(str));
return chalk.bgRed(chalk.white(str));
});
message = message.replace(/(\d+)\s*missing/g, (str, value) => {
var val = Number.parseInt(value);
if (val === 0) {
return colors.bgGreen(colors.black(str));
return chalk.bgGreen(chalk.black(str));
} else {
return colors.bgRed(colors.white(str));
return chalk.bgRed(chalk.white(str));
}
});
message = message.replace(/(\d+)\s*invalid/g, (str) => {
return colors.bgYellow(colors.black(str));
return chalk.bgYellow(chalk.black(str));
});
message = message.replace(/\n/g, () => {
return '\n ';
Expand All @@ -46,99 +76,97 @@ function formatResult(message) {
}

program
.version('0.0.1')
.version('2.0.0')
.usage('<path> [options]')
.option('-s, --schema <path>', 'set schema path. defaults to ./translation.schema.json')
.option('-f, --filter <regex>', 'a regular expression to filter source files. defaults to \\.jsx?$')
.option('-v, --validate', 'use to validate translation file(s). path has to be a JSON file or directory. requires --schema <path>')
.option('-e, --export <path>', 'export all translation keys FROM a JavaScript file or directory.')
.option('-t, --target <path>', 'export all translation keys TO a JSON file. requires --export <path>.\n If --target is not set, JSON will be printed to the output.')
.action(function (path) {
logger.clear();
if (program.validate) {
if (!program.schema) {
console.log(' ' + colors.bgRed(colors.white('error:')) + ' ' + 'option `--schema <path>` missing');
console.log(' ' + chalk.bgRed('error:') + ' ' + 'option `--schema <path>` missing');
process.exit(1);
}
validateSchema(path, program.schema, (output, type) => {
switch (type) {
case 'info':
console.log(' ' + colors.bgWhite(colors.black('info:')) + ' ' + output);
break;
case 'warn':
console.log(' ' + colors.bgYellow(colors.black('warn:')) + ' ' + output);
break;
case 'error':
console.log('');
console.log(' ' + colors.red('X') + ' invalid: ' + formatResult(output));
process.exit(1);
break;
case 'success':
console.log('');
console.log(' ' + colors.green('√') + ' valid: ' + formatResult(output));
break;
}
const result = validateTranslations({
rootPath: path,
schemaPath: program.schema,
logger: logger,
progress: progressBar()
}).then((result) => {
logger.flush();
console.log('');
console.log(' ' + chalk.green(successSymbol) + ' valid: ' + formatResult(result));
process.exit(0);
}).catch((err) => {
logger.flush();
console.log('');
console.log(' ' + chalk.red(errorSymbol) + ' invalid: ' + formatResult(err.message));
process.exit(1);
});
} else if (program.export || program.filter) {
if (program.export && (pathLib.extname(program.export) && !pathLib.extname(program.export).match(program.filter || '\\.jsx?$'))) {
console.log(' ' + colors.bgRed(colors.white('error:')) + ' ' + program.export + ' is not a JavaScript file.');
} else if (program.export) {
const filter = program.filter || '\\.jsx?$';
if (program.export && (pathLib.extname(program.export) && !pathLib.extname(program.export).match(filter))) {
logger.error(program.export + ' does not match filter \'' + filter + '\'.');
logger.flush();
process.exit(1);
}
if (program.target && pathLib.extname(program.target) !== '.json') {
console.log(' ' + colors.bgRed(colors.white('error:')) + ' ' + program.target + ' is not a json file.');
logger.error(program.target + ' is not a json file.');
logger.flush();
process.exit(1);
}
exportTranslationKeys(path, program.export,
(output, type) => {
if (program.target) {
switch (type) {
case 'info':
console.log(' ' + colors.bgWhite(colors.black('info:')) + ' ' + output);
break;
case 'warn':
console.log(' ' + colors.bgYellow(colors.black('warn:')) + ' ' + output);
break;
case 'error':
console.log(' ' + colors.bgRed(colors.white('error:')) + ' ' + output);
process.exit(1);
break;
case 'success':
console.log(' ' + colors.bgGreen(colors.black('success:')) + ' ' + output);
break;
}
}
},
(templates) => {
if (program.target) {
fs.writeFile(program.target, JSON.stringify(JSON.parse(templates), null, 2), 'utf-8', function (err) {
exportTranslationKeys({
rootPath: path,
filePath: program.export,
filter: program.filter,
logger: (program.target)?logger:undefined,
progress: (program.target)?progressBar():undefined
}).then((result) => {
logger.flush();
if (program.target) {
fs.writeFile(program.target, JSON.stringify(result, null, '\t'), 'utf-8', function (err) {
if (err) {
console.log(' ' + colors.bgRed(colors.white('error:')) + ' ' + err.message);
logger.error(err.message);
logger.flush();
process.exit(1);
return;
}
console.log(' ' + colors.bgGreen(colors.black('success:')) + ' Exported translation keys to ' + program.target);
console.log(' ' + chalk.green(successSymbol) + ' Exported translation keys to ' + program.target);
process.exit(0);
});
} else {
console.log(JSON.stringify(JSON.parse(templates), null, 2));
console.log(JSON.stringify(result, null, '\t'));
process.exit(0);
}
});
}).catch((err) => {
logger.error(err.message);
logger.flush();
process.exit(1);
});
} else {
i18nTagSchema(path, program.filter || '\\.jsx?$', program.schema || './translation.schema.json', (output, type) => {
switch (type) {
case 'info':
console.log(' ' + colors.bgWhite(colors.black('info:')) + ' ' + output);
break;
case 'warn':
console.log(' ' + colors.bgYellow(colors.black('warn:')) + ' ' + output);
break;
case 'error':
console.log(' ' + colors.bgRed(colors.white('error:')) + ' ' + output);
process.exit(1);
break;
case 'success':
console.log(' ' + colors.bgGreen(colors.black('success:')) + ' ' + formatResult(output));
break;
}
})
generateTranslationSchema({
rootPath: path,
filter: program.filter,
logger: (program.schema)?logger:undefined,
schemaPath: program.schema || './translation.schema.json',
progress: (program.schema)?progressBar():undefined
}).then((result) => {
logger.flush();
if (program.schema) {
console.log(' ' + chalk.green(successSymbol) + ' Generated schema ' + program.schema);
process.exit(0);
} else {
console.log(JSON.stringify(result, null, '\t'));
process.exit(0);
}
}).catch((err) => {
logger.error(err.message);
logger.flush();
process.exit(1);
});
}
})
.parse(process.argv);
8 changes: 5 additions & 3 deletions lib/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const readFileTemplates = ({rootPath, filePath, templates = [], templatePatterns
newTemplates = mergeTemplates(templates, parsedTemplates, fileGroup)
logInfo(logger, `${path.relative(rootPath, filePath)} (${length} template${(length === 1) ? '' : 's'})`)
} else {
logInfo(logger, `${path.relative(rootPath, filePath)} (0 templates)`)
logTrace(logger, `${path.relative(rootPath, filePath)} (0 templates)`)
}
return { templates: newTemplates, templatePatterns: newTemplatePatterns }
}
Expand All @@ -69,7 +69,7 @@ export const exportTranslationKeysFromFiles = async ({rootPath, filePath = '.',
logError(logger, error)
throw new Error(error)
}
const progressCallback = (progress) ? throttle(progress, 100) : () => {}
const progressCallback = (progress) ? throttle(progress, 16, true) : () => {}
const fullPath = path.resolve(rootPath, filePath)
const fileStats = fs.lstatSync(fullPath)
if (fileStats.isFile()) {
Expand Down Expand Up @@ -113,6 +113,7 @@ export const exportTranslationKeysFromFiles = async ({rootPath, filePath = '.',
const totalCount = fileNames.length
let templates
let templatePatterns
progressCallback(fileCount, totalCount, '')
for(const fileName of fileNames) {
const { file, filePath } = fileName
try {
Expand All @@ -121,12 +122,13 @@ export const exportTranslationKeysFromFiles = async ({rootPath, filePath = '.',
templates = result.templates
templatePatterns = result.templatePatterns
} catch(err) {
logError(logger, `${filePath}: ${err.messag}`)
logError(logger, `${file}: ${err.message}`)
logTrace(logger, err)
}
fileCount++
progressCallback(fileCount, totalCount, file)
}
if(fileNames.length && progress) progress(totalCount, totalCount, fileNames[fileNames.length-1].file)
log(logger, `exported ${templates.length} translation keys from ${fullPath}`)
return { templates: sortTemplates(templates), templatePatterns }
}
Expand Down
14 changes: 11 additions & 3 deletions lib/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@ export const validateTranslationFiles = async ({rootPath, schemaPath, logger, pr
logError(logger, error)
throw new Error(error)
}
const progressCallback = (progress) ? throttle(progress, 100) : () => {}
if(!await fs.exists(schemaPath)) {
const error = 'schemaPath file does not exist.'
logError(logger, error)
throw new Error(error)
}
const progressCallback = (progress) ? throttle(progress, 16) : () => {}
const fileStats = await fs.lstat(rootPath)
if (fileStats.isFile()) {
const result = await validateFile({
Expand Down Expand Up @@ -143,6 +148,7 @@ export const validateTranslationFiles = async ({rootPath, schemaPath, logger, pr
const totalCount = fileNames.length
const results = []
let success = true
progressCallback(fileCount, totalCount, '')
for(const fileName of fileNames) {
const { file, filePath } = fileName
try {
Expand All @@ -153,14 +159,16 @@ export const validateTranslationFiles = async ({rootPath, schemaPath, logger, pr
})
results.push(result)
} catch (err) {
logError(logger, `${filePath}: ${err.messag}`)
const error = `${file}: ${err.message}`
logError(logger, error)
logTrace(logger, err)
results.push(err.message)
results.push(error)
success = false
}
fileCount++
progressCallback(fileCount, totalCount, file)
}
if(fileNames.length && progress) progress(totalCount, totalCount, fileNames[fileNames.length-1].file)
if(success) {
return results.join('\n')
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@
"babel-runtime": "^6.18.0",
"babel-traverse": "^6.18.0",
"babylon": "^6.13.1",
"colors": "^1.1.2",
"chalk": "^1.1.3",
"commander": "^2.9.0",
"escape-string-regexp": "^1.0.5",
"lodash": "^4.16.6",
"progress": "^1.1.8",
"typescript": "^2.0.6"
},
"devDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion typings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
"globalDependencies": {
"babel-traverse": "registry:dt/babel-traverse#6.7.0+20160423062108",
"babylon": "registry:dt/babylon#6.7.0+20160423062108",
"colors": "registry:dt/colors#0.6.0-1+20160501135139",
"chalk": "registry:dt/chalk#0.4.0+20160317120654",
"commander": "registry:dt/commander#2.3.0+20160317120654",
"escape-string-regexp": "registry:dt/escape-string-regexp#0.0.0+20160316155526",
"lodash": "registry:dt/lodash#4.14.0+20161004174455",
"progress": "registry:dt/progress#1.1.8+20160906145338",
"typescript": "registry:dt/typescript#0.4.0+20160317120654"
},
"globalDevDependencies": {
Expand Down
Loading

0 comments on commit ee115b6

Please sign in to comment.