@factorialco/fcode-cli 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -2
- package/dist/commands/add.js +3 -1
- package/dist/commands/clone.d.ts +1 -0
- package/dist/commands/clone.js +5 -0
- package/dist/commands/i18n/add.d.ts +9 -0
- package/dist/commands/i18n/add.js +13 -0
- package/dist/commands/i18n/pull.d.ts +12 -0
- package/dist/commands/i18n/pull.js +14 -0
- package/dist/commands/i18n/push.d.ts +12 -0
- package/dist/commands/i18n/push.js +14 -0
- package/dist/commands/i18n/remove.d.ts +9 -0
- package/dist/commands/i18n/remove.js +19 -0
- package/dist/commands/i18n/reset.d.ts +9 -0
- package/dist/commands/i18n/reset.js +13 -0
- package/dist/commands/i18n/status.d.ts +11 -0
- package/dist/commands/i18n/status.js +42 -0
- package/dist/commands/pull.js +5 -3
- package/dist/commands/push.js +5 -3
- package/dist/commands/reset.js +3 -1
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +5 -2
- package/dist/commands/status.js +4 -2
- package/dist/commands/team/push.js +3 -0
- package/dist/config/remote.js +1 -1
- package/dist/config/workspace.js +10 -0
- package/dist/core/commands/i18n-command.d.ts +5 -0
- package/dist/core/commands/i18n-command.js +16 -0
- package/dist/core/errors.d.ts +6 -0
- package/dist/core/errors.js +19 -1
- package/dist/core/executors/execution-task.js +4 -0
- package/dist/core/executors/javascript-execution-task.js +26 -1
- package/dist/core/resource-pusher.js +15 -0
- package/dist/core/types.d.ts +29 -1
- package/dist/core/types.js +43 -2
- package/dist/repositories/locale.d.ts +24 -0
- package/dist/repositories/locale.js +102 -0
- package/dist/repositories/resource.js +4 -1
- package/dist/repositories/team.js +5 -2
- package/dist/runtime/libs/python/fcode/__init__.py +1 -0
- package/dist/runtime/libs/python/fcode/context.py +43 -1
- package/dist/services/fcode-graphql-client.d.ts +11 -1
- package/dist/services/fcode-graphql-client.js +111 -0
- package/dist/services/locale-merger.d.ts +23 -0
- package/dist/services/locale-merger.js +111 -0
- package/dist/services/locale-resolver.d.ts +18 -0
- package/dist/services/locale-resolver.js +120 -0
- package/dist/services/process-runner.d.ts +1 -0
- package/dist/services/process-runner.js +2 -2
- package/dist/utils/gitignore.d.ts +1 -1
- package/dist/utils/gitignore.js +4 -1
- package/oclif.manifest.json +194 -1
- package/package.json +2 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Locale } from '../core/types';
|
|
2
|
+
/**
|
|
3
|
+
* Collapses the locale files a workspace inherits into one file per identifier.
|
|
4
|
+
*
|
|
5
|
+
* The cloud lists a locale once per workspace that defines it, and every one of them is live: an
|
|
6
|
+
* override is layered over its parent key by key, so a locale several workspaces define is several
|
|
7
|
+
* files. A workspace directory keeps one inherited file per identifier instead — one file cannot
|
|
8
|
+
* hold two names — so the parents' files are merged here, in the order the cloud resolves them.
|
|
9
|
+
*
|
|
10
|
+
* A single parent's file is kept verbatim, comments and nesting included. Only a locale that
|
|
11
|
+
* several parents define is rewritten, as a flat mapping of dotted keys, which is the shape the
|
|
12
|
+
* lookup uses anyway and the one shape where a nested and a dotted key cannot disagree.
|
|
13
|
+
*/
|
|
14
|
+
export declare function mergeInheritedLocales(locales: Array<Locale>): Array<Locale>;
|
|
15
|
+
/**
|
|
16
|
+
* Locale identifiers are case-sensitive on the platform (pt-BR and pt-br are different locales),
|
|
17
|
+
* but the filesystems macOS and Windows usually run on are not — both identifiers would collapse
|
|
18
|
+
* into one file, so a pull would write one locale over the other, `status` would report a phantom
|
|
19
|
+
* change forever, and the next push would silently overwrite one locale's cloud content with the
|
|
20
|
+
* other's. Refused outright before anything is written; on a case-sensitive filesystem the files
|
|
21
|
+
* live side by side and nothing is refused.
|
|
22
|
+
*/
|
|
23
|
+
export declare function assertLocaleFilesCanCoexist(locales: Array<Locale>, filesystemKeepsCase?: boolean): void;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeInheritedLocales = mergeInheritedLocales;
|
|
4
|
+
exports.assertLocaleFilesCanCoexist = assertLocaleFilesCanCoexist;
|
|
5
|
+
const fs = require("fs-extra");
|
|
6
|
+
const os = require("node:os");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const yaml_1 = require("yaml");
|
|
9
|
+
const color_1 = require("@oclif/color");
|
|
10
|
+
const cli_1 = require("../utils/cli");
|
|
11
|
+
const types_1 = require("../core/types");
|
|
12
|
+
const locale_resolver_1 = require("./locale-resolver");
|
|
13
|
+
/**
|
|
14
|
+
* Collapses the locale files a workspace inherits into one file per identifier.
|
|
15
|
+
*
|
|
16
|
+
* The cloud lists a locale once per workspace that defines it, and every one of them is live: an
|
|
17
|
+
* override is layered over its parent key by key, so a locale several workspaces define is several
|
|
18
|
+
* files. A workspace directory keeps one inherited file per identifier instead — one file cannot
|
|
19
|
+
* hold two names — so the parents' files are merged here, in the order the cloud resolves them.
|
|
20
|
+
*
|
|
21
|
+
* A single parent's file is kept verbatim, comments and nesting included. Only a locale that
|
|
22
|
+
* several parents define is rewritten, as a flat mapping of dotted keys, which is the shape the
|
|
23
|
+
* lookup uses anyway and the one shape where a nested and a dotted key cannot disagree.
|
|
24
|
+
*/
|
|
25
|
+
function mergeInheritedLocales(locales) {
|
|
26
|
+
const inheritedByIdentifier = new Map();
|
|
27
|
+
for (const locale of locales.filter((locale) => locale.resolvingTeamSlug)) {
|
|
28
|
+
inheritedByIdentifier.set(locale.locale, [...(inheritedByIdentifier.get(locale.locale) ?? []), locale]);
|
|
29
|
+
}
|
|
30
|
+
return [
|
|
31
|
+
...locales.filter((locale) => !locale.resolvingTeamSlug),
|
|
32
|
+
...[...inheritedByIdentifier.values()].map((files) => (files.length === 1 ? files[0] : mergeLocaleFiles(files))),
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The platform walks parents sorted by their slug — `Team.parentTeamIdsInResolutionOrder` — and
|
|
37
|
+
* the first defining a key wins. Deriving that order here, rather than trusting the order the API
|
|
38
|
+
* happened to list the rows in (which follows the listing's `orderBy`, `updatedAt` by default),
|
|
39
|
+
* is what keeps a local run and the cloud resolving the same text.
|
|
40
|
+
*/
|
|
41
|
+
function inResolutionOrder(files) {
|
|
42
|
+
return [...files].sort((a, b) => {
|
|
43
|
+
const [aSlug, bSlug] = [a.resolvingTeamSlug ?? '', b.resolvingTeamSlug ?? ''];
|
|
44
|
+
return aSlug < bSlug ? -1 : (aSlug > bSlug ? 1 : 0);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @param files the same locale as every parent workspace defines it, in any order
|
|
49
|
+
* @returns one locale carrying their merged translations, attributed to the winning parent
|
|
50
|
+
*/
|
|
51
|
+
function mergeLocaleFiles(files) {
|
|
52
|
+
const ordered = inResolutionOrder(files);
|
|
53
|
+
const [winner] = ordered;
|
|
54
|
+
const translations = {};
|
|
55
|
+
// Lowest precedence first, so the first parent in resolution order is the text that survives —
|
|
56
|
+
// the same layering the platform applies.
|
|
57
|
+
for (const file of [...ordered].reverse()) {
|
|
58
|
+
Object.assign(translations, translationsOf(file));
|
|
59
|
+
}
|
|
60
|
+
return new types_1.Locale(winner.id, winner.locale, `${mergeHeader(ordered)}${(0, yaml_1.stringify)(translations)}`, winner.resolvingTeamSlug);
|
|
61
|
+
}
|
|
62
|
+
function translationsOf(file) {
|
|
63
|
+
try {
|
|
64
|
+
return (0, locale_resolver_1.flattenTranslations)((0, yaml_1.parse)(file.content));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
// Skipped rather than fatal: a workspace cannot fix a parent's file, and the cloud ignores an
|
|
68
|
+
// unparseable one too.
|
|
69
|
+
cli_1.default.log(color_1.default.yellow(`\n⚠️ locale '${file.locale}' from workspace '${file.resolvingTeamSlug}' could not be parsed and will be ignored: ${error.message}`));
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Locale identifiers are case-sensitive on the platform (pt-BR and pt-br are different locales),
|
|
75
|
+
* but the filesystems macOS and Windows usually run on are not — both identifiers would collapse
|
|
76
|
+
* into one file, so a pull would write one locale over the other, `status` would report a phantom
|
|
77
|
+
* change forever, and the next push would silently overwrite one locale's cloud content with the
|
|
78
|
+
* other's. Refused outright before anything is written; on a case-sensitive filesystem the files
|
|
79
|
+
* live side by side and nothing is refused.
|
|
80
|
+
*/
|
|
81
|
+
function assertLocaleFilesCanCoexist(locales, filesystemKeepsCase = filesystemKeepsCaseApart()) {
|
|
82
|
+
const slugsByFoldedSlug = new Map();
|
|
83
|
+
for (const locale of locales) {
|
|
84
|
+
const slug = locale.slug ?? locale.locale;
|
|
85
|
+
const folded = slug.toLowerCase();
|
|
86
|
+
slugsByFoldedSlug.set(folded, [...(slugsByFoldedSlug.get(folded) ?? []), slug]);
|
|
87
|
+
}
|
|
88
|
+
const collisions = [...slugsByFoldedSlug.values()].filter((slugs) => slugs.length > 1);
|
|
89
|
+
if (collisions.length === 0 || filesystemKeepsCase) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const described = collisions.map((slugs) => `'${slugs.join("' / '")}'`).join(', ');
|
|
93
|
+
throw new Error(`locales differing only in case cannot both be stored on this filesystem: ${described}. ` +
|
|
94
|
+
'Rename or remove one of them in the cloud and pull again.');
|
|
95
|
+
}
|
|
96
|
+
function filesystemKeepsCaseApart() {
|
|
97
|
+
const probe = fs.mkdtempSync(path.join(os.tmpdir(), 'fcode-case-'));
|
|
98
|
+
try {
|
|
99
|
+
fs.outputFileSync(path.join(probe, 'a'), '');
|
|
100
|
+
return !fs.existsSync(path.join(probe, 'A'));
|
|
101
|
+
}
|
|
102
|
+
finally {
|
|
103
|
+
fs.removeSync(probe);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function mergeHeader(files) {
|
|
107
|
+
const workspaces = files.map((file) => file.resolvingTeamSlug).join(', ');
|
|
108
|
+
return (`# Generated by fcode: the '${files[0].locale}' translations inherited from ${workspaces},\n` +
|
|
109
|
+
`# merged in the platform's resolution order (the first wins where they disagree).\n` +
|
|
110
|
+
`# Edit i18n/${files[0].locale}.yaml to override any of these keys.\n`);
|
|
111
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the translations a local run resolves `fcode.i18n` against, so `fcode run` behaves like
|
|
3
|
+
* the cloud does.
|
|
4
|
+
*
|
|
5
|
+
* The cloud does this work in the engine: it merges the workspace's locale over its parents', then
|
|
6
|
+
* layers the chosen locale over the primary one and ships a single flat map. Locally there is no
|
|
7
|
+
* engine, so the same layering happens here over the files in `i18n/` — including the inherited
|
|
8
|
+
* ones, which `pull` writes into the same directory as `<locale>.inherited.yaml`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function flattenTranslations(document: unknown, prefix?: string, target?: Record<string, string>): Record<string, string>;
|
|
11
|
+
/**
|
|
12
|
+
* @param requestedLocale the locale to run with, or undefined to use the workspace's primary
|
|
13
|
+
* @returns the locale actually used and its flat translations, with the primary locale underneath
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveLocalTranslations(requestedLocale?: string, workspaceDir?: string): {
|
|
16
|
+
locale?: string;
|
|
17
|
+
translations: Record<string, string>;
|
|
18
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.flattenTranslations = flattenTranslations;
|
|
4
|
+
exports.resolveLocalTranslations = resolveLocalTranslations;
|
|
5
|
+
const fs = require("fs-extra");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
const yaml_1 = require("yaml");
|
|
8
|
+
const cli_1 = require("../utils/cli");
|
|
9
|
+
const color_1 = require("@oclif/color");
|
|
10
|
+
const types_1 = require("../core/types");
|
|
11
|
+
const I18N_DIRECTORY = 'i18n';
|
|
12
|
+
const YAML_EXTENSION = '.yaml';
|
|
13
|
+
const KEY_SEPARATOR = '.';
|
|
14
|
+
/**
|
|
15
|
+
* Builds the translations a local run resolves `fcode.i18n` against, so `fcode run` behaves like
|
|
16
|
+
* the cloud does.
|
|
17
|
+
*
|
|
18
|
+
* The cloud does this work in the engine: it merges the workspace's locale over its parents', then
|
|
19
|
+
* layers the chosen locale over the primary one and ships a single flat map. Locally there is no
|
|
20
|
+
* engine, so the same layering happens here over the files in `i18n/` — including the inherited
|
|
21
|
+
* ones, which `pull` writes into the same directory as `<locale>.inherited.yaml`.
|
|
22
|
+
*/
|
|
23
|
+
function flattenTranslations(document, prefix = '', target = {}) {
|
|
24
|
+
if (document === null || document === undefined || typeof document !== 'object' || Array.isArray(document)) {
|
|
25
|
+
return target;
|
|
26
|
+
}
|
|
27
|
+
for (const [key, value] of Object.entries(document)) {
|
|
28
|
+
const keyPath = prefix ? `${prefix}${KEY_SEPARATOR}${key}` : key;
|
|
29
|
+
if (value === null || value === undefined) {
|
|
30
|
+
// A key written without a value is a placeholder: it counts as not translated, so lookups
|
|
31
|
+
// fall through to the fallback locale instead of resolving to an empty string.
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
35
|
+
flattenTranslations(value, keyPath, target);
|
|
36
|
+
}
|
|
37
|
+
else if (!Array.isArray(value)) {
|
|
38
|
+
target[keyPath] = String(value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return target;
|
|
42
|
+
}
|
|
43
|
+
function readLocaleFile(workspaceDir, slug) {
|
|
44
|
+
const filePath = path.join(workspaceDir, I18N_DIRECTORY, `${slug}${YAML_EXTENSION}`);
|
|
45
|
+
if (!fs.pathExistsSync(filePath)) {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return flattenTranslations((0, yaml_1.parse)(fs.readFileSync(filePath).toString()));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
// A broken translation file must never stop a run, exactly as in the cloud.
|
|
53
|
+
cli_1.default.log(color_1.default.yellow(`\n⚠️ locale '${slug}' could not be parsed and will be ignored: ${error.message}`));
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** The identifier a locale file contributes keys to, whether the workspace owns the file or not. */
|
|
58
|
+
function identifierOf(slug) {
|
|
59
|
+
return slug.endsWith(types_1.Locale.INHERITED_SUFFIX) ? slug.slice(0, -types_1.Locale.INHERITED_SUFFIX.length) : slug;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* One locale as this workspace resolves it: what it inherits, with its own file layered on top key
|
|
63
|
+
* by key, so a key it does not translate itself still resolves to the parent's text. `pull` has
|
|
64
|
+
* already merged the parents' files into the inherited one.
|
|
65
|
+
*/
|
|
66
|
+
function readLocale(workspaceDir, locale) {
|
|
67
|
+
return {
|
|
68
|
+
...readLocaleFile(workspaceDir, `${locale}${types_1.Locale.INHERITED_SUFFIX}`),
|
|
69
|
+
...readLocaleFile(workspaceDir, locale),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function chosenPrimaryLocaleOf(workspaceDir) {
|
|
73
|
+
try {
|
|
74
|
+
const team = fs.readJsonSync(path.join(workspaceDir, 'team.json'));
|
|
75
|
+
return typeof team?.primaryLocale === 'string' && team.primaryLocale.length > 0 ? team.primaryLocale : undefined;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Every locale file in the workspace, own and inherited: `pull` writes them all to `i18n/`. */
|
|
82
|
+
function localeFileSlugs(workspaceDir) {
|
|
83
|
+
const directory = path.join(workspaceDir, I18N_DIRECTORY);
|
|
84
|
+
if (!fs.pathExistsSync(directory)) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
return fs
|
|
88
|
+
.readdirSync(directory)
|
|
89
|
+
.filter((fileName) => fileName.endsWith(YAML_EXTENSION))
|
|
90
|
+
.map((fileName) => path.basename(fileName, YAML_EXTENSION))
|
|
91
|
+
.sort();
|
|
92
|
+
}
|
|
93
|
+
/** Every locale the workspace resolves, counting a locale defined in several workspaces once. */
|
|
94
|
+
function availableLocales(workspaceDir) {
|
|
95
|
+
return [...new Set(localeFileSlugs(workspaceDir).map((slug) => identifierOf(slug)))].sort();
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The workspace's primary locale: the one chosen in `team.json`, or its first locale when none is
|
|
99
|
+
* chosen — or when the chosen one has no file anymore, which would otherwise resolve zero keys.
|
|
100
|
+
* Mirrors the platform's `PrimaryLocale.resolve`, so a local run and the cloud agree on which
|
|
101
|
+
* locale a process without an explicit one resolves against.
|
|
102
|
+
*/
|
|
103
|
+
function primaryLocaleOf(workspaceDir) {
|
|
104
|
+
const available = availableLocales(workspaceDir);
|
|
105
|
+
const chosen = chosenPrimaryLocaleOf(workspaceDir);
|
|
106
|
+
return chosen && available.includes(chosen) ? chosen : available[0];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* @param requestedLocale the locale to run with, or undefined to use the workspace's primary
|
|
110
|
+
* @returns the locale actually used and its flat translations, with the primary locale underneath
|
|
111
|
+
*/
|
|
112
|
+
function resolveLocalTranslations(requestedLocale, workspaceDir = process.cwd()) {
|
|
113
|
+
const primary = primaryLocaleOf(workspaceDir);
|
|
114
|
+
const locale = requestedLocale || primary;
|
|
115
|
+
if (!locale) {
|
|
116
|
+
return { locale: undefined, translations: {} };
|
|
117
|
+
}
|
|
118
|
+
const fallback = primary && primary !== locale ? readLocale(workspaceDir, primary) : {};
|
|
119
|
+
return { locale, translations: { ...fallback, ...readLocale(workspaceDir, locale) } };
|
|
120
|
+
}
|
|
@@ -34,7 +34,7 @@ function mergeVariables(base, ...overrides) {
|
|
|
34
34
|
return [...map.values()];
|
|
35
35
|
}
|
|
36
36
|
async function runProcess(options) {
|
|
37
|
-
const { slug, version, logLevel = 'DEBUG', request, jsonLogs = false, suppressElapsedLog = false } = options;
|
|
37
|
+
const { slug, version, logLevel = 'DEBUG', request, jsonLogs = false, suppressElapsedLog = false, locale } = options;
|
|
38
38
|
const processRepository = new process_1.default();
|
|
39
39
|
const variableRepository = new variable_1.default();
|
|
40
40
|
const fcodeProcess = await processRepository.find(slug, version);
|
|
@@ -50,7 +50,7 @@ async function runProcess(options) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
const ExecutionTask = fcodeProcess.script.programmingLanguage === 'PYTHON' ? python_execution_task_1.default : javascript_execution_task_1.default;
|
|
53
|
-
const executor = new ExecutionTask(fcodeProcess.script.programmingLanguage, fcodeProcess, { version, parameters, variables, logLevel, request, jsonLogs });
|
|
53
|
+
const executor = new ExecutionTask(fcodeProcess.script.programmingLanguage, fcodeProcess, { version, parameters, variables, logLevel, request, jsonLogs, locale });
|
|
54
54
|
const start = Date.now();
|
|
55
55
|
try {
|
|
56
56
|
await executor.prepare();
|
|
@@ -14,6 +14,6 @@ export declare function renderGitignoreWithInherited(existing: string, inherited
|
|
|
14
14
|
* parent team and pulled read-only) are not tracked by git. Regenerated from the
|
|
15
15
|
* current workspace metadata, so resources that stop being inherited drop out.
|
|
16
16
|
* Inherited variable values live in one file, which is ignored as soon as the
|
|
17
|
-
* workspace inherits any variable.
|
|
17
|
+
* workspace inherits any variable; inherited locales are one file each, like modules.
|
|
18
18
|
*/
|
|
19
19
|
export declare function syncInheritedGitignore(workspaceDir?: string): Promise<void>;
|
package/dist/utils/gitignore.js
CHANGED
|
@@ -46,7 +46,7 @@ function stripManagedBlock(content) {
|
|
|
46
46
|
* parent team and pulled read-only) are not tracked by git. Regenerated from the
|
|
47
47
|
* current workspace metadata, so resources that stop being inherited drop out.
|
|
48
48
|
* Inherited variable values live in one file, which is ignored as soon as the
|
|
49
|
-
* workspace inherits any variable.
|
|
49
|
+
* workspace inherits any variable; inherited locales are one file each, like modules.
|
|
50
50
|
*/
|
|
51
51
|
async function syncInheritedGitignore(workspaceDir = process.cwd()) {
|
|
52
52
|
const team = await new workspace_1.default(workspaceDir).getActiveTeam();
|
|
@@ -59,6 +59,9 @@ async function syncInheritedGitignore(workspaceDir = process.cwd()) {
|
|
|
59
59
|
.filter((module) => module.resolvingTeamSlug)
|
|
60
60
|
.map((module) => `modules/${(0, filenamify_1.default)(module.name)}/`),
|
|
61
61
|
...(Object.values(team.variables ?? {}).some((variable) => variable.resolvingTeamSlug) ? [inheritedVariablesFileName] : []),
|
|
62
|
+
...Object.values(team.i18n ?? {})
|
|
63
|
+
.filter((locale) => locale.resolvingTeamSlug)
|
|
64
|
+
.map((locale) => `i18n/${(0, filenamify_1.default)(locale.slug)}.yaml`),
|
|
62
65
|
];
|
|
63
66
|
const gitignorePath = path.join(workspaceDir, '.gitignore');
|
|
64
67
|
const existing = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf8') : '';
|
package/oclif.manifest.json
CHANGED
|
@@ -506,6 +506,13 @@
|
|
|
506
506
|
"allowNo": false,
|
|
507
507
|
"type": "boolean"
|
|
508
508
|
},
|
|
509
|
+
"locale": {
|
|
510
|
+
"description": "Locale fcode.i18n resolves against (defaults to the workspace primaryLocale)",
|
|
511
|
+
"name": "locale",
|
|
512
|
+
"hasDynamicHelp": false,
|
|
513
|
+
"multiple": false,
|
|
514
|
+
"type": "option"
|
|
515
|
+
},
|
|
509
516
|
"variablesFile": {
|
|
510
517
|
"char": "e",
|
|
511
518
|
"description": "Path to a .env file whose variables override workspace variables",
|
|
@@ -876,6 +883,192 @@
|
|
|
876
883
|
"status.js"
|
|
877
884
|
]
|
|
878
885
|
},
|
|
886
|
+
"i18n:add": {
|
|
887
|
+
"aliases": [],
|
|
888
|
+
"args": {
|
|
889
|
+
"resourceSlug": {
|
|
890
|
+
"description": "Resource slug",
|
|
891
|
+
"name": "resourceSlug"
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
"description": "Keeps track of new local locales",
|
|
895
|
+
"examples": [
|
|
896
|
+
"$ fcode i18n:add",
|
|
897
|
+
"$ fcode i18n:add pt-BR"
|
|
898
|
+
],
|
|
899
|
+
"flags": {},
|
|
900
|
+
"hasDynamicHelp": false,
|
|
901
|
+
"hiddenAliases": [],
|
|
902
|
+
"id": "i18n:add",
|
|
903
|
+
"pluginAlias": "@factorialco/fcode-cli",
|
|
904
|
+
"pluginName": "@factorialco/fcode-cli",
|
|
905
|
+
"pluginType": "core",
|
|
906
|
+
"strict": true,
|
|
907
|
+
"isESM": false,
|
|
908
|
+
"relativePath": [
|
|
909
|
+
"dist",
|
|
910
|
+
"commands",
|
|
911
|
+
"i18n",
|
|
912
|
+
"add.js"
|
|
913
|
+
]
|
|
914
|
+
},
|
|
915
|
+
"i18n:pull": {
|
|
916
|
+
"aliases": [],
|
|
917
|
+
"args": {
|
|
918
|
+
"resourceSlug": {
|
|
919
|
+
"description": "Resource slug",
|
|
920
|
+
"name": "resourceSlug"
|
|
921
|
+
}
|
|
922
|
+
},
|
|
923
|
+
"description": "Fetch all locales from cloud and save them locally",
|
|
924
|
+
"examples": [
|
|
925
|
+
"$ fcode i18n:pull"
|
|
926
|
+
],
|
|
927
|
+
"flags": {
|
|
928
|
+
"force": {
|
|
929
|
+
"char": "f",
|
|
930
|
+
"description": "Force command",
|
|
931
|
+
"name": "force",
|
|
932
|
+
"allowNo": false,
|
|
933
|
+
"type": "boolean"
|
|
934
|
+
}
|
|
935
|
+
},
|
|
936
|
+
"hasDynamicHelp": false,
|
|
937
|
+
"hiddenAliases": [],
|
|
938
|
+
"id": "i18n:pull",
|
|
939
|
+
"pluginAlias": "@factorialco/fcode-cli",
|
|
940
|
+
"pluginName": "@factorialco/fcode-cli",
|
|
941
|
+
"pluginType": "core",
|
|
942
|
+
"strict": true,
|
|
943
|
+
"isESM": false,
|
|
944
|
+
"relativePath": [
|
|
945
|
+
"dist",
|
|
946
|
+
"commands",
|
|
947
|
+
"i18n",
|
|
948
|
+
"pull.js"
|
|
949
|
+
]
|
|
950
|
+
},
|
|
951
|
+
"i18n:push": {
|
|
952
|
+
"aliases": [],
|
|
953
|
+
"args": {
|
|
954
|
+
"resourceSlug": {
|
|
955
|
+
"description": "Resource slug",
|
|
956
|
+
"name": "resourceSlug"
|
|
957
|
+
}
|
|
958
|
+
},
|
|
959
|
+
"description": "Create or update cloud locales from local ones",
|
|
960
|
+
"examples": [
|
|
961
|
+
"$ fcode i18n:push",
|
|
962
|
+
"$ fcode i18n:push pt-BR"
|
|
963
|
+
],
|
|
964
|
+
"flags": {
|
|
965
|
+
"force": {
|
|
966
|
+
"char": "f",
|
|
967
|
+
"description": "Force command",
|
|
968
|
+
"name": "force",
|
|
969
|
+
"allowNo": false,
|
|
970
|
+
"type": "boolean"
|
|
971
|
+
}
|
|
972
|
+
},
|
|
973
|
+
"hasDynamicHelp": false,
|
|
974
|
+
"hiddenAliases": [],
|
|
975
|
+
"id": "i18n:push",
|
|
976
|
+
"pluginAlias": "@factorialco/fcode-cli",
|
|
977
|
+
"pluginName": "@factorialco/fcode-cli",
|
|
978
|
+
"pluginType": "core",
|
|
979
|
+
"strict": true,
|
|
980
|
+
"isESM": false,
|
|
981
|
+
"relativePath": [
|
|
982
|
+
"dist",
|
|
983
|
+
"commands",
|
|
984
|
+
"i18n",
|
|
985
|
+
"push.js"
|
|
986
|
+
]
|
|
987
|
+
},
|
|
988
|
+
"i18n:remove": {
|
|
989
|
+
"aliases": [],
|
|
990
|
+
"args": {
|
|
991
|
+
"resourceSlug": {
|
|
992
|
+
"description": "Resource slug",
|
|
993
|
+
"name": "resourceSlug"
|
|
994
|
+
}
|
|
995
|
+
},
|
|
996
|
+
"description": "Remove track of a local locale",
|
|
997
|
+
"examples": [
|
|
998
|
+
"$ fcode i18n:remove pt-BR"
|
|
999
|
+
],
|
|
1000
|
+
"flags": {},
|
|
1001
|
+
"hasDynamicHelp": false,
|
|
1002
|
+
"hiddenAliases": [],
|
|
1003
|
+
"id": "i18n:remove",
|
|
1004
|
+
"pluginAlias": "@factorialco/fcode-cli",
|
|
1005
|
+
"pluginName": "@factorialco/fcode-cli",
|
|
1006
|
+
"pluginType": "core",
|
|
1007
|
+
"strict": true,
|
|
1008
|
+
"isESM": false,
|
|
1009
|
+
"relativePath": [
|
|
1010
|
+
"dist",
|
|
1011
|
+
"commands",
|
|
1012
|
+
"i18n",
|
|
1013
|
+
"remove.js"
|
|
1014
|
+
]
|
|
1015
|
+
},
|
|
1016
|
+
"i18n:reset": {
|
|
1017
|
+
"aliases": [],
|
|
1018
|
+
"args": {
|
|
1019
|
+
"resourceSlug": {
|
|
1020
|
+
"description": "Resource slug",
|
|
1021
|
+
"name": "resourceSlug"
|
|
1022
|
+
}
|
|
1023
|
+
},
|
|
1024
|
+
"description": "Reset locales with contents from current remote",
|
|
1025
|
+
"examples": [
|
|
1026
|
+
"$ fcode i18n:reset"
|
|
1027
|
+
],
|
|
1028
|
+
"flags": {},
|
|
1029
|
+
"hasDynamicHelp": false,
|
|
1030
|
+
"hiddenAliases": [],
|
|
1031
|
+
"id": "i18n:reset",
|
|
1032
|
+
"pluginAlias": "@factorialco/fcode-cli",
|
|
1033
|
+
"pluginName": "@factorialco/fcode-cli",
|
|
1034
|
+
"pluginType": "core",
|
|
1035
|
+
"strict": true,
|
|
1036
|
+
"isESM": false,
|
|
1037
|
+
"relativePath": [
|
|
1038
|
+
"dist",
|
|
1039
|
+
"commands",
|
|
1040
|
+
"i18n",
|
|
1041
|
+
"reset.js"
|
|
1042
|
+
]
|
|
1043
|
+
},
|
|
1044
|
+
"i18n:status": {
|
|
1045
|
+
"aliases": [],
|
|
1046
|
+
"args": {
|
|
1047
|
+
"resourceSlug": {
|
|
1048
|
+
"description": "Resource slug",
|
|
1049
|
+
"name": "resourceSlug"
|
|
1050
|
+
}
|
|
1051
|
+
},
|
|
1052
|
+
"description": "List all locales status",
|
|
1053
|
+
"examples": [
|
|
1054
|
+
"$ fcode i18n:status"
|
|
1055
|
+
],
|
|
1056
|
+
"flags": {},
|
|
1057
|
+
"hasDynamicHelp": false,
|
|
1058
|
+
"hiddenAliases": [],
|
|
1059
|
+
"id": "i18n:status",
|
|
1060
|
+
"pluginAlias": "@factorialco/fcode-cli",
|
|
1061
|
+
"pluginName": "@factorialco/fcode-cli",
|
|
1062
|
+
"pluginType": "core",
|
|
1063
|
+
"strict": true,
|
|
1064
|
+
"isESM": false,
|
|
1065
|
+
"relativePath": [
|
|
1066
|
+
"dist",
|
|
1067
|
+
"commands",
|
|
1068
|
+
"i18n",
|
|
1069
|
+
"status.js"
|
|
1070
|
+
]
|
|
1071
|
+
},
|
|
879
1072
|
"modules:add": {
|
|
880
1073
|
"aliases": [],
|
|
881
1074
|
"args": {
|
|
@@ -1970,5 +2163,5 @@
|
|
|
1970
2163
|
]
|
|
1971
2164
|
}
|
|
1972
2165
|
},
|
|
1973
|
-
"version": "2.
|
|
2166
|
+
"version": "2.2.0"
|
|
1974
2167
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@factorialco/fcode-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"packageManager": "pnpm@11.7.0",
|
|
5
5
|
"description": "Factorial Code Command Line Interface",
|
|
6
6
|
"homepage": "https://code.factorialhr.com/docs/cli/",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"openid-client": "^5.6.5",
|
|
42
42
|
"string-width": "^4.2.3",
|
|
43
43
|
"strip-comments": "^2.0.1",
|
|
44
|
+
"yaml": "^2.8.1",
|
|
44
45
|
"zod": "^4.4.3"
|
|
45
46
|
},
|
|
46
47
|
"peerDependencies": {
|