@lang-tag/cli 0.10.1 → 0.11.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.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Predefined algorithms for onConfigGeneration hook.
3
+ *
4
+ * These algorithms customize how translation tag configurations are generated
5
+ * during collection and regeneration.
6
+ */
7
+ export { pathBasedConfigGenerator, type PathBasedConfigGeneratorOptions } from './path-based-config-generator.ts';
@@ -0,0 +1,110 @@
1
+ import { LangTagCLIConfigGenerationEvent } from '../../config.ts';
2
+ export interface PathBasedConfigGeneratorOptions {
3
+ /**
4
+ * Whether to include the filename (without extension) as part of the path segments.
5
+ * @default false
6
+ */
7
+ includeFileName?: boolean;
8
+ /**
9
+ * Whether to completely remove folders wrapped in brackets () or [].
10
+ * If false, only the brackets are removed from folder names.
11
+ * @default true
12
+ *
13
+ * @example
14
+ * - true: 'app/(admin)/users' -> 'app/users'
15
+ * - false: 'app/(admin)/users' -> 'app/admin/users'
16
+ */
17
+ removeBracketedFolders?: boolean;
18
+ /**
19
+ * List of folder names to completely ignore globally.
20
+ * These will be removed from all paths regardless of their position.
21
+ * @default []
22
+ *
23
+ * @example ['src', 'app', 'components']
24
+ */
25
+ ignoreFolders?: string[];
26
+ /**
27
+ * When true, automatically extracts root folder names from the config.includes patterns
28
+ * and adds them to the ignoreFolders list.
29
+ *
30
+ * @default false
31
+ *
32
+ * @example
33
+ * // With includes: ['src/**\/*.{js,ts,jsx,tsx}']
34
+ * // Automatically ignores: ['src']
35
+ *
36
+ * // With includes: ['(src|app)/**\/*.{js,ts,jsx,tsx}', 'components/**\/*.{jsx,tsx}']
37
+ * // Automatically ignores: ['src', 'app', 'components']
38
+ */
39
+ ignoreIncludesRootFolders?: boolean;
40
+ /**
41
+ * Hierarchical structure for ignoring specific folder patterns.
42
+ * Keys represent path segments to match, values indicate what to ignore at that level.
43
+ *
44
+ * @example
45
+ * {
46
+ * 'src': {
47
+ * 'app': true, // ignore 'app' when under 'src'
48
+ * 'features': ['auth', 'admin'] // ignore 'auth' and 'admin' under 'src/features'
49
+ * }
50
+ * }
51
+ */
52
+ ignoreStructured?: Record<string, any>;
53
+ /**
54
+ * Convert the final namespace to lowercase.
55
+ * @default false
56
+ */
57
+ lowercaseNamespace?: boolean;
58
+ /**
59
+ * Case transformation to apply to the namespace.
60
+ * Available options: 'camel', 'capital', 'constant', 'dot', 'header', 'kebab',
61
+ * 'lower', 'no', 'param', 'pascal', 'path', 'sentence', 'snake', 'swap', 'title', 'upper'
62
+ * @default undefined (no transformation)
63
+ */
64
+ namespaceCase?: 'camel' | 'capital' | 'constant' | 'dot' | 'header' | 'kebab' | 'lower' | 'no' | 'param' | 'pascal' | 'path' | 'sentence' | 'snake' | 'swap' | 'title' | 'upper';
65
+ /**
66
+ * Case transformation to apply to the path segments.
67
+ * Available options: 'camel', 'capital', 'constant', 'dot', 'header', 'kebab',
68
+ * 'lower', 'no', 'param', 'pascal', 'path', 'sentence', 'snake', 'swap', 'title', 'upper'
69
+ * @default undefined (no transformation)
70
+ */
71
+ pathCase?: 'camel' | 'capital' | 'constant' | 'dot' | 'header' | 'kebab' | 'lower' | 'no' | 'param' | 'pascal' | 'path' | 'sentence' | 'snake' | 'swap' | 'title' | 'upper';
72
+ /**
73
+ * Fallback namespace to use when no segments remain after filtering.
74
+ * Defaults to the defaultNamespace from langTagConfig.collect.defaultNamespace if not provided.
75
+ * @default undefined
76
+ */
77
+ fallbackNamespace?: string;
78
+ /**
79
+ * When true and the generated namespace equals the fallback/default namespace,
80
+ * the namespace will be omitted from the configuration as it's redundant.
81
+ * @default true
82
+ */
83
+ clearOnDefaultNamespace?: boolean;
84
+ }
85
+ /**
86
+ * Automatically generates namespace and path configuration based on file path structure.
87
+ *
88
+ * This algorithm analyzes the relative file path and intelligently extracts namespace
89
+ * and path segments according to configurable rules.
90
+ *
91
+ * @param options - Configuration options for path-based generation
92
+ * @returns A function compatible with LangTagCLIConfig.onConfigGeneration
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * import { pathBasedConfigGenerator } from '@lang-tag/cli/algorithms';
97
+ *
98
+ * export default {
99
+ * onConfigGeneration: pathBasedConfigGenerator({
100
+ * includeFileName: false,
101
+ * removeBracketedFolders: true,
102
+ * ignoreFolders: ['lib', 'utils'],
103
+ * ignoreIncludesRootFolders: true, // Auto-ignores root folders from includes
104
+ * lowercaseNamespace: true,
105
+ * fallbackNamespace: 'common'
106
+ * })
107
+ * };
108
+ * ```
109
+ */
110
+ export declare function pathBasedConfigGenerator(options?: PathBasedConfigGeneratorOptions): (event: LangTagCLIConfigGenerationEvent) => Promise<void>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Predefined algorithms for onImport hook.
3
+ *
4
+ * These algorithms customize how library translations are imported
5
+ * and organized in your project.
6
+ */
7
+ export {};
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const path = require("pathe");
4
+ const caseLib = require("case");
5
+ function _interopNamespaceDefault(e) {
6
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
7
+ if (e) {
8
+ for (const k in e) {
9
+ if (k !== "default") {
10
+ const d = Object.getOwnPropertyDescriptor(e, k);
11
+ Object.defineProperty(n, k, d.get ? d : {
12
+ enumerable: true,
13
+ get: () => e[k]
14
+ });
15
+ }
16
+ }
17
+ }
18
+ n.default = e;
19
+ return Object.freeze(n);
20
+ }
21
+ const caseLib__namespace = /* @__PURE__ */ _interopNamespaceDefault(caseLib);
22
+ function pathBasedConfigGenerator(options = {}) {
23
+ const {
24
+ includeFileName = false,
25
+ removeBracketedFolders = true,
26
+ ignoreFolders = [],
27
+ ignoreIncludesRootFolders = false,
28
+ ignoreStructured = {},
29
+ lowercaseNamespace = false,
30
+ namespaceCase,
31
+ pathCase,
32
+ fallbackNamespace,
33
+ clearOnDefaultNamespace = true
34
+ } = options;
35
+ return async (event) => {
36
+ const { relativePath, langTagConfig } = event;
37
+ const actualFallbackNamespace = fallbackNamespace ?? langTagConfig.collect?.defaultNamespace;
38
+ let finalIgnoreFolders = [...ignoreFolders];
39
+ if (ignoreIncludesRootFolders && langTagConfig.includes) {
40
+ const extractedFolders = extractRootFoldersFromIncludes(langTagConfig.includes);
41
+ finalIgnoreFolders = [.../* @__PURE__ */ new Set([...finalIgnoreFolders, ...extractedFolders])];
42
+ }
43
+ let pathSegments = relativePath.split(path.sep).filter(Boolean);
44
+ if (pathSegments.length === 0) {
45
+ return;
46
+ }
47
+ const fileName = pathSegments[pathSegments.length - 1];
48
+ const fileNameWithoutExt = fileName.replace(/\.[^.]+$/, "");
49
+ if (includeFileName) {
50
+ pathSegments[pathSegments.length - 1] = fileNameWithoutExt;
51
+ } else {
52
+ pathSegments = pathSegments.slice(0, -1);
53
+ }
54
+ pathSegments = pathSegments.map((segment) => {
55
+ const bracketMatch = segment.match(/^[\(\[](.+)[\)\]]$/);
56
+ if (bracketMatch) {
57
+ return removeBracketedFolders ? null : bracketMatch[1];
58
+ }
59
+ return segment;
60
+ }).filter((seg) => seg !== null);
61
+ pathSegments = applyStructuredIgnore(pathSegments, ignoreStructured);
62
+ pathSegments = pathSegments.filter((seg) => !finalIgnoreFolders.includes(seg));
63
+ let namespace;
64
+ let path$1;
65
+ if (pathSegments.length >= 1) {
66
+ namespace = pathSegments[0];
67
+ if (pathSegments.length > 1) {
68
+ path$1 = pathSegments.slice(1).join(".");
69
+ }
70
+ } else {
71
+ namespace = actualFallbackNamespace;
72
+ }
73
+ if (namespace) {
74
+ if (lowercaseNamespace) {
75
+ namespace = namespace.toLowerCase();
76
+ }
77
+ if (namespaceCase) {
78
+ namespace = applyCaseTransform(namespace, namespaceCase);
79
+ }
80
+ }
81
+ if (path$1 && pathCase) {
82
+ const pathParts = path$1.split(".");
83
+ const transformedParts = pathParts.map((part) => applyCaseTransform(part, pathCase));
84
+ path$1 = transformedParts.join(".");
85
+ }
86
+ const newConfig = {};
87
+ if (clearOnDefaultNamespace && namespace === actualFallbackNamespace) {
88
+ if (path$1) {
89
+ newConfig.path = path$1;
90
+ } else {
91
+ event.save(void 0);
92
+ return;
93
+ }
94
+ } else {
95
+ if (namespace) {
96
+ newConfig.namespace = namespace;
97
+ }
98
+ if (path$1) {
99
+ newConfig.path = path$1;
100
+ }
101
+ }
102
+ if (Object.keys(newConfig).length > 0) {
103
+ event.save(newConfig);
104
+ }
105
+ };
106
+ }
107
+ function applyStructuredIgnore(segments, structure) {
108
+ const result = [];
109
+ let currentStructure = structure;
110
+ for (let i = 0; i < segments.length; i++) {
111
+ const segment = segments[i];
112
+ if (segment in currentStructure) {
113
+ const rule = currentStructure[segment];
114
+ if (rule === true) {
115
+ currentStructure = structure;
116
+ continue;
117
+ } else if (Array.isArray(rule)) {
118
+ result.push(segment);
119
+ if (i + 1 < segments.length && rule.includes(segments[i + 1])) {
120
+ i++;
121
+ }
122
+ currentStructure = structure;
123
+ continue;
124
+ } else if (typeof rule === "object" && rule !== null) {
125
+ result.push(segment);
126
+ currentStructure = rule;
127
+ continue;
128
+ }
129
+ }
130
+ result.push(segment);
131
+ currentStructure = structure;
132
+ }
133
+ return result;
134
+ }
135
+ function applyCaseTransform(str, caseType) {
136
+ const caseFunction = caseLib__namespace[caseType];
137
+ if (typeof caseFunction === "function") {
138
+ return caseFunction(str);
139
+ }
140
+ return str;
141
+ }
142
+ function extractRootFoldersFromIncludes(includes) {
143
+ const folders = /* @__PURE__ */ new Set();
144
+ for (const pattern of includes) {
145
+ let cleanPattern = pattern.replace(/^\.\//, "");
146
+ const match = cleanPattern.match(/^([^/]+)/);
147
+ if (!match) continue;
148
+ const firstSegment = match[1];
149
+ const groupMatch = firstSegment.match(/^[\(\[]([^\)\]]+)[\)\]]$/);
150
+ if (groupMatch) {
151
+ const groupFolders = groupMatch[1].split("|").map((f) => f.trim());
152
+ groupFolders.forEach((folder) => folders.add(folder));
153
+ } else {
154
+ folders.add(firstSegment);
155
+ }
156
+ }
157
+ return Array.from(folders);
158
+ }
159
+ exports.pathBasedConfigGenerator = pathBasedConfigGenerator;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Predefined algorithms for lang-tag-cli configuration.
3
+ *
4
+ * These algorithms can be used in your lang-tag-cli config file
5
+ * to customize how tags are processed during collection and regeneration.
6
+ */
7
+ export { pathBasedConfigGenerator, type PathBasedConfigGeneratorOptions } from './config-generation/index.ts';
@@ -0,0 +1,142 @@
1
+ import { sep } from "pathe";
2
+ import * as caseLib from "case";
3
+ function pathBasedConfigGenerator(options = {}) {
4
+ const {
5
+ includeFileName = false,
6
+ removeBracketedFolders = true,
7
+ ignoreFolders = [],
8
+ ignoreIncludesRootFolders = false,
9
+ ignoreStructured = {},
10
+ lowercaseNamespace = false,
11
+ namespaceCase,
12
+ pathCase,
13
+ fallbackNamespace,
14
+ clearOnDefaultNamespace = true
15
+ } = options;
16
+ return async (event) => {
17
+ const { relativePath, langTagConfig } = event;
18
+ const actualFallbackNamespace = fallbackNamespace ?? langTagConfig.collect?.defaultNamespace;
19
+ let finalIgnoreFolders = [...ignoreFolders];
20
+ if (ignoreIncludesRootFolders && langTagConfig.includes) {
21
+ const extractedFolders = extractRootFoldersFromIncludes(langTagConfig.includes);
22
+ finalIgnoreFolders = [.../* @__PURE__ */ new Set([...finalIgnoreFolders, ...extractedFolders])];
23
+ }
24
+ let pathSegments = relativePath.split(sep).filter(Boolean);
25
+ if (pathSegments.length === 0) {
26
+ return;
27
+ }
28
+ const fileName = pathSegments[pathSegments.length - 1];
29
+ const fileNameWithoutExt = fileName.replace(/\.[^.]+$/, "");
30
+ if (includeFileName) {
31
+ pathSegments[pathSegments.length - 1] = fileNameWithoutExt;
32
+ } else {
33
+ pathSegments = pathSegments.slice(0, -1);
34
+ }
35
+ pathSegments = pathSegments.map((segment) => {
36
+ const bracketMatch = segment.match(/^[\(\[](.+)[\)\]]$/);
37
+ if (bracketMatch) {
38
+ return removeBracketedFolders ? null : bracketMatch[1];
39
+ }
40
+ return segment;
41
+ }).filter((seg) => seg !== null);
42
+ pathSegments = applyStructuredIgnore(pathSegments, ignoreStructured);
43
+ pathSegments = pathSegments.filter((seg) => !finalIgnoreFolders.includes(seg));
44
+ let namespace;
45
+ let path;
46
+ if (pathSegments.length >= 1) {
47
+ namespace = pathSegments[0];
48
+ if (pathSegments.length > 1) {
49
+ path = pathSegments.slice(1).join(".");
50
+ }
51
+ } else {
52
+ namespace = actualFallbackNamespace;
53
+ }
54
+ if (namespace) {
55
+ if (lowercaseNamespace) {
56
+ namespace = namespace.toLowerCase();
57
+ }
58
+ if (namespaceCase) {
59
+ namespace = applyCaseTransform(namespace, namespaceCase);
60
+ }
61
+ }
62
+ if (path && pathCase) {
63
+ const pathParts = path.split(".");
64
+ const transformedParts = pathParts.map((part) => applyCaseTransform(part, pathCase));
65
+ path = transformedParts.join(".");
66
+ }
67
+ const newConfig = {};
68
+ if (clearOnDefaultNamespace && namespace === actualFallbackNamespace) {
69
+ if (path) {
70
+ newConfig.path = path;
71
+ } else {
72
+ event.save(void 0);
73
+ return;
74
+ }
75
+ } else {
76
+ if (namespace) {
77
+ newConfig.namespace = namespace;
78
+ }
79
+ if (path) {
80
+ newConfig.path = path;
81
+ }
82
+ }
83
+ if (Object.keys(newConfig).length > 0) {
84
+ event.save(newConfig);
85
+ }
86
+ };
87
+ }
88
+ function applyStructuredIgnore(segments, structure) {
89
+ const result = [];
90
+ let currentStructure = structure;
91
+ for (let i = 0; i < segments.length; i++) {
92
+ const segment = segments[i];
93
+ if (segment in currentStructure) {
94
+ const rule = currentStructure[segment];
95
+ if (rule === true) {
96
+ currentStructure = structure;
97
+ continue;
98
+ } else if (Array.isArray(rule)) {
99
+ result.push(segment);
100
+ if (i + 1 < segments.length && rule.includes(segments[i + 1])) {
101
+ i++;
102
+ }
103
+ currentStructure = structure;
104
+ continue;
105
+ } else if (typeof rule === "object" && rule !== null) {
106
+ result.push(segment);
107
+ currentStructure = rule;
108
+ continue;
109
+ }
110
+ }
111
+ result.push(segment);
112
+ currentStructure = structure;
113
+ }
114
+ return result;
115
+ }
116
+ function applyCaseTransform(str, caseType) {
117
+ const caseFunction = caseLib[caseType];
118
+ if (typeof caseFunction === "function") {
119
+ return caseFunction(str);
120
+ }
121
+ return str;
122
+ }
123
+ function extractRootFoldersFromIncludes(includes) {
124
+ const folders = /* @__PURE__ */ new Set();
125
+ for (const pattern of includes) {
126
+ let cleanPattern = pattern.replace(/^\.\//, "");
127
+ const match = cleanPattern.match(/^([^/]+)/);
128
+ if (!match) continue;
129
+ const firstSegment = match[1];
130
+ const groupMatch = firstSegment.match(/^[\(\[]([^\)\]]+)[\)\]]$/);
131
+ if (groupMatch) {
132
+ const groupFolders = groupMatch[1].split("|").map((f) => f.trim());
133
+ groupFolders.forEach((folder) => folders.add(folder));
134
+ } else {
135
+ folders.add(firstSegment);
136
+ }
137
+ }
138
+ return Array.from(folders);
139
+ }
140
+ export {
141
+ pathBasedConfigGenerator
142
+ };
package/config.d.ts CHANGED
@@ -170,6 +170,7 @@ export interface LangTagCLIConfigGenerationEvent {
170
170
  isImportedLibrary: boolean;
171
171
  /** The configuration object extracted from the lang tag's options argument (e.g., `{ namespace: 'common', path: 'my.path' }`). */
172
172
  config: LangTagTranslationsConfig | undefined;
173
+ langTagConfig: LangTagCLIConfig;
173
174
  /**
174
175
  * Tells CLI to replace tag configuration
175
176
  * undefined = means configuration will be removed
package/index.cjs CHANGED
@@ -441,6 +441,7 @@ async function checkAndRegenerateFileLangTags(config, logger, file, path$12) {
441
441
  let newConfig = void 0;
442
442
  let shouldUpdate = false;
443
443
  await config.onConfigGeneration({
444
+ langTagConfig: config,
444
445
  config: tag.parameterConfig,
445
446
  absolutePath: file,
446
447
  relativePath: path$12,
@@ -1414,13 +1415,22 @@ async function detectModuleSystem() {
1414
1415
  return "cjs";
1415
1416
  }
1416
1417
  }
1417
- function getExportStatement(moduleSystem) {
1418
- return moduleSystem === "esm" ? "export default config;" : "module.exports = config;";
1419
- }
1420
1418
  async function generateDefaultConfig() {
1421
1419
  const moduleSystem = await detectModuleSystem();
1422
- const exportStatement = getExportStatement(moduleSystem);
1423
- return `/** @type {import('@lang-tag/cli/config').LangTagCLIConfig} */
1420
+ const importStatement = moduleSystem === "esm" ? `import { pathBasedConfigGenerator } from '@lang-tag/cli/algorithms';` : `const { pathBasedConfigGenerator } = require('@lang-tag/cli/algorithms');`;
1421
+ const exportStatement = moduleSystem === "esm" ? "export default config;" : "module.exports = config;";
1422
+ return `${importStatement}
1423
+
1424
+ const generationAlgorithm = pathBasedConfigGenerator({
1425
+ ignoreIncludesRootFolders: true,
1426
+ removeBracketedFolders: true,
1427
+ namespaceCase: 'kebab',
1428
+ pathCase: 'camel',
1429
+ clearOnDefaultNamespace: true,
1430
+ ignoreFolders: ['core', 'utils', 'helpers']
1431
+ });
1432
+
1433
+ /** @type {import('@lang-tag/cli/config').LangTagCLIConfig} */
1424
1434
  const config = {
1425
1435
  tagName: 'lang',
1426
1436
  isLibrary: false,
@@ -1431,13 +1441,9 @@ const config = {
1431
1441
  // We do not modify imported configurations
1432
1442
  if (event.isImportedLibrary) return;
1433
1443
 
1434
- // const config = event.config || {};
1435
- //
1436
- // if (!config.path) {
1437
- // config.path = 'test';
1438
- // config.namespace = 'testNamespace';
1439
- // event.save(config);
1440
- // }
1444
+ if (event.config?.manual) return;
1445
+
1446
+ await generationAlgorithm(event);
1441
1447
  },
1442
1448
  collect: {
1443
1449
  defaultNamespace: 'common',
package/index.js CHANGED
@@ -421,6 +421,7 @@ async function checkAndRegenerateFileLangTags(config, logger, file, path2) {
421
421
  let newConfig = void 0;
422
422
  let shouldUpdate = false;
423
423
  await config.onConfigGeneration({
424
+ langTagConfig: config,
424
425
  config: tag.parameterConfig,
425
426
  absolutePath: file,
426
427
  relativePath: path2,
@@ -1394,13 +1395,22 @@ async function detectModuleSystem() {
1394
1395
  return "cjs";
1395
1396
  }
1396
1397
  }
1397
- function getExportStatement(moduleSystem) {
1398
- return moduleSystem === "esm" ? "export default config;" : "module.exports = config;";
1399
- }
1400
1398
  async function generateDefaultConfig() {
1401
1399
  const moduleSystem = await detectModuleSystem();
1402
- const exportStatement = getExportStatement(moduleSystem);
1403
- return `/** @type {import('@lang-tag/cli/config').LangTagCLIConfig} */
1400
+ const importStatement = moduleSystem === "esm" ? `import { pathBasedConfigGenerator } from '@lang-tag/cli/algorithms';` : `const { pathBasedConfigGenerator } = require('@lang-tag/cli/algorithms');`;
1401
+ const exportStatement = moduleSystem === "esm" ? "export default config;" : "module.exports = config;";
1402
+ return `${importStatement}
1403
+
1404
+ const generationAlgorithm = pathBasedConfigGenerator({
1405
+ ignoreIncludesRootFolders: true,
1406
+ removeBracketedFolders: true,
1407
+ namespaceCase: 'kebab',
1408
+ pathCase: 'camel',
1409
+ clearOnDefaultNamespace: true,
1410
+ ignoreFolders: ['core', 'utils', 'helpers']
1411
+ });
1412
+
1413
+ /** @type {import('@lang-tag/cli/config').LangTagCLIConfig} */
1404
1414
  const config = {
1405
1415
  tagName: 'lang',
1406
1416
  isLibrary: false,
@@ -1411,13 +1421,9 @@ const config = {
1411
1421
  // We do not modify imported configurations
1412
1422
  if (event.isImportedLibrary) return;
1413
1423
 
1414
- // const config = event.config || {};
1415
- //
1416
- // if (!config.path) {
1417
- // config.path = 'test';
1418
- // config.namespace = 'testNamespace';
1419
- // event.save(config);
1420
- // }
1424
+ if (event.config?.manual) return;
1425
+
1426
+ await generationAlgorithm(event);
1421
1427
  },
1422
1428
  collect: {
1423
1429
  defaultNamespace: 'common',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lang-tag/cli",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -22,6 +22,11 @@
22
22
  ".": {
23
23
  "import": "./index.js",
24
24
  "require": "./index.cjs"
25
+ },
26
+ "./algorithms": {
27
+ "types": "./algorithms/index.d.ts",
28
+ "import": "./algorithms/index.js",
29
+ "require": "./algorithms/index.cjs"
25
30
  }
26
31
  },
27
32
  "peerDependencies": {
@@ -29,6 +34,7 @@
29
34
  },
30
35
  "dependencies": {
31
36
  "acorn": "^8.15.0",
37
+ "case": "^1.6.3",
32
38
  "chokidar": "^4.0.3",
33
39
  "commander": "^13.1.0",
34
40
  "globby": "^14.1.0",