@lang-tag/cli 0.18.0 → 0.19.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.
@@ -35,7 +35,7 @@ function applyCaseTransform(str, caseType) {
35
35
  }
36
36
  class DictionaryCollector extends namespaceCollector.TranslationsCollector {
37
37
  constructor(options = {
38
- appendNamespaceToPath: false
38
+ appendNamespaceToPath: true
39
39
  }) {
40
40
  super();
41
41
  this.options = options;
@@ -17,7 +17,7 @@ function applyCaseTransform(str, caseType) {
17
17
  }
18
18
  class DictionaryCollector extends TranslationsCollector {
19
19
  constructor(options = {
20
- appendNamespaceToPath: false
20
+ appendNamespaceToPath: true
21
21
  }) {
22
22
  super();
23
23
  this.options = options;
package/index.cjs CHANGED
@@ -102,22 +102,83 @@ class $LT_TagProcessor {
102
102
  const matches = [];
103
103
  let currentIndex = 0;
104
104
  const skipRanges = this.buildSkipRanges(fileContent);
105
- const startPattern = new RegExp(
106
- `${optionalVariableAssignment}${tagName}\\(\\s*\\{`,
105
+ const tagNamePattern = new RegExp(
106
+ `${optionalVariableAssignment}${tagName}`,
107
107
  "g"
108
108
  );
109
109
  while (true) {
110
- startPattern.lastIndex = currentIndex;
111
- const startMatch = startPattern.exec(fileContent);
112
- if (!startMatch) break;
113
- const matchStartIndex = startMatch.index;
114
- const variableName = startMatch[1] || void 0;
110
+ tagNamePattern.lastIndex = currentIndex;
111
+ const tagNameMatch = tagNamePattern.exec(fileContent);
112
+ if (!tagNameMatch) break;
113
+ const tagNameStartIndex = tagNameMatch.index;
114
+ const variableName = tagNameMatch[1] || void 0;
115
+ if (this.isInSkipRange(tagNameStartIndex, skipRanges)) {
116
+ currentIndex = tagNameStartIndex + 1;
117
+ continue;
118
+ }
119
+ let i = tagNameStartIndex + tagNameMatch[0].length;
120
+ let genericType = void 0;
121
+ let matchStartIndex = tagNameStartIndex;
122
+ while (i < fileContent.length && /\s/.test(fileContent[i])) {
123
+ i++;
124
+ }
125
+ if (i < fileContent.length && fileContent[i] === "<") {
126
+ const genericStart = i;
127
+ let angleCount = 1;
128
+ i++;
129
+ while (i < fileContent.length && angleCount > 0) {
130
+ const char = fileContent[i];
131
+ if (char === "<") angleCount++;
132
+ else if (char === ">") angleCount--;
133
+ else if ((char === '"' || char === "'" || char === "`") && !this.isInSkipRange(i, skipRanges)) {
134
+ i++;
135
+ while (i < fileContent.length) {
136
+ if (fileContent[i] === "\\") {
137
+ i += 2;
138
+ continue;
139
+ }
140
+ if (fileContent[i] === char) {
141
+ i++;
142
+ break;
143
+ }
144
+ i++;
145
+ }
146
+ continue;
147
+ }
148
+ i++;
149
+ }
150
+ if (angleCount === 0) {
151
+ genericType = fileContent.substring(genericStart + 1, i - 1).trim();
152
+ matchStartIndex = tagNameStartIndex;
153
+ } else {
154
+ currentIndex = tagNameStartIndex + 1;
155
+ continue;
156
+ }
157
+ } else {
158
+ matchStartIndex = tagNameStartIndex;
159
+ }
160
+ while (i < fileContent.length && /\s/.test(fileContent[i])) {
161
+ i++;
162
+ }
163
+ if (i >= fileContent.length || fileContent[i] !== "(" || i + 1 >= fileContent.length) {
164
+ currentIndex = tagNameStartIndex + 1;
165
+ continue;
166
+ }
167
+ i++;
168
+ while (i < fileContent.length && /\s/.test(fileContent[i])) {
169
+ i++;
170
+ }
171
+ if (i >= fileContent.length || fileContent[i] !== "{") {
172
+ currentIndex = tagNameStartIndex + 1;
173
+ continue;
174
+ }
175
+ const braceStartIndex = i;
115
176
  if (this.isInSkipRange(matchStartIndex, skipRanges)) {
116
177
  currentIndex = matchStartIndex + 1;
117
178
  continue;
118
179
  }
119
180
  let braceCount = 1;
120
- let i = matchStartIndex + startMatch[0].length;
181
+ i++;
121
182
  while (i < fileContent.length && braceCount > 0) {
122
183
  if (fileContent[i] === "{") braceCount++;
123
184
  if (fileContent[i] === "}") braceCount--;
@@ -127,10 +188,7 @@ class $LT_TagProcessor {
127
188
  currentIndex = matchStartIndex + 1;
128
189
  continue;
129
190
  }
130
- let parameter1Text = fileContent.substring(
131
- matchStartIndex + startMatch[0].length - 1,
132
- i
133
- );
191
+ let parameter1Text = fileContent.substring(braceStartIndex, i);
134
192
  let parameter2Text;
135
193
  while (i < fileContent.length && (fileContent[i] === " " || fileContent[i] === "\n" || fileContent[i] === " ")) {
136
194
  i++;
@@ -225,6 +283,7 @@ class $LT_TagProcessor {
225
283
  matches.push({
226
284
  fullMatch,
227
285
  variableName,
286
+ genericType,
228
287
  parameter1Text,
229
288
  parameter2Text,
230
289
  parameterTranslations,
@@ -278,7 +337,8 @@ class $LT_TagProcessor {
278
337
  }
279
338
  const arg1 = this.config.translationArgPosition === 1 ? newTranslationsString : newConfigString;
280
339
  const arg2 = this.config.translationArgPosition === 1 ? newConfigString : newTranslationsString;
281
- let tagFunction = `${this.config.tagName}(${arg1}`;
340
+ const genericTypePart = tag.genericType ? `<${tag.genericType}>` : "";
341
+ let tagFunction = `${this.config.tagName}${genericTypePart}(${arg1}`;
282
342
  if (arg2) tagFunction += `, ${arg2}`;
283
343
  tagFunction += ")";
284
344
  if (tag.variableName)
@@ -1581,9 +1641,11 @@ async function generateImportFiles(config, logger, importManager) {
1581
1641
  const content = renderTemplate$2(templateData);
1582
1642
  await $LT_EnsureDirectoryExists(path.dirname(filePath));
1583
1643
  await promises.writeFile(filePath, content, "utf-8");
1644
+ const encodedFilePath = encodeURI(filePath);
1584
1645
  logger.success('Created tag file: "{file}"', {
1585
1646
  file: importedFile.pathRelativeToImportDir
1586
1647
  });
1648
+ logger.debug(" └── link: file://{path}", { path: encodedFilePath });
1587
1649
  }
1588
1650
  }
1589
1651
  class ImportManager {
@@ -1661,12 +1723,12 @@ async function $LT_ImportLibraries(config, logger) {
1661
1723
  logger.warn("No tags were imported from any library files");
1662
1724
  return;
1663
1725
  }
1726
+ await $LT_EnsureDirectoryExists(config.import.dir);
1664
1727
  await generateImportFiles(config, logger, importManager);
1665
1728
  if (config.import.onImportFinish) config.import.onImportFinish();
1666
1729
  }
1667
1730
  async function $LT_ImportTranslations() {
1668
1731
  const { config, logger } = await $LT_GetCommandEssentials();
1669
- await $LT_EnsureDirectoryExists(config.import.dir);
1670
1732
  logger.info("Importing translations from libraries...");
1671
1733
  await $LT_ImportLibraries(config, logger);
1672
1734
  logger.success("Successfully imported translations from libraries.");
@@ -1722,6 +1784,28 @@ function detectProjectDirectories() {
1722
1784
  }
1723
1785
  return detectedFolders.length > 0 ? detectedFolders.sort() : ["src", "app"];
1724
1786
  }
1787
+ function getDefaultAnswers() {
1788
+ return {
1789
+ projectType: "project",
1790
+ tagName: "lang",
1791
+ collectorType: "namespace",
1792
+ namespaceOptions: {
1793
+ modifyNamespaceOptions: false,
1794
+ defaultNamespace: "common"
1795
+ },
1796
+ localesDirectory: "public/locales",
1797
+ configGeneration: {
1798
+ enabled: true,
1799
+ useAlgorithm: "path-based",
1800
+ keepVariables: true
1801
+ },
1802
+ importLibraries: true,
1803
+ interfereWithCollection: false,
1804
+ includeDirectories: ["src"],
1805
+ baseLanguageCode: "en",
1806
+ addCommentGuides: false
1807
+ };
1808
+ }
1725
1809
  async function askProjectSetupQuestions() {
1726
1810
  const projectType = await select({
1727
1811
  message: "Is this a project or a library?",
@@ -1975,7 +2059,7 @@ async function detectModuleSystem() {
1975
2059
  return "cjs";
1976
2060
  }
1977
2061
  }
1978
- async function $LT_CMD_InitConfig() {
2062
+ async function $LT_CMD_InitConfig(options = {}) {
1979
2063
  const logger = $LT_CreateDefaultLogger();
1980
2064
  if (fs.existsSync(CONFIG_FILE_NAME)) {
1981
2065
  logger.error(
@@ -1987,7 +2071,10 @@ async function $LT_CMD_InitConfig() {
1987
2071
  logger.info("Welcome to Lang Tag CLI Setup!");
1988
2072
  console.log("");
1989
2073
  try {
1990
- const answers = await askProjectSetupQuestions();
2074
+ const answers = options.yes ? getDefaultAnswers() : await askProjectSetupQuestions();
2075
+ if (options.yes) {
2076
+ logger.info("Using default configuration (--yes flag detected)...");
2077
+ }
1991
2078
  const moduleSystem = await detectModuleSystem();
1992
2079
  const configContent = renderConfigTemplate({
1993
2080
  answers,
@@ -2003,13 +2090,13 @@ async function $LT_CMD_InitConfig() {
2003
2090
  configFile: CONFIG_FILE_NAME
2004
2091
  });
2005
2092
  logger.info(
2006
- " 2. Since you have installed all basic libraries (React, TypeScript, etc.)"
2093
+ " 2. Ensure all dependencies are installed (TypeScript, React, etc.),"
2007
2094
  );
2008
2095
  logger.info(
2009
- " and the initialized basic tag is based on what you use in your project,"
2096
+ ' then run "npx lang-tag init-tag" to generate an initial tag'
2010
2097
  );
2011
2098
  logger.info(
2012
- ' we recommend using "npx lang-tag init-tag" to generate an initial version of the tag'
2099
+ " (the tag will be based on what libraries you have in your project)"
2013
2100
  );
2014
2101
  logger.info(
2015
2102
  " 3. Use your tag in the project under the include directories"
@@ -2358,7 +2445,7 @@ function createCli() {
2358
2445
  commander.program.command("watch").alias("w").description(
2359
2446
  "Watch for changes in source files and automatically collect translations"
2360
2447
  ).action($LT_WatchTranslations);
2361
- commander.program.command("init").description("Initialize project with default configuration").action($LT_CMD_InitConfig);
2448
+ commander.program.command("init").description("Initialize project with default configuration").option("-y, --yes", "Skip prompts and use default configuration").action($LT_CMD_InitConfig);
2362
2449
  commander.program.command("init-tag").description("Initialize a new lang-tag function file").option(
2363
2450
  "-n, --name <name>",
2364
2451
  "Name of the tag function (default: from config)"
package/index.js CHANGED
@@ -82,22 +82,83 @@ class $LT_TagProcessor {
82
82
  const matches = [];
83
83
  let currentIndex = 0;
84
84
  const skipRanges = this.buildSkipRanges(fileContent);
85
- const startPattern = new RegExp(
86
- `${optionalVariableAssignment}${tagName}\\(\\s*\\{`,
85
+ const tagNamePattern = new RegExp(
86
+ `${optionalVariableAssignment}${tagName}`,
87
87
  "g"
88
88
  );
89
89
  while (true) {
90
- startPattern.lastIndex = currentIndex;
91
- const startMatch = startPattern.exec(fileContent);
92
- if (!startMatch) break;
93
- const matchStartIndex = startMatch.index;
94
- const variableName = startMatch[1] || void 0;
90
+ tagNamePattern.lastIndex = currentIndex;
91
+ const tagNameMatch = tagNamePattern.exec(fileContent);
92
+ if (!tagNameMatch) break;
93
+ const tagNameStartIndex = tagNameMatch.index;
94
+ const variableName = tagNameMatch[1] || void 0;
95
+ if (this.isInSkipRange(tagNameStartIndex, skipRanges)) {
96
+ currentIndex = tagNameStartIndex + 1;
97
+ continue;
98
+ }
99
+ let i = tagNameStartIndex + tagNameMatch[0].length;
100
+ let genericType = void 0;
101
+ let matchStartIndex = tagNameStartIndex;
102
+ while (i < fileContent.length && /\s/.test(fileContent[i])) {
103
+ i++;
104
+ }
105
+ if (i < fileContent.length && fileContent[i] === "<") {
106
+ const genericStart = i;
107
+ let angleCount = 1;
108
+ i++;
109
+ while (i < fileContent.length && angleCount > 0) {
110
+ const char = fileContent[i];
111
+ if (char === "<") angleCount++;
112
+ else if (char === ">") angleCount--;
113
+ else if ((char === '"' || char === "'" || char === "`") && !this.isInSkipRange(i, skipRanges)) {
114
+ i++;
115
+ while (i < fileContent.length) {
116
+ if (fileContent[i] === "\\") {
117
+ i += 2;
118
+ continue;
119
+ }
120
+ if (fileContent[i] === char) {
121
+ i++;
122
+ break;
123
+ }
124
+ i++;
125
+ }
126
+ continue;
127
+ }
128
+ i++;
129
+ }
130
+ if (angleCount === 0) {
131
+ genericType = fileContent.substring(genericStart + 1, i - 1).trim();
132
+ matchStartIndex = tagNameStartIndex;
133
+ } else {
134
+ currentIndex = tagNameStartIndex + 1;
135
+ continue;
136
+ }
137
+ } else {
138
+ matchStartIndex = tagNameStartIndex;
139
+ }
140
+ while (i < fileContent.length && /\s/.test(fileContent[i])) {
141
+ i++;
142
+ }
143
+ if (i >= fileContent.length || fileContent[i] !== "(" || i + 1 >= fileContent.length) {
144
+ currentIndex = tagNameStartIndex + 1;
145
+ continue;
146
+ }
147
+ i++;
148
+ while (i < fileContent.length && /\s/.test(fileContent[i])) {
149
+ i++;
150
+ }
151
+ if (i >= fileContent.length || fileContent[i] !== "{") {
152
+ currentIndex = tagNameStartIndex + 1;
153
+ continue;
154
+ }
155
+ const braceStartIndex = i;
95
156
  if (this.isInSkipRange(matchStartIndex, skipRanges)) {
96
157
  currentIndex = matchStartIndex + 1;
97
158
  continue;
98
159
  }
99
160
  let braceCount = 1;
100
- let i = matchStartIndex + startMatch[0].length;
161
+ i++;
101
162
  while (i < fileContent.length && braceCount > 0) {
102
163
  if (fileContent[i] === "{") braceCount++;
103
164
  if (fileContent[i] === "}") braceCount--;
@@ -107,10 +168,7 @@ class $LT_TagProcessor {
107
168
  currentIndex = matchStartIndex + 1;
108
169
  continue;
109
170
  }
110
- let parameter1Text = fileContent.substring(
111
- matchStartIndex + startMatch[0].length - 1,
112
- i
113
- );
171
+ let parameter1Text = fileContent.substring(braceStartIndex, i);
114
172
  let parameter2Text;
115
173
  while (i < fileContent.length && (fileContent[i] === " " || fileContent[i] === "\n" || fileContent[i] === " ")) {
116
174
  i++;
@@ -205,6 +263,7 @@ class $LT_TagProcessor {
205
263
  matches.push({
206
264
  fullMatch,
207
265
  variableName,
266
+ genericType,
208
267
  parameter1Text,
209
268
  parameter2Text,
210
269
  parameterTranslations,
@@ -258,7 +317,8 @@ class $LT_TagProcessor {
258
317
  }
259
318
  const arg1 = this.config.translationArgPosition === 1 ? newTranslationsString : newConfigString;
260
319
  const arg2 = this.config.translationArgPosition === 1 ? newConfigString : newTranslationsString;
261
- let tagFunction = `${this.config.tagName}(${arg1}`;
320
+ const genericTypePart = tag.genericType ? `<${tag.genericType}>` : "";
321
+ let tagFunction = `${this.config.tagName}${genericTypePart}(${arg1}`;
262
322
  if (arg2) tagFunction += `, ${arg2}`;
263
323
  tagFunction += ")";
264
324
  if (tag.variableName)
@@ -1561,9 +1621,11 @@ async function generateImportFiles(config, logger, importManager) {
1561
1621
  const content = renderTemplate$2(templateData);
1562
1622
  await $LT_EnsureDirectoryExists(dirname(filePath));
1563
1623
  await writeFile(filePath, content, "utf-8");
1624
+ const encodedFilePath = encodeURI(filePath);
1564
1625
  logger.success('Created tag file: "{file}"', {
1565
1626
  file: importedFile.pathRelativeToImportDir
1566
1627
  });
1628
+ logger.debug(" └── link: file://{path}", { path: encodedFilePath });
1567
1629
  }
1568
1630
  }
1569
1631
  class ImportManager {
@@ -1641,12 +1703,12 @@ async function $LT_ImportLibraries(config, logger) {
1641
1703
  logger.warn("No tags were imported from any library files");
1642
1704
  return;
1643
1705
  }
1706
+ await $LT_EnsureDirectoryExists(config.import.dir);
1644
1707
  await generateImportFiles(config, logger, importManager);
1645
1708
  if (config.import.onImportFinish) config.import.onImportFinish();
1646
1709
  }
1647
1710
  async function $LT_ImportTranslations() {
1648
1711
  const { config, logger } = await $LT_GetCommandEssentials();
1649
- await $LT_EnsureDirectoryExists(config.import.dir);
1650
1712
  logger.info("Importing translations from libraries...");
1651
1713
  await $LT_ImportLibraries(config, logger);
1652
1714
  logger.success("Successfully imported translations from libraries.");
@@ -1702,6 +1764,28 @@ function detectProjectDirectories() {
1702
1764
  }
1703
1765
  return detectedFolders.length > 0 ? detectedFolders.sort() : ["src", "app"];
1704
1766
  }
1767
+ function getDefaultAnswers() {
1768
+ return {
1769
+ projectType: "project",
1770
+ tagName: "lang",
1771
+ collectorType: "namespace",
1772
+ namespaceOptions: {
1773
+ modifyNamespaceOptions: false,
1774
+ defaultNamespace: "common"
1775
+ },
1776
+ localesDirectory: "public/locales",
1777
+ configGeneration: {
1778
+ enabled: true,
1779
+ useAlgorithm: "path-based",
1780
+ keepVariables: true
1781
+ },
1782
+ importLibraries: true,
1783
+ interfereWithCollection: false,
1784
+ includeDirectories: ["src"],
1785
+ baseLanguageCode: "en",
1786
+ addCommentGuides: false
1787
+ };
1788
+ }
1705
1789
  async function askProjectSetupQuestions() {
1706
1790
  const projectType = await select({
1707
1791
  message: "Is this a project or a library?",
@@ -1955,7 +2039,7 @@ async function detectModuleSystem() {
1955
2039
  return "cjs";
1956
2040
  }
1957
2041
  }
1958
- async function $LT_CMD_InitConfig() {
2042
+ async function $LT_CMD_InitConfig(options = {}) {
1959
2043
  const logger = $LT_CreateDefaultLogger();
1960
2044
  if (existsSync(CONFIG_FILE_NAME)) {
1961
2045
  logger.error(
@@ -1967,7 +2051,10 @@ async function $LT_CMD_InitConfig() {
1967
2051
  logger.info("Welcome to Lang Tag CLI Setup!");
1968
2052
  console.log("");
1969
2053
  try {
1970
- const answers = await askProjectSetupQuestions();
2054
+ const answers = options.yes ? getDefaultAnswers() : await askProjectSetupQuestions();
2055
+ if (options.yes) {
2056
+ logger.info("Using default configuration (--yes flag detected)...");
2057
+ }
1971
2058
  const moduleSystem = await detectModuleSystem();
1972
2059
  const configContent = renderConfigTemplate({
1973
2060
  answers,
@@ -1983,13 +2070,13 @@ async function $LT_CMD_InitConfig() {
1983
2070
  configFile: CONFIG_FILE_NAME
1984
2071
  });
1985
2072
  logger.info(
1986
- " 2. Since you have installed all basic libraries (React, TypeScript, etc.)"
2073
+ " 2. Ensure all dependencies are installed (TypeScript, React, etc.),"
1987
2074
  );
1988
2075
  logger.info(
1989
- " and the initialized basic tag is based on what you use in your project,"
2076
+ ' then run "npx lang-tag init-tag" to generate an initial tag'
1990
2077
  );
1991
2078
  logger.info(
1992
- ' we recommend using "npx lang-tag init-tag" to generate an initial version of the tag'
2079
+ " (the tag will be based on what libraries you have in your project)"
1993
2080
  );
1994
2081
  logger.info(
1995
2082
  " 3. Use your tag in the project under the include directories"
@@ -2338,7 +2425,7 @@ function createCli() {
2338
2425
  program.command("watch").alias("w").description(
2339
2426
  "Watch for changes in source files and automatically collect translations"
2340
2427
  ).action($LT_WatchTranslations);
2341
- program.command("init").description("Initialize project with default configuration").action($LT_CMD_InitConfig);
2428
+ program.command("init").description("Initialize project with default configuration").option("-y, --yes", "Skip prompts and use default configuration").action($LT_CMD_InitConfig);
2342
2429
  program.command("init-tag").description("Initialize a new lang-tag function file").option(
2343
2430
  "-n, --name <name>",
2344
2431
  "Name of the tag function (default: from config)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lang-tag/cli",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/type.d.ts CHANGED
@@ -144,6 +144,8 @@ export interface LangTagCLIProcessedTag {
144
144
  parameterTranslations: any;
145
145
  parameterConfig?: any;
146
146
  variableName?: string;
147
+ /** Generic type parameter if present (e.g., "ValidationTranslations" from "lang<ValidationTranslations>(...)") */
148
+ genericType?: string;
147
149
  /** Character index in the whole text where the match starts */
148
150
  index: number;
149
151
  /** Line number (1-based) where the match was found */