(feature) add support for expanding paths on Windows + added more usage examples to cli
This commit is contained in:
parent
7b94c9b9a5
commit
b813ec0063
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,6 +8,8 @@ npm-debug.log*
|
|||||||
|
|
||||||
# Compiled files
|
# Compiled files
|
||||||
dist
|
dist
|
||||||
|
src/**/*.js
|
||||||
|
tests/**/*.js
|
||||||
|
|
||||||
# Extracted strings
|
# Extracted strings
|
||||||
strings.json
|
strings.json
|
||||||
|
6
package-lock.json
generated
6
package-lock.json
generated
@ -107,6 +107,12 @@
|
|||||||
"any-observable": "^0.3.0"
|
"any-observable": "^0.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/braces": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/chai": {
|
"@types/chai": {
|
||||||
"version": "4.2.10",
|
"version": "4.2.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.10.tgz",
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
},
|
},
|
||||||
"config": {},
|
"config": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/braces": "^3.0.0",
|
||||||
"@types/chai": "^4.2.10",
|
"@types/chai": "^4.2.10",
|
||||||
"@types/flat": "^5.0.0",
|
"@types/flat": "^5.0.0",
|
||||||
"@types/glob": "^7.1.1",
|
"@types/glob": "^7.1.1",
|
||||||
@ -68,6 +69,7 @@
|
|||||||
"@types/mocha": "^7.0.2",
|
"@types/mocha": "^7.0.2",
|
||||||
"@types/node": "^12.12.29",
|
"@types/node": "^12.12.29",
|
||||||
"@types/yargs": "^15.0.4",
|
"@types/yargs": "^15.0.4",
|
||||||
|
"braces": "^3.0.2",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"husky": "^4.2.3",
|
"husky": "^4.2.3",
|
||||||
"lint-staged": "^10.0.8",
|
"lint-staged": "^10.0.8",
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import * as fs from 'fs';
|
|
||||||
import * as yargs from 'yargs';
|
import * as yargs from 'yargs';
|
||||||
|
|
||||||
import { ExtractTask } from './tasks/extract.task';
|
import { ExtractTask } from './tasks/extract.task';
|
||||||
@ -14,9 +13,22 @@ import { NullAsDefaultValuePostProcessor } from '../post-processors/null-as-defa
|
|||||||
import { PurgeObsoleteKeysPostProcessor } from '../post-processors/purge-obsolete-keys.post-processor';
|
import { PurgeObsoleteKeysPostProcessor } from '../post-processors/purge-obsolete-keys.post-processor';
|
||||||
import { CompilerInterface } from '../compilers/compiler.interface';
|
import { CompilerInterface } from '../compilers/compiler.interface';
|
||||||
import { CompilerFactory } from '../compilers/compiler.factory';
|
import { CompilerFactory } from '../compilers/compiler.factory';
|
||||||
|
import { normalizePaths } from '../utils/fs-helpers';
|
||||||
import { donateMessage } from '../utils/donate';
|
import { donateMessage } from '../utils/donate';
|
||||||
|
|
||||||
export const cli = yargs
|
// First parsing pass to be able to access pattern argument for use input/output arguments
|
||||||
|
const y = yargs
|
||||||
|
.option('patterns', {
|
||||||
|
alias: 'p',
|
||||||
|
describe: 'Default patterns',
|
||||||
|
type: 'array',
|
||||||
|
default: ['/**/*.html', '/**/*.ts'],
|
||||||
|
hidden: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const parsed = y.parse();
|
||||||
|
|
||||||
|
export const cli = y
|
||||||
.usage('Extract strings from files for translation.\nUsage: $0 [options]')
|
.usage('Extract strings from files for translation.\nUsage: $0 [options]')
|
||||||
.version(require(__dirname + '/../../package.json').version)
|
.version(require(__dirname + '/../../package.json').version)
|
||||||
.alias('version', 'v')
|
.alias('version', 'v')
|
||||||
@ -30,19 +42,9 @@ export const cli = yargs
|
|||||||
normalize: true,
|
normalize: true,
|
||||||
required: true
|
required: true
|
||||||
})
|
})
|
||||||
.check(options => {
|
.coerce('input', (input: string[]) => {
|
||||||
options.input.forEach((dir: string) => {
|
const paths = normalizePaths(input, parsed.patterns);
|
||||||
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
|
return paths;
|
||||||
throw new Error(`The path you supplied was not found: '${dir}'`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.option('patterns', {
|
|
||||||
alias: 'p',
|
|
||||||
describe: 'Extract strings from the following file patterns',
|
|
||||||
type: 'array',
|
|
||||||
default: ['/**/*.html', '/**/*.ts']
|
|
||||||
})
|
})
|
||||||
.option('output', {
|
.option('output', {
|
||||||
alias: 'o',
|
alias: 'o',
|
||||||
@ -51,16 +53,20 @@ export const cli = yargs
|
|||||||
normalize: true,
|
normalize: true,
|
||||||
required: true
|
required: true
|
||||||
})
|
})
|
||||||
|
.coerce('output', (output: string[]) => {
|
||||||
|
const paths = normalizePaths(output, parsed.patterns);
|
||||||
|
return paths;
|
||||||
|
})
|
||||||
.option('format', {
|
.option('format', {
|
||||||
alias: 'f',
|
alias: 'f',
|
||||||
describe: 'Output format',
|
describe: 'Format',
|
||||||
default: 'json',
|
default: 'json',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
choices: ['json', 'namespaced-json', 'pot']
|
choices: ['json', 'namespaced-json', 'pot']
|
||||||
})
|
})
|
||||||
.option('format-indentation', {
|
.option('format-indentation', {
|
||||||
alias: 'fi',
|
alias: 'fi',
|
||||||
describe: 'Output format indentation',
|
describe: 'Format indentation (JSON/Namedspaced JSON)',
|
||||||
default: '\t',
|
default: '\t',
|
||||||
type: 'string'
|
type: 'string'
|
||||||
})
|
})
|
||||||
@ -71,31 +77,39 @@ export const cli = yargs
|
|||||||
})
|
})
|
||||||
.option('sort', {
|
.option('sort', {
|
||||||
alias: 's',
|
alias: 's',
|
||||||
describe: 'Sort strings in alphabetical order when saving',
|
describe: 'Sort strings in alphabetical order',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
})
|
})
|
||||||
.option('clean', {
|
.option('clean', {
|
||||||
alias: 'c',
|
alias: 'c',
|
||||||
describe: 'Remove obsolete strings when merging',
|
describe: 'Remove obsolete strings after merge',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
})
|
})
|
||||||
.option('key-as-default-value', {
|
.option('key-as-default-value', {
|
||||||
alias: 'k',
|
alias: 'k',
|
||||||
describe: 'Use key as default value for translations',
|
describe: 'Use key as default value',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
})
|
})
|
||||||
.option('null-as-default-value', {
|
.option('null-as-default-value', {
|
||||||
alias: 'n',
|
alias: 'n',
|
||||||
describe: 'Use null as default value for translations',
|
describe: 'Use null as default value',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
})
|
})
|
||||||
|
.group(['format', 'format-indentation', 'sort', 'clean'], 'Output')
|
||||||
|
.group(['key-as-default-value', 'null-as-default-value'], 'Default value (defaults to empty string)')
|
||||||
.conflicts('key-as-default-value', 'null-as-default-value')
|
.conflicts('key-as-default-value', 'null-as-default-value')
|
||||||
|
.example(`$0 -i ./src-a/ -i ./src-b/ -o strings.json`, 'Extract (ts, html) from multiple paths')
|
||||||
|
.example(`$0 -i './{src-a,src-b}/' -o strings.json`, 'Extract (ts, html) from multiple paths using brace expansion')
|
||||||
|
.example(`$0 -i ./src/ -o ./i18n/da.json -o ./i18n/en.json`, 'Extract (ts, html) and save to da.json+en.json')
|
||||||
|
.example(`$0 -i ./src/ -o './i18n/{en,da}.json'`, 'Extract (ts, html) and save to da.json+en.json using brace expansion')
|
||||||
|
.example(`$0 -i './src/**/*.{ts,tsx,html}' -o strings.json`, 'Extract from ts, tsx and html')
|
||||||
|
.example(`$0 -i './src/**/!(*.spec).{ts,html}' -o strings.json`, 'Extract from ts, html, excluding files with ".spec" in filename')
|
||||||
|
.wrap(110)
|
||||||
.exitProcess(true)
|
.exitProcess(true)
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
const extractTask = new ExtractTask(cli.input, cli.output, {
|
const extractTask = new ExtractTask(cli.input, cli.output, {
|
||||||
replace: cli.replace,
|
replace: cli.replace
|
||||||
patterns: cli.patterns
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Parsers
|
// Parsers
|
||||||
|
@ -12,13 +12,11 @@ import * as mkdirp from 'mkdirp';
|
|||||||
|
|
||||||
export interface ExtractTaskOptionsInterface {
|
export interface ExtractTaskOptionsInterface {
|
||||||
replace?: boolean;
|
replace?: boolean;
|
||||||
patterns?: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExtractTask implements TaskInterface {
|
export class ExtractTask implements TaskInterface {
|
||||||
protected options: ExtractTaskOptionsInterface = {
|
protected options: ExtractTaskOptionsInterface = {
|
||||||
replace: false,
|
replace: false
|
||||||
patterns: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
protected parsers: ParserInterface[] = [];
|
protected parsers: ParserInterface[] = [];
|
||||||
@ -100,8 +98,8 @@ export class ExtractTask implements TaskInterface {
|
|||||||
*/
|
*/
|
||||||
protected extract(): TranslationCollection {
|
protected extract(): TranslationCollection {
|
||||||
let collection: TranslationCollection = new TranslationCollection();
|
let collection: TranslationCollection = new TranslationCollection();
|
||||||
this.inputs.forEach(dir => {
|
this.inputs.forEach(pattern => {
|
||||||
this.readDir(dir, this.options.patterns).forEach(filePath => {
|
this.getFiles(pattern).forEach(filePath => {
|
||||||
this.out(dim('- %s'), filePath);
|
this.out(dim('- %s'), filePath);
|
||||||
const contents: string = fs.readFileSync(filePath, 'utf-8');
|
const contents: string = fs.readFileSync(filePath, 'utf-8');
|
||||||
this.parsers.forEach(parser => {
|
this.parsers.forEach(parser => {
|
||||||
@ -138,15 +136,12 @@ export class ExtractTask implements TaskInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all files in dir matching patterns
|
* Get all files matching pattern
|
||||||
*/
|
*/
|
||||||
protected readDir(dir: string, patterns: string[]): string[] {
|
protected getFiles(pattern: string): string[] {
|
||||||
return patterns.reduce((results, pattern) => {
|
return glob
|
||||||
return glob
|
.sync(pattern)
|
||||||
.sync(dir + pattern)
|
.filter(filePath => fs.statSync(filePath).isFile());
|
||||||
.filter(filePath => fs.statSync(filePath).isFile())
|
|
||||||
.concat(results);
|
|
||||||
}, []);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected out(...args: any[]): void {
|
protected out(...args: any[]): void {
|
||||||
|
26
src/utils/fs-helpers.ts
Normal file
26
src/utils/fs-helpers.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import * as os from 'os';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as braces from 'braces';
|
||||||
|
|
||||||
|
export function normalizeHomeDir(path: string): string {
|
||||||
|
if (path.substring(0, 1) === '~') {
|
||||||
|
return `${os.homedir()}/${path.substring(1)}`;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function expandPattern(pattern: string): string[] {
|
||||||
|
return braces(pattern, { expand: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizePaths(patterns: string[], defaultPatterns: string[] = []): string[] {
|
||||||
|
return patterns.map(pattern =>
|
||||||
|
expandPattern(pattern).map(path => {
|
||||||
|
path = normalizeHomeDir(path);
|
||||||
|
if (fs.existsSync(path) && fs.statSync(path).isDirectory()) {
|
||||||
|
return defaultPatterns.map(defaultPattern => path + defaultPattern);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}).flat()
|
||||||
|
).flat();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user