From 5badbeb8278aad7930af59ab07c24825e6505b58 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Mon, 9 Dec 2019 12:21:46 +0900 Subject: [PATCH 1/8] :pushpin: upgrade typescript --- package.json | 2 +- tsconfig.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index fea00a4..c275697 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "jest": "^24.8.0", "opener": "^1.5.1", "ts-jest": "^24.0.2", - "typescript": "^3.7.0", + "typescript": "^3.7.3", "typescript-eslint-language-service": "^1.3.0", "vue": "^2.6.10", "vue-template-compiler": "^2.6.10" diff --git a/tsconfig.json b/tsconfig.json index 5109c18..1c85524 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "target": "es2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ diff --git a/yarn.lock b/yarn.lock index bfd9716..77e8ff6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5044,10 +5044,10 @@ typescript-eslint-language-service@^1.3.0: resolved "https://registry.yarnpkg.com/typescript-eslint-language-service/-/typescript-eslint-language-service-1.4.0.tgz#2b4a1cab0dd903c5555ee084f3244524d4c36883" integrity sha512-tukb/z5Koi1wPkTl/BrRYNg2SdKX2YaBeZhG+7mr7Z7js4KYxs1HnmsqWV20QgEwFSYe8JbGlfmZJoM3adiPlA== -typescript@^3.7.0: - version "3.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== +typescript@^3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69" + integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw== uglify-js@^3.1.4: version "3.6.9" From 43a3be0ee89e767f5f71984d9d5be708ef76bdfc Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Tue, 10 Dec 2019 17:55:26 +0900 Subject: [PATCH 2/8] :star: new: add push command --- lib/commands/push.js | 79 +++++++++ lib/utils.js | 8 +- src/commands/push.ts | 151 ++++++++++++++++ test/commands/__mocks__/404-provider.js | 1 + .../__mocks__/@scope/l10n-service-provider.js | 0 .../__mocks__/l10n-service-provider.js | 11 ++ test/commands/push.test.ts | 163 ++++++++++++++++++ .../conf/l10n-service-provider-conf.json | 5 + test/fixtures/locales/en.json | 3 + test/fixtures/locales/ja.json | 4 + test/fixtures/locales/lang-messages.json | 3 + test/fixtures/locales/lang.json | 3 + tsconfig.json | 3 +- types/index.d.ts | 8 + 14 files changed, 439 insertions(+), 3 deletions(-) create mode 100644 lib/commands/push.js create mode 100644 src/commands/push.ts create mode 100644 test/commands/__mocks__/404-provider.js create mode 100644 test/commands/__mocks__/@scope/l10n-service-provider.js create mode 100644 test/commands/__mocks__/l10n-service-provider.js create mode 100644 test/commands/push.test.ts create mode 100644 test/fixtures/conf/l10n-service-provider-conf.json create mode 100644 test/fixtures/locales/en.json create mode 100644 test/fixtures/locales/ja.json create mode 100644 test/fixtures/locales/lang-messages.json create mode 100644 test/fixtures/locales/lang.json diff --git a/lib/commands/push.js b/lib/commands/push.js new file mode 100644 index 0000000..147e1ba --- /dev/null +++ b/lib/commands/push.js @@ -0,0 +1,79 @@ +"use strict"; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils_1 = require("../utils"); +const debug_1 = require("debug"); +const debug = debug_1.debug('vue-i18n-locale-message:commands:infuse'); +exports.command = 'push'; +exports.aliases = 'ph'; +exports.describe = 'push locale messages to localization service'; +exports.builder = (args) => { + return args + .option('target', { + type: 'string', + alias: 't', + describe: 'target path that locale messages file is stored', + demandOption: true + }) + .option('locale', { + type: 'string', + alias: 'l', + describe: `the locale of locale messages file specified with --target, if it's specified single-file` + }) + .option('match', { + type: 'string', + alias: 'm', + describe: `option should be accepted a regex filenames, must be specified together --target if it's directory path of locale messages` + }) + .option('provider', { + type: 'string', + alias: 'p', + describe: 'the target localization service provider', + demandOption: true + }) + .option('providerConf', { + type: 'string', + alias: 'c', + describe: 'the json file configration of localization service provider' + }); +}; +exports.handler = (args) => { + const targetPath = utils_1.resolve(args.target); + const provider = loadProvider(args.provider); + if (provider === null) { + // TODO: should be showd console message + return; + } +}; +function loadProvider(provider) { + let mod = null; + try { + mod = require(require.resolve(provider)); + } + catch (e) { } + return mod; +} +async function loadProviderConf(confPath) { + let conf = null; + try { + conf = await Promise.resolve().then(() => __importStar(require(confPath))); + } + catch (e) { } + return conf; +} +function push() { + return false; +} +exports.default = { + command: exports.command, + aliases: exports.aliases, + describe: exports.describe, + builder: exports.builder, + handler: exports.handler +}; diff --git a/lib/utils.js b/lib/utils.js index df4a962..a8a6450 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -43,10 +43,14 @@ function reflectSFCDescriptor(basePath, components) { filename: target.path, compiler: compiler }); - return Object.assign(Object.assign({}, parsePath(basePath, target.path)), { raw: target.content, customBlocks, + return { + ...parsePath(basePath, target.path), + raw: target.content, + customBlocks, template, script, - styles }); + styles + }; }); } exports.reflectSFCDescriptor = reflectSFCDescriptor; diff --git a/src/commands/push.ts b/src/commands/push.ts new file mode 100644 index 0000000..3ddac00 --- /dev/null +++ b/src/commands/push.ts @@ -0,0 +1,151 @@ +import { Arguments, Argv } from 'yargs' + +import { resolve } from '../utils' +import path from 'path' +import glob from 'glob' +import { ProviderConstructor, LocaleMessages } from '../../types' +import { debug as Debug } from 'debug' +const debug = Debug('vue-i18n-locale-message:commands:push') + +type PushOptions = { + provider: string + conf?: string + target?: string + locale?: string + targetPaths?: string + filenameMatch?: string + dryRun: boolean +} + +export const command = 'push' +export const aliases = 'ph' +export const describe = 'push locale messages to localization service' + +export const builder = (args: Argv): Argv => { + return args + .option('provider', { + type: 'string', + alias: 'p', + describe: 'the target localization service provider', + demandOption: true + }) + .option('conf', { + type: 'string', + alias: 'c', + describe: 'the json file configration of localization service provider' + }) + .option('target', { + type: 'string', + alias: 't', + describe: 'target path that locale messages file is stored, default push with the filename of target path as locale' + }) + .option('locale', { + type: 'string', + alias: 'l', + describe: `option for the locale of locale messages file specified with --target, if it's specified single-file` + }) + .option('targetPaths', { + type: 'string', + alias: 'T', + describe: 'target directory paths that locale messages files is stored, Can also be specified multi paths with comma delimiter' + }) + .option('filenameMatch', { + type: 'string', + alias: 'm', + describe: `option should be accepted a regex filenames, must be specified together --targets if it's directory path of locale messages` + }) + .option('dryRun', { + type: 'boolean', + alias: 'd', + default: false, + describe: `run the push command, but do not apply to locale messages of localization service` + }) +} + +export const handler = (args: Arguments): void => { + const ProviderConstructor = loadProvider(args.provider) + + if (ProviderConstructor === null) { + // TODO: should refactor console message + console.log(`Not found ${args.provider} provider`) + return + } + + let conf + if (args.conf) { + conf = loadProviderConf(resolve(args.conf)) + } + + if (!args.target && !args.targetPaths) { + // TODO: should refactor console message + console.log('You need to specify either --target or --targetPaths') + return + } + + let messages: LocaleMessages = {} + + if (args.target) { + const targetPath = resolve(args.target) + const parsed = path.parse(targetPath) + const locale = args.locale ? args.locale : parsed.name + messages = Object.assign(messages, { [locale]: require(targetPath) }) + } else if (args.targetPaths) { + const filenameMatch = args.filenameMatch + if (!filenameMatch) { + // TODO: should refactor console message + console.log('You need to specify together --filenameMatch') + return + } + const targetPaths = args.targetPaths.split(',').filter(p => p) + targetPaths.forEach(targetPath => { + const globedPaths = glob.sync(targetPath).map(p => resolve(p)) + globedPaths.forEach(fullPath => { + const parsed = path.parse(fullPath) + const re = new RegExp(filenameMatch, 'ig') + const match = re.exec(parsed.base) + debug('regex match', match, fullPath) + if (match && match[1]) { + const locale = match[1] + messages = Object.assign(messages, { [locale]: require(fullPath) }) + } else { + // TODO: should refactor console message + console.log(`${fullPath} is not matched with ${filenameMatch}`) + } + }) + }) + } + + const provider = new ProviderConstructor(conf) + if (provider.push(messages, args.dryRun)) { + // TODO: should refactor console message + console.log('push success') + } else { + // TODO: should refactor console message + console.error('push fail') + } +} + +function loadProvider (provider: string): ProviderConstructor | null { + let mod: ProviderConstructor | null = null + try { + // NOTE: Should we check the interfaces ? + mod = require(require.resolve(provider)) as ProviderConstructor + } catch (e) { } + return mod +} + +function loadProviderConf (confPath: string): JSON | undefined { + let conf + try { + conf = require(confPath) as JSON + } catch (e) { } + return conf +} + +export default { + command, + aliases, + describe, + builder, + handler +} diff --git a/test/commands/__mocks__/404-provider.js b/test/commands/__mocks__/404-provider.js new file mode 100644 index 0000000..4a3df65 --- /dev/null +++ b/test/commands/__mocks__/404-provider.js @@ -0,0 +1 @@ +throw new Error('cannot find module') \ No newline at end of file diff --git a/test/commands/__mocks__/@scope/l10n-service-provider.js b/test/commands/__mocks__/@scope/l10n-service-provider.js new file mode 100644 index 0000000..e69de29 diff --git a/test/commands/__mocks__/l10n-service-provider.js b/test/commands/__mocks__/l10n-service-provider.js new file mode 100644 index 0000000..e756ad7 --- /dev/null +++ b/test/commands/__mocks__/l10n-service-provider.js @@ -0,0 +1,11 @@ +class L10nServiceProvider { + constructor (options) { + this._options = options + } + + push (messages) { + return true + } +} + +module.exports = L10nServiceProvider diff --git a/test/commands/push.test.ts b/test/commands/push.test.ts new file mode 100644 index 0000000..2d6a99d --- /dev/null +++ b/test/commands/push.test.ts @@ -0,0 +1,163 @@ +import * as yargs from 'yargs' + +// ------- +// mocking + +const mockPush = jest.fn() +jest.mock('@scope/l10n-service-provider', () => { + return jest.fn().mockImplementation(() => { + return { push: mockPush } + }) +}) +import L10nServiceProvider from '@scope/l10n-service-provider' + +// ------------------- +// setup/teadown hooks + +let spyLog +let spyError +beforeEach(() => { + spyLog = jest.spyOn(global.console, 'log') + spyError = jest.spyOn(global.console, 'error') +}) + +afterEach(() => { + spyError.mockRestore() + spyLog.mockRestore() + jest.clearAllMocks() +}) + +// ---------- +// test cases + +test('--provider: not found', async () => { + // setup mocks + + // run + const push = await import('../../src/commands/push') + const cmd = yargs.command(push) + await new Promise((resolve, reject) => { + cmd.parse(`push --provider=./404-provider.js`, (err, argv, output) => { + err ? reject(err) : resolve(output) + }) + }) + expect(spyLog).toHaveBeenCalledWith('Not found ./404-provider.js provider') +}) + +test('not specified --target and --targetPaths', async () => { + const push = await import('../../src/commands/push') + const cmd = yargs.command(push) + await new Promise((resolve, reject) => { + cmd.parse(`push --provider=l10n-service-provider`, (err, argv, output) => { + err ? reject(err) : resolve(output) + }) + }) + expect(spyLog).toHaveBeenCalledWith('You need to specify either --target or --targetPaths') +}) + +test('--target option', async () => { + // setup mocks + mockPush.mockImplementation(messages => true) + + // run + const push = await import('../../src/commands/push') + const cmd = yargs.command(push) + await new Promise((resolve, reject) => { + cmd.parse(`push --provider=@scope/l10n-service-provider --target=./test/fixtures/locales/en.json`, (err, argv, output) => { + err ? reject(err) : resolve(output) + }) + }) + + expect(mockPush).toHaveBeenCalledWith({ en: { hello: 'world' }}, false) +}) + +test('--locale option', async () => { + // setup mocks + mockPush.mockImplementation(messages => true) + + // run + const push = await import('../../src/commands/push') + const cmd = yargs.command(push) + await new Promise((resolve, reject) => { + cmd.parse(`push --provider=@scope/l10n-service-provider --target=./test/fixtures/locales/lang.json --locale=ja`, (err, argv, output) => { + err ? reject(err) : resolve(output) + }) + }) + + expect(mockPush).toHaveBeenCalledWith({ ja: { hello: '世界' }}, false) +}) + +test('--conf option', async () => { + // setup mocks + mockPush.mockImplementation(messages => true) + + // run + const push = await import('../../src/commands/push') + const cmd = yargs.command(push) + await new Promise((resolve, reject) => { + cmd.parse(`push --provider=@scope/l10n-service-provider --conf=./test/fixtures/conf/l10n-service-provider-conf.json --target=./test/fixtures/locales/en.json`, (err, argv, output) => { + err ? reject(err) : resolve(output) + }) + }) + + expect(L10nServiceProvider).toHaveBeenCalledWith({ provider: { token: 'xxx' }}) +}) + +test('--targetPaths option', async () => { + // setup mocks + mockPush.mockImplementation(messages => true) + + // run + const push = await import('../../src/commands/push') + const cmd = yargs.command(push) + await new Promise((resolve, reject) => { + cmd.parse(`push --provider=@scope/l10n-service-provider --targetPaths=./test/fixtures/locales/*.json --filenameMatch=^([\\w]*)\\.json`, (err, argv, output) => { + err ? reject(err) : resolve(output) + }) + }) + + expect(mockPush).toHaveBeenCalledWith({ + en: { + hello: 'world' + }, + lang: { + hello: '世界' + }, + ja: { + hello: 'こんにちわわわ!', + world: 'ザ・ワールド' + } + }, false) +}) + +test('not specified --filenameMatch', async () => { + // setup mocks + mockPush.mockImplementation(messages => true) + + // run + const push = await import('../../src/commands/push') + const cmd = yargs.command(push) + await new Promise((resolve, reject) => { + cmd.parse(`push --provider=@scope/l10n-service-provider --targetPaths=./test/fixtures/locales/*.json`, (err, argv, output) => { + err ? reject(err) : resolve(output) + }) + }) + + expect(spyLog).toHaveBeenCalledWith('You need to specify together --filenameMatch') +}) + +test('--dryRun option', async () => { + // setup mocks + mockPush.mockImplementation(messages => false) + + // run + const push = await import('../../src/commands/push') + const cmd = yargs.command(push) + await new Promise((resolve, reject) => { + cmd.parse(`push --provider=@scope/l10n-service-provider --target=./test/fixtures/locales/lang.json --locale=ja --dryRun`, (err, argv, output) => { + err ? reject(err) : resolve(output) + }) + }) + + expect(mockPush).toHaveBeenCalledWith({ ja: { hello: '世界' }}, true) +}) diff --git a/test/fixtures/conf/l10n-service-provider-conf.json b/test/fixtures/conf/l10n-service-provider-conf.json new file mode 100644 index 0000000..294cabd --- /dev/null +++ b/test/fixtures/conf/l10n-service-provider-conf.json @@ -0,0 +1,5 @@ +{ + "provider": { + "token": "xxx" + } +} diff --git a/test/fixtures/locales/en.json b/test/fixtures/locales/en.json new file mode 100644 index 0000000..090fb53 --- /dev/null +++ b/test/fixtures/locales/en.json @@ -0,0 +1,3 @@ +{ + "hello": "world" +} diff --git a/test/fixtures/locales/ja.json b/test/fixtures/locales/ja.json new file mode 100644 index 0000000..d47585f --- /dev/null +++ b/test/fixtures/locales/ja.json @@ -0,0 +1,4 @@ +{ + "hello": "こんにちわわわ!", + "world": "ザ・ワールド" +} diff --git a/test/fixtures/locales/lang-messages.json b/test/fixtures/locales/lang-messages.json new file mode 100644 index 0000000..c571699 --- /dev/null +++ b/test/fixtures/locales/lang-messages.json @@ -0,0 +1,3 @@ +{ + "hello": "世界" +} diff --git a/test/fixtures/locales/lang.json b/test/fixtures/locales/lang.json new file mode 100644 index 0000000..c571699 --- /dev/null +++ b/test/fixtures/locales/lang.json @@ -0,0 +1,3 @@ +{ + "hello": "世界" +} diff --git a/tsconfig.json b/tsconfig.json index 1c85524..ac064e1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -50,9 +50,10 @@ // ], // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + "resolveJsonModule": true /* Source Map Options */ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ diff --git a/types/index.d.ts b/types/index.d.ts index 9e57e4d..6a3a9fc 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -159,6 +159,14 @@ export interface SFCFileInfo { declare function squeeze (basePath: string, files: SFCFileInfo[]): MetaLocaleMessage declare function infuse (basePath: string, sources: SFCFileInfo[], meta: MetaLocaleMessage, options?: FormatOptions): SFCFileInfo[] +export interface Provider { + push (messages: LocaleMessages, dryRun: boolean): boolean +} + +export interface ProviderConstructor { + new (configration?: JSON): Provider +} + // extend for vue-i18n-locale-message declare module 'vue-template-compiler' { interface SFCDescriptor { From 52349f23b93cd5c1148e5a1bce4580234b30b7de Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Tue, 10 Dec 2019 17:57:23 +0900 Subject: [PATCH 3/8] :package: build: generate dist files --- lib/commands/push.js | 119 ++++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 31 deletions(-) diff --git a/lib/commands/push.js b/lib/commands/push.js index 147e1ba..75efaa9 100644 --- a/lib/commands/push.js +++ b/lib/commands/push.js @@ -1,75 +1,132 @@ "use strict"; -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; - result["default"] = mod; - return result; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("../utils"); +const path_1 = __importDefault(require("path")); +const glob_1 = __importDefault(require("glob")); const debug_1 = require("debug"); -const debug = debug_1.debug('vue-i18n-locale-message:commands:infuse'); +const debug = debug_1.debug('vue-i18n-locale-message:commands:push'); exports.command = 'push'; exports.aliases = 'ph'; exports.describe = 'push locale messages to localization service'; exports.builder = (args) => { return args + .option('provider', { + type: 'string', + alias: 'p', + describe: 'the target localization service provider', + demandOption: true + }) + .option('conf', { + type: 'string', + alias: 'c', + describe: 'the json file configration of localization service provider' + }) .option('target', { type: 'string', alias: 't', - describe: 'target path that locale messages file is stored', - demandOption: true + describe: 'target path that locale messages file is stored, default push with the filename of target path as locale' }) .option('locale', { type: 'string', alias: 'l', - describe: `the locale of locale messages file specified with --target, if it's specified single-file` + describe: `option for the locale of locale messages file specified with --target, if it's specified single-file` }) - .option('match', { + .option('targetPaths', { type: 'string', - alias: 'm', - describe: `option should be accepted a regex filenames, must be specified together --target if it's directory path of locale messages` + alias: 'T', + describe: 'target directory paths that locale messages files is stored, Can also be specified multi paths with comma delimiter' }) - .option('provider', { + .option('filenameMatch', { type: 'string', - alias: 'p', - describe: 'the target localization service provider', - demandOption: true + alias: 'm', + describe: `option should be accepted a regex filenames, must be specified together --targets if it's directory path of locale messages` }) - .option('providerConf', { - type: 'string', - alias: 'c', - describe: 'the json file configration of localization service provider' + .option('dryRun', { + type: 'boolean', + alias: 'd', + default: false, + describe: `run the push command, but do not apply to locale messages of localization service` }); }; exports.handler = (args) => { - const targetPath = utils_1.resolve(args.target); - const provider = loadProvider(args.provider); - if (provider === null) { - // TODO: should be showd console message + const ProviderConstructor = loadProvider(args.provider); + if (ProviderConstructor === null) { + // TODO: should refactor console message + console.log(`Not found ${args.provider} provider`); + return; + } + let conf; + if (args.conf) { + conf = loadProviderConf(utils_1.resolve(args.conf)); + } + if (!args.target && !args.targetPaths) { + // TODO: should refactor console message + console.log('You need to specify either --target or --targetPaths'); return; } + let messages = {}; + if (args.target) { + const targetPath = utils_1.resolve(args.target); + const parsed = path_1.default.parse(targetPath); + const locale = args.locale ? args.locale : parsed.name; + messages = Object.assign(messages, { [locale]: require(targetPath) }); + } + else if (args.targetPaths) { + const filenameMatch = args.filenameMatch; + if (!filenameMatch) { + // TODO: should refactor console message + console.log('You need to specify together --filenameMatch'); + return; + } + const targetPaths = args.targetPaths.split(',').filter(p => p); + targetPaths.forEach(targetPath => { + const globedPaths = glob_1.default.sync(targetPath).map(p => utils_1.resolve(p)); + globedPaths.forEach(fullPath => { + const parsed = path_1.default.parse(fullPath); + const re = new RegExp(filenameMatch, 'ig'); + const match = re.exec(parsed.base); + debug('regex match', match, fullPath); + if (match && match[1]) { + const locale = match[1]; + messages = Object.assign(messages, { [locale]: require(fullPath) }); + } + else { + // TODO: should refactor console message + console.log(`${fullPath} is not matched with ${filenameMatch}`); + } + }); + }); + } + const provider = new ProviderConstructor(conf); + if (provider.push(messages, args.dryRun)) { + // TODO: should refactor console message + console.log('push success'); + } + else { + // TODO: should refactor console message + console.error('push fail'); + } }; function loadProvider(provider) { let mod = null; try { + // NOTE: Should we check the interfaces ? mod = require(require.resolve(provider)); } catch (e) { } return mod; } -async function loadProviderConf(confPath) { - let conf = null; +function loadProviderConf(confPath) { + let conf; try { - conf = await Promise.resolve().then(() => __importStar(require(confPath))); + conf = require(confPath); } catch (e) { } return conf; } -function push() { - return false; -} exports.default = { command: exports.command, aliases: exports.aliases, From 9a4449dfa65baa5fb4b5522f8526499778019b8e Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Mon, 16 Dec 2019 14:49:06 +0900 Subject: [PATCH 4/8] :shirt: refactor: change to kebab-case from camel-case --- src/commands/push.ts | 4 ++-- test/commands/push.test.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/commands/push.ts b/src/commands/push.ts index 3ddac00..c885212 100644 --- a/src/commands/push.ts +++ b/src/commands/push.ts @@ -78,7 +78,7 @@ export const handler = (args: Arguments): void => { if (!args.target && !args.targetPaths) { // TODO: should refactor console message - console.log('You need to specify either --target or --targetPaths') + console.log('You need to specify either --target or --target-paths') return } @@ -93,7 +93,7 @@ export const handler = (args: Arguments): void => { const filenameMatch = args.filenameMatch if (!filenameMatch) { // TODO: should refactor console message - console.log('You need to specify together --filenameMatch') + console.log('You need to specify together --filename-match') return } const targetPaths = args.targetPaths.split(',').filter(p => p) diff --git a/test/commands/push.test.ts b/test/commands/push.test.ts index 2d6a99d..20efc17 100644 --- a/test/commands/push.test.ts +++ b/test/commands/push.test.ts @@ -52,7 +52,7 @@ test('not specified --target and --targetPaths', async () => { err ? reject(err) : resolve(output) }) }) - expect(spyLog).toHaveBeenCalledWith('You need to specify either --target or --targetPaths') + expect(spyLog).toHaveBeenCalledWith('You need to specify either --target or --target-paths') }) test('--target option', async () => { @@ -111,7 +111,7 @@ test('--targetPaths option', async () => { const push = await import('../../src/commands/push') const cmd = yargs.command(push) await new Promise((resolve, reject) => { - cmd.parse(`push --provider=@scope/l10n-service-provider --targetPaths=./test/fixtures/locales/*.json --filenameMatch=^([\\w]*)\\.json`, (err, argv, output) => { + cmd.parse(`push --provider=@scope/l10n-service-provider --target-paths=./test/fixtures/locales/*.json --filename-match=^([\\w]*)\\.json`, (err, argv, output) => { err ? reject(err) : resolve(output) }) }) @@ -138,12 +138,12 @@ test('not specified --filenameMatch', async () => { const push = await import('../../src/commands/push') const cmd = yargs.command(push) await new Promise((resolve, reject) => { - cmd.parse(`push --provider=@scope/l10n-service-provider --targetPaths=./test/fixtures/locales/*.json`, (err, argv, output) => { + cmd.parse(`push --provider=@scope/l10n-service-provider --target-paths=./test/fixtures/locales/*.json`, (err, argv, output) => { err ? reject(err) : resolve(output) }) }) - expect(spyLog).toHaveBeenCalledWith('You need to specify together --filenameMatch') + expect(spyLog).toHaveBeenCalledWith('You need to specify together --filename-match') }) test('--dryRun option', async () => { From 38559d74a75ff9152aa8fd1e39e45013d64138fc Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Mon, 16 Dec 2019 14:56:15 +0900 Subject: [PATCH 5/8] :pencil: docs: add push command usage --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dcbe3f6..5db9ac1 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ yarn global vue-i18n-locale-message - CLI - squeeze the locale messages from `i18n` custom block - infuse the locale messages to `i18n` custom block + - push the locale messages to localization service ## :rocket: Usages @@ -101,13 +102,22 @@ vue-i18n-locale-message squeeze --target=./src --output=./messages.json vue-i18n-locale-message infuse --target=./src --locales=./translated.json ``` +#### push + +```sh +vue-i18n-locale-message push --provider=l10n-service-provider \ + --conf=110n-service-provider-conf.json \ + --target-paths=./src/locales/*.json \ + --filename-match=^([\\w]*)\\.json +``` + ## :raising_hand: Motivations The big motivation is as follows. - :tired_face: Hard to integrate locale messages for localization services - :tired_face: Hard to maintain consistency of locale message keys (`eslint-plugin-vue-i18n` need it!) -- :pray: Requested by 3rd vendor tools (`vue-i18n-ally` and etc ...) +- :pray: Requested by 3rd vendor tools (`i18n-ally` and etc ...) ## :book: API: Specifications From 1f2f0573d4d62ff2aa33480aafdedc698a98818f Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Mon, 16 Dec 2019 18:18:57 +0900 Subject: [PATCH 6/8] :up: change push passing resource --- lib/commands/push.js | 9 +- src/commands/push.ts | 108 ++++++++++++------ .../__mocks__/l10n-service-provider.js | 2 +- test/commands/push.test.ts | 76 ++++++++---- .../conf/l10n-service-provider-conf.json | 3 +- types/index.d.ts | 33 +++++- 6 files changed, 167 insertions(+), 64 deletions(-) diff --git a/lib/commands/push.js b/lib/commands/push.js index 75efaa9..e4eb754 100644 --- a/lib/commands/push.js +++ b/lib/commands/push.js @@ -51,7 +51,7 @@ exports.builder = (args) => { describe: `run the push command, but do not apply to locale messages of localization service` }); }; -exports.handler = (args) => { +exports.handler = async (args) => { const ProviderConstructor = loadProvider(args.provider); if (ProviderConstructor === null) { // TODO: should refactor console message @@ -64,7 +64,7 @@ exports.handler = (args) => { } if (!args.target && !args.targetPaths) { // TODO: should refactor console message - console.log('You need to specify either --target or --targetPaths'); + console.log('You need to specify either --target or --target-paths'); return; } let messages = {}; @@ -78,7 +78,7 @@ exports.handler = (args) => { const filenameMatch = args.filenameMatch; if (!filenameMatch) { // TODO: should refactor console message - console.log('You need to specify together --filenameMatch'); + console.log('You need to specify together --filename-match'); return; } const targetPaths = args.targetPaths.split(',').filter(p => p); @@ -101,7 +101,8 @@ exports.handler = (args) => { }); } const provider = new ProviderConstructor(conf); - if (provider.push(messages, args.dryRun)) { + const ret = await provider.push(messages, args.dryRun); + if (ret) { // TODO: should refactor console message console.log('push success'); } diff --git a/src/commands/push.ts b/src/commands/push.ts index c885212..340c15d 100644 --- a/src/commands/push.ts +++ b/src/commands/push.ts @@ -3,10 +3,17 @@ import { Arguments, Argv } from 'yargs' import { resolve } from '../utils' import path from 'path' import glob from 'glob' -import { ProviderConstructor, LocaleMessages } from '../../types' + import { debug as Debug } from 'debug' const debug = Debug('vue-i18n-locale-message:commands:push') +import { + ProviderConstructor, + ProviderConfiguration, + ProviderPushResource, + ProviderPushMode +} from '../../types' + type PushOptions = { provider: string conf?: string @@ -17,6 +24,8 @@ type PushOptions = { dryRun: boolean } +const DEFUALT_CONF = { provider: {}, pushMode: 'locale-message' } as ProviderConfiguration + export const command = 'push' export const aliases = 'ph' export const describe = 'push locale messages to localization service' @@ -62,7 +71,7 @@ export const builder = (args: Argv): Argv => { }) } -export const handler = (args: Arguments): void => { +export const handler = async (args: Arguments): Promise => { const ProviderConstructor = loadProvider(args.provider) if (ProviderConstructor === null) { @@ -71,7 +80,7 @@ export const handler = (args: Arguments): void => { return } - let conf + let conf = DEFUALT_CONF if (args.conf) { conf = loadProviderConf(resolve(args.conf)) } @@ -82,19 +91,69 @@ export const handler = (args: Arguments): void => { return } - let messages: LocaleMessages = {} + let resource + try { + resource = getProviderPushResource(args, conf.pushMode) + } catch (e) { + console.log(e.message) + return + } + + const provider = new ProviderConstructor(conf) + const ret = await provider.push(resource, args.dryRun) + if (ret) { + // TODO: should refactor console message + console.log('push success') + } else { + // TODO: should refactor console message + console.error('push fail') + } +} + +function loadProvider (provider: string): ProviderConstructor | null { + let mod: ProviderConstructor | null = null + try { + // NOTE: Should we check the interfaces ? + mod = require(require.resolve(provider)) as ProviderConstructor + } catch (e) { } + return mod +} + +function loadProviderConf (confPath: string): ProviderConfiguration { + let conf = DEFUALT_CONF + try { + conf = require(confPath) as ProviderConfiguration + } catch (e) { } + return conf +} + +function getProviderPushResource (args: Arguments, mode: ProviderPushMode): ProviderPushResource { + const resource = { mode } as ProviderPushResource + debug(`getProviderPushResource: mode=${mode}`) + + if (mode === 'locale-message') { + resource.messages = {} + } else { // 'raw-file' + resource.files = [] + } if (args.target) { const targetPath = resolve(args.target) const parsed = path.parse(targetPath) const locale = args.locale ? args.locale : parsed.name - messages = Object.assign(messages, { [locale]: require(targetPath) }) + if (mode === 'locale-message') { + resource.messages = Object.assign(resource.messages, { [locale]: require(targetPath) }) + } else { // 'file-path' + resource.files?.push({ + locale, + path: targetPath + }) + } } else if (args.targetPaths) { const filenameMatch = args.filenameMatch if (!filenameMatch) { // TODO: should refactor console message - console.log('You need to specify together --filename-match') - return + throw new Error('You need to specify together --filename-match') } const targetPaths = args.targetPaths.split(',').filter(p => p) targetPaths.forEach(targetPath => { @@ -106,7 +165,14 @@ export const handler = (args: Arguments): void => { debug('regex match', match, fullPath) if (match && match[1]) { const locale = match[1] - messages = Object.assign(messages, { [locale]: require(fullPath) }) + if (mode === 'locale-message') { + resource.messages = Object.assign(resource.messages, { [locale]: require(fullPath) }) + } else { // 'file-path' + resource.files?.push({ + locale, + path: fullPath + }) + } } else { // TODO: should refactor console message console.log(`${fullPath} is not matched with ${filenameMatch}`) @@ -115,31 +181,7 @@ export const handler = (args: Arguments): void => { }) } - const provider = new ProviderConstructor(conf) - if (provider.push(messages, args.dryRun)) { - // TODO: should refactor console message - console.log('push success') - } else { - // TODO: should refactor console message - console.error('push fail') - } -} - -function loadProvider (provider: string): ProviderConstructor | null { - let mod: ProviderConstructor | null = null - try { - // NOTE: Should we check the interfaces ? - mod = require(require.resolve(provider)) as ProviderConstructor - } catch (e) { } - return mod -} - -function loadProviderConf (confPath: string): JSON | undefined { - let conf - try { - conf = require(confPath) as JSON - } catch (e) { } - return conf + return resource } export default { diff --git a/test/commands/__mocks__/l10n-service-provider.js b/test/commands/__mocks__/l10n-service-provider.js index e756ad7..14b2655 100644 --- a/test/commands/__mocks__/l10n-service-provider.js +++ b/test/commands/__mocks__/l10n-service-provider.js @@ -3,7 +3,7 @@ class L10nServiceProvider { this._options = options } - push (messages) { + async push (resource, dryRun) { return true } } diff --git a/test/commands/push.test.ts b/test/commands/push.test.ts index 20efc17..e83d812 100644 --- a/test/commands/push.test.ts +++ b/test/commands/push.test.ts @@ -1,4 +1,5 @@ import * as yargs from 'yargs' +import * as path from 'path' // ------- // mocking @@ -57,7 +58,7 @@ test('not specified --target and --targetPaths', async () => { test('--target option', async () => { // setup mocks - mockPush.mockImplementation(messages => true) + mockPush.mockImplementation(resource => true) // run const push = await import('../../src/commands/push') @@ -68,12 +69,17 @@ test('--target option', async () => { }) }) - expect(mockPush).toHaveBeenCalledWith({ en: { hello: 'world' }}, false) + expect(mockPush).toHaveBeenCalledWith({ + messages: { + en: { hello: 'world' } + }, + mode: 'locale-message' + }, false) }) test('--locale option', async () => { // setup mocks - mockPush.mockImplementation(messages => true) + mockPush.mockImplementation(resource => true) // run const push = await import('../../src/commands/push') @@ -84,28 +90,44 @@ test('--locale option', async () => { }) }) - expect(mockPush).toHaveBeenCalledWith({ ja: { hello: '世界' }}, false) + expect(mockPush).toHaveBeenCalledWith({ + messages: { + ja: { hello: '世界' } + }, + mode: 'locale-message' + }, false) }) test('--conf option', async () => { // setup mocks - mockPush.mockImplementation(messages => true) + mockPush.mockImplementation(reosurce => true) // run + const TARGET_LOCALE = './test/fixtures/locales/en.json' const push = await import('../../src/commands/push') const cmd = yargs.command(push) await new Promise((resolve, reject) => { - cmd.parse(`push --provider=@scope/l10n-service-provider --conf=./test/fixtures/conf/l10n-service-provider-conf.json --target=./test/fixtures/locales/en.json`, (err, argv, output) => { + cmd.parse(`push --provider=@scope/l10n-service-provider --conf=./test/fixtures/conf/l10n-service-provider-conf.json --target=${TARGET_LOCALE}`, (err, argv, output) => { err ? reject(err) : resolve(output) }) }) - expect(L10nServiceProvider).toHaveBeenCalledWith({ provider: { token: 'xxx' }}) + expect(L10nServiceProvider).toHaveBeenCalledWith({ + provider: { token: 'xxx' }, + pushMode: 'file-path' + }) + expect(mockPush).toHaveBeenCalledWith({ + files: [{ + locale: 'en', + path: path.resolve(TARGET_LOCALE) + }], + mode: 'file-path' + }, false) }) -test('--targetPaths option', async () => { +test('--target-paths option', async () => { // setup mocks - mockPush.mockImplementation(messages => true) + mockPush.mockImplementation(resource => true) // run const push = await import('../../src/commands/push') @@ -117,22 +139,25 @@ test('--targetPaths option', async () => { }) expect(mockPush).toHaveBeenCalledWith({ - en: { - hello: 'world' + messages: { + en: { + hello: 'world' + }, + lang: { + hello: '世界' + }, + ja: { + hello: 'こんにちわわわ!', + world: 'ザ・ワールド' + } }, - lang: { - hello: '世界' - }, - ja: { - hello: 'こんにちわわわ!', - world: 'ザ・ワールド' - } + mode: 'locale-message' }, false) }) -test('not specified --filenameMatch', async () => { +test('not specified --filename-match', async () => { // setup mocks - mockPush.mockImplementation(messages => true) + mockPush.mockImplementation(resource => true) // run const push = await import('../../src/commands/push') @@ -146,9 +171,9 @@ test('not specified --filenameMatch', async () => { expect(spyLog).toHaveBeenCalledWith('You need to specify together --filename-match') }) -test('--dryRun option', async () => { +test('--dry-run option', async () => { // setup mocks - mockPush.mockImplementation(messages => false) + mockPush.mockImplementation(resource => false) // run const push = await import('../../src/commands/push') @@ -159,5 +184,10 @@ test('--dryRun option', async () => { }) }) - expect(mockPush).toHaveBeenCalledWith({ ja: { hello: '世界' }}, true) + expect(mockPush).toHaveBeenCalledWith({ + messages: { + ja: { hello: '世界' } + }, + mode: 'locale-message' + }, true) }) diff --git a/test/fixtures/conf/l10n-service-provider-conf.json b/test/fixtures/conf/l10n-service-provider-conf.json index 294cabd..84579ef 100644 --- a/test/fixtures/conf/l10n-service-provider-conf.json +++ b/test/fixtures/conf/l10n-service-provider-conf.json @@ -1,5 +1,6 @@ { "provider": { "token": "xxx" - } + }, + "pushMode": "file-path" } diff --git a/types/index.d.ts b/types/index.d.ts index 6a3a9fc..4ef7237 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -159,12 +159,41 @@ export interface SFCFileInfo { declare function squeeze (basePath: string, files: SFCFileInfo[]): MetaLocaleMessage declare function infuse (basePath: string, sources: SFCFileInfo[], meta: MetaLocaleMessage, options?: FormatOptions): SFCFileInfo[] +export type ProviderPushMode = 'file-path' | 'locale-message' + +export type ProviderPushFileInfo = { + locale: Locale + path: string +} + +export type ProviderPushResource = { + mode: ProviderPushMode + files?: ProviderPushFileInfo[] + messages?: LocaleMessages +} + export interface Provider { - push (messages: LocaleMessages, dryRun: boolean): boolean + push (resource: ProviderPushResource, dryRun: boolean): Promise } export interface ProviderConstructor { - new (configration?: JSON): Provider + new (configration?: ProviderConfiguration): Provider +} + +/** + * ProviderConfiguration provider fields structure + * e.g. + * { + * "provider": { + * "token": "xxx" + * }, + * "pushMode": "file-path" + * } + */ + +export type ProviderConfiguration = { + provider: any // TODO: should be reoslve types + pushMode: ProviderPushMode } // extend for vue-i18n-locale-message From 75951dca5a9fa2850981e33ded7602f67bd53110 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Tue, 17 Dec 2019 18:55:49 +0900 Subject: [PATCH 7/8] :up: update: tweak provider interface --- lib/commands/push.js | 114 ++++++++++++------ src/commands/push.ts | 33 +++-- .../__mocks__/l10n-service-provider.js | 2 +- test/commands/push.test.ts | 12 ++ types/index.d.ts | 12 +- 5 files changed, 118 insertions(+), 55 deletions(-) diff --git a/lib/commands/push.js b/lib/commands/push.js index e4eb754..f7e786a 100644 --- a/lib/commands/push.js +++ b/lib/commands/push.js @@ -8,6 +8,7 @@ const path_1 = __importDefault(require("path")); const glob_1 = __importDefault(require("glob")); const debug_1 = require("debug"); const debug = debug_1.debug('vue-i18n-locale-message:commands:push'); +const DEFUALT_CONF = { provider: {}, pushMode: 'locale-message' }; exports.command = 'push'; exports.aliases = 'ph'; exports.describe = 'push locale messages to localization service'; @@ -52,13 +53,13 @@ exports.builder = (args) => { }); }; exports.handler = async (args) => { - const ProviderConstructor = loadProvider(args.provider); - if (ProviderConstructor === null) { + const ProviderFactory = loadProvider(args.provider); + if (ProviderFactory === null) { // TODO: should refactor console message console.log(`Not found ${args.provider} provider`); return; } - let conf; + let conf = DEFUALT_CONF; if (args.conf) { conf = loadProviderConf(utils_1.resolve(args.conf)); } @@ -67,31 +68,100 @@ exports.handler = async (args) => { console.log('You need to specify either --target or --target-paths'); return; } - let messages = {}; + let resource; + try { + resource = getProviderPushResource(args, conf.pushMode); + } + catch (e) { + console.log(e.message); + return; + } + const provider = ProviderFactory(conf); + const ret = await provider.push(resource, args.dryRun); + if (ret) { + // TODO: should refactor console message + console.log('push success'); + } + else { + // TODO: should refactor console message + console.error('push fail'); + } +}; +function loadProvider(provider) { + let mod = null; + try { + // TODO: should validate I/F checking & dynamic importing + const m = require(require.resolve(provider)); + debug('loaderProvider', m); + if ('__esModule' in m) { + mod = m.default; + } + else { + mod = m; + } + } + catch (e) { } + return mod; +} +function loadProviderConf(confPath) { + let conf = DEFUALT_CONF; + try { + // TODO: should validate I/F checking & dynamic importing + conf = require(confPath); + } + catch (e) { } + return conf; +} +function getProviderPushResource(args, mode) { + var _a; + const resource = { mode }; + debug(`getProviderPushResource: mode=${mode}`); + if (mode === 'locale-message') { + resource.messages = {}; + } + else { // 'file-path' + resource.files = []; + } if (args.target) { const targetPath = utils_1.resolve(args.target); const parsed = path_1.default.parse(targetPath); const locale = args.locale ? args.locale : parsed.name; - messages = Object.assign(messages, { [locale]: require(targetPath) }); + if (mode === 'locale-message') { + resource.messages = Object.assign(resource.messages, { [locale]: require(targetPath) }); + } + else { // 'file-path' + (_a = resource.files) === null || _a === void 0 ? void 0 : _a.push({ + locale, + path: targetPath + }); + } } else if (args.targetPaths) { const filenameMatch = args.filenameMatch; if (!filenameMatch) { // TODO: should refactor console message - console.log('You need to specify together --filename-match'); - return; + throw new Error('You need to specify together --filename-match'); } const targetPaths = args.targetPaths.split(',').filter(p => p); targetPaths.forEach(targetPath => { const globedPaths = glob_1.default.sync(targetPath).map(p => utils_1.resolve(p)); globedPaths.forEach(fullPath => { + var _a; const parsed = path_1.default.parse(fullPath); const re = new RegExp(filenameMatch, 'ig'); const match = re.exec(parsed.base); debug('regex match', match, fullPath); if (match && match[1]) { const locale = match[1]; - messages = Object.assign(messages, { [locale]: require(fullPath) }); + if (mode === 'locale-message') { + resource.messages = Object.assign(resource.messages, { [locale]: require(fullPath) }); + } + else { // 'file-path' + (_a = resource.files) === null || _a === void 0 ? void 0 : _a.push({ + locale, + path: fullPath + }); + } } else { // TODO: should refactor console message @@ -100,33 +170,7 @@ exports.handler = async (args) => { }); }); } - const provider = new ProviderConstructor(conf); - const ret = await provider.push(messages, args.dryRun); - if (ret) { - // TODO: should refactor console message - console.log('push success'); - } - else { - // TODO: should refactor console message - console.error('push fail'); - } -}; -function loadProvider(provider) { - let mod = null; - try { - // NOTE: Should we check the interfaces ? - mod = require(require.resolve(provider)); - } - catch (e) { } - return mod; -} -function loadProviderConf(confPath) { - let conf; - try { - conf = require(confPath); - } - catch (e) { } - return conf; + return resource; } exports.default = { command: exports.command, diff --git a/src/commands/push.ts b/src/commands/push.ts index 340c15d..7b48388 100644 --- a/src/commands/push.ts +++ b/src/commands/push.ts @@ -8,7 +8,7 @@ import { debug as Debug } from 'debug' const debug = Debug('vue-i18n-locale-message:commands:push') import { - ProviderConstructor, + ProviderFactory, ProviderConfiguration, ProviderPushResource, ProviderPushMode @@ -72,9 +72,9 @@ export const builder = (args: Argv): Argv => { } export const handler = async (args: Arguments): Promise => { - const ProviderConstructor = loadProvider(args.provider) + const ProviderFactory = loadProvider(args.provider) - if (ProviderConstructor === null) { + if (ProviderFactory === null) { // TODO: should refactor console message console.log(`Not found ${args.provider} provider`) return @@ -99,22 +99,28 @@ export const handler = async (args: Arguments): Promise => return } - const provider = new ProviderConstructor(conf) - const ret = await provider.push(resource, args.dryRun) - if (ret) { + try { + const provider = ProviderFactory(conf) + await provider.push(resource, args.dryRun) // TODO: should refactor console message console.log('push success') - } else { + } catch (e) { // TODO: should refactor console message - console.error('push fail') + console.error('push fail', e) } } -function loadProvider (provider: string): ProviderConstructor | null { - let mod: ProviderConstructor | null = null +function loadProvider (provider: string): ProviderFactory | null { + let mod: ProviderFactory | null = null try { - // NOTE: Should we check the interfaces ? - mod = require(require.resolve(provider)) as ProviderConstructor + // TODO: should validate I/F checking & dynamic importing + const m = require(require.resolve(provider)) + debug('loaderProvider', m) + if ('__esModule' in m) { + mod = m.default as ProviderFactory + } else { + mod = m as ProviderFactory + } } catch (e) { } return mod } @@ -122,6 +128,7 @@ function loadProvider (provider: string): ProviderConstructor | null { function loadProviderConf (confPath: string): ProviderConfiguration { let conf = DEFUALT_CONF try { + // TODO: should validate I/F checking & dynamic importing conf = require(confPath) as ProviderConfiguration } catch (e) { } return conf @@ -133,7 +140,7 @@ function getProviderPushResource (args: Arguments, mode: ProviderPu if (mode === 'locale-message') { resource.messages = {} - } else { // 'raw-file' + } else { // 'file-path' resource.files = [] } diff --git a/test/commands/__mocks__/l10n-service-provider.js b/test/commands/__mocks__/l10n-service-provider.js index 14b2655..aaf22a0 100644 --- a/test/commands/__mocks__/l10n-service-provider.js +++ b/test/commands/__mocks__/l10n-service-provider.js @@ -4,7 +4,7 @@ class L10nServiceProvider { } async push (resource, dryRun) { - return true + return } } diff --git a/test/commands/push.test.ts b/test/commands/push.test.ts index e83d812..da10338 100644 --- a/test/commands/push.test.ts +++ b/test/commands/push.test.ts @@ -1,6 +1,9 @@ import * as yargs from 'yargs' import * as path from 'path' +import { debug as Debug } from 'debug' +const debug = Debug('vue-i18n-locale-message:test') + // ------- // mocking @@ -12,6 +15,15 @@ jest.mock('@scope/l10n-service-provider', () => { }) import L10nServiceProvider from '@scope/l10n-service-provider' +jest.mock('@scope/poeditor-service-provider', () => { + const mod = jest.requireActual('../../src/poeditor-service-provider') + debug('modmod', mod) + return mod + // const ret = { __esModule: true, ...mod } + // return ret +}) +import PoeditorServiceProvider from '@scope/poeditor-service-provider' + // ------------------- // setup/teadown hooks diff --git a/types/index.d.ts b/types/index.d.ts index 4ef7237..011b3aa 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -173,12 +173,10 @@ export type ProviderPushResource = { } export interface Provider { - push (resource: ProviderPushResource, dryRun: boolean): Promise + push (resource: ProviderPushResource, dryRun: boolean): Promise } -export interface ProviderConstructor { - new (configration?: ProviderConfiguration): Provider -} +export type ProviderFactory = (configration: ProviderConfiguration) => Provider /** * ProviderConfiguration provider fields structure @@ -191,11 +189,13 @@ export interface ProviderConstructor { * } */ -export type ProviderConfiguration = { - provider: any // TODO: should be reoslve types +export interface ProviderConfiguration { + provider: { [key in keyof ProviderConfigurationValue]: ProviderConfigurationValue[key] } pushMode: ProviderPushMode } +export type ProviderConfigurationValue = T & { [prop: string]: unknown } + // extend for vue-i18n-locale-message declare module 'vue-template-compiler' { interface SFCDescriptor { From e51cb60571b53d06cdbdcc577179da49ec7326fd Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Tue, 17 Dec 2019 19:04:06 +0900 Subject: [PATCH 8/8] :up: update: fix ci --- test/commands/push.test.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/commands/push.test.ts b/test/commands/push.test.ts index da10338..e83d812 100644 --- a/test/commands/push.test.ts +++ b/test/commands/push.test.ts @@ -1,9 +1,6 @@ import * as yargs from 'yargs' import * as path from 'path' -import { debug as Debug } from 'debug' -const debug = Debug('vue-i18n-locale-message:test') - // ------- // mocking @@ -15,15 +12,6 @@ jest.mock('@scope/l10n-service-provider', () => { }) import L10nServiceProvider from '@scope/l10n-service-provider' -jest.mock('@scope/poeditor-service-provider', () => { - const mod = jest.requireActual('../../src/poeditor-service-provider') - debug('modmod', mod) - return mod - // const ret = { __esModule: true, ...mod } - // return ret -}) -import PoeditorServiceProvider from '@scope/poeditor-service-provider' - // ------------------- // setup/teadown hooks