vitek-plugin 0.1.2-beta.3 → 0.1.2-beta.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/core/types/generate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAiB/C;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CA+FvF;AA4LD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAE,OAAc,GAAG,MAAM,CAuKxH;AA6KD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,OAAc,GAC3B,OAAO,CAAC,IAAI,CAAC,CAUf"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/core/types/generate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAyB/C;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CA+FvF;AA4LD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAE,OAAc,GAAG,MAAM,CAuKxH;AAmKD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,OAAc,GAC3B,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -18,6 +18,13 @@ function deduplicateRoutesByKey(routes) {
18
18
  return true;
19
19
  });
20
20
  }
21
+ function partToIdentifier(part) {
22
+ const clean = part.replace(/^[:*]/, '');
23
+ return clean
24
+ .split('-')
25
+ .map(word => capitalize(word))
26
+ .join('');
27
+ }
21
28
  /**
22
29
  * Generates the content of the generated types file
23
30
  */
@@ -480,13 +487,8 @@ function generateFunctionNameBase(route) {
480
487
  const parts = route.pattern
481
488
  .split('/')
482
489
  .filter(Boolean)
483
- .filter(part => part !== 'index') // Remove "index" from function name
484
- .map(part => {
485
- // Remove : or * from the beginning
486
- const clean = part.replace(/^[:*]/, '');
487
- // Capitalize
488
- return capitalize(clean);
489
- });
490
+ .filter(part => part !== 'index')
491
+ .map(part => partToIdentifier(part));
490
492
  const pathName = parts.join('');
491
493
  return `${method}${pathName}`;
492
494
  }
@@ -500,12 +502,7 @@ function generateUniqueFunctionName(route, baseName, conflictingRoutes, existing
500
502
  // Remove "index" but keep other parts (including params for context)
501
503
  const allParts = patternParts
502
504
  .filter(part => part !== 'index')
503
- .map(part => {
504
- // For parameters, include the name as context to differentiate
505
- // Example: ":id" becomes "Id", "*ids" becomes "Ids"
506
- const clean = part.replace(/^[:*]/, '');
507
- return capitalize(clean);
508
- });
505
+ .map(part => partToIdentifier(part));
509
506
  // Use all parts of the path to ensure uniqueness from the start
510
507
  if (allParts.length > 0) {
511
508
  const fullPathName = allParts.join('');
@@ -211,6 +211,20 @@ describe('generateServicesContent', () => {
211
211
  expect(count).toBe(1);
212
212
  });
213
213
  });
214
+ it('generates valid identifiers for paths with hyphens (e.g. inter-squad)', () => {
215
+ const routes = [
216
+ route('inter-squad/requests', 'post', []),
217
+ route('inter-squad/requests/:id/status', 'put', ['id']),
218
+ ];
219
+ const content = generateServicesContent(routes, API_BASE, true);
220
+ const names = getExportedFunctionNames(content);
221
+ expect(names).toHaveLength(2);
222
+ expect(names).toContain('postInterSquadRequests');
223
+ expect(names).toContain('putInterSquadRequestsIdStatus');
224
+ names.forEach((name) => {
225
+ expect(name).toMatch(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/);
226
+ });
227
+ });
214
228
  it('regression: flows/id/versions GET and POST each appear once', () => {
215
229
  const routes = [
216
230
  route('flows/:id/versions', 'get', ['id']),
package/dist/plugin.js CHANGED
@@ -57,12 +57,12 @@ export function vitek(options = {}) {
57
57
  },
58
58
  transform(code, id) {
59
59
  const idPath = id.startsWith('file:') ? fileURLToPath(id) : id;
60
- const fullApiDir = path.resolve(root, apiDirOption);
60
+ const srcDir = path.resolve(root, 'src');
61
61
  const virtualCandidate = idPath.startsWith('/') ? path.join(root, idPath.replace(/^\//, '')) : null;
62
62
  const normalizedId = virtualCandidate != null && fs.existsSync(virtualCandidate)
63
63
  ? virtualCandidate
64
64
  : path.resolve(idPath);
65
- if (!normalizedId.startsWith(fullApiDir))
65
+ if (!normalizedId.startsWith(srcDir))
66
66
  return null;
67
67
  const dir = path.dirname(normalizedId);
68
68
  const rootSlash = path.resolve(root) + path.sep;
@@ -87,13 +87,32 @@ describe('vitek plugin resolveId and transform', () => {
87
87
  });
88
88
  });
89
89
  describe('transform', () => {
90
- const healthGetId = path.join(process.cwd(), 'placeholder'); // will override per platform
91
- it('returns null when id is not under apiDir', () => {
90
+ it('returns null when id is not under src', () => {
92
91
  const code = "import x from '../lib/greeting';";
93
- const idOutside = pathToFileURL(path.join(rootDir, 'src', 'main.ts')).href;
92
+ fs.mkdirSync(path.join(rootDir, 'other'), { recursive: true });
93
+ const idOutside = pathToFileURL(path.join(rootDir, 'other', 'main.ts')).href;
94
94
  const result = callTransform(plugin, code, idOutside);
95
95
  expect(result).toBeNull();
96
96
  });
97
+ it('rewrites relative import when id is under src/lib (not only api)', () => {
98
+ fs.writeFileSync(path.join(rootDir, 'src', 'lib', 'executor.ts'), "import { getGreeting } from './greeting';\nexport function run() { return getGreeting(); }\n", 'utf-8');
99
+ const code = "import { getGreeting } from './greeting';\nexport function run() { return getGreeting(); }\n";
100
+ const id = pathToFileURL(path.join(rootDir, 'src', 'lib', 'executor.ts')).href;
101
+ const result = callTransform(plugin, code, id);
102
+ expect(result).not.toBeNull();
103
+ expect(result.code).toContain("/src/lib/greeting");
104
+ expect(result.code).not.toContain("from './greeting'");
105
+ });
106
+ it('rewrites relative import in src/lib file that imports from subpath', () => {
107
+ fs.mkdirSync(path.join(rootDir, 'src', 'lib', 'nested'), { recursive: true });
108
+ fs.writeFileSync(path.join(rootDir, 'src', 'lib', 'nested', 'helper.ts'), "export function getMessage() { return 'ok'; }\n", 'utf-8');
109
+ fs.writeFileSync(path.join(rootDir, 'src', 'lib', 'nested', 'index.ts'), "export { getMessage } from './helper';\n", 'utf-8');
110
+ const code = "export { getMessage } from './helper';\n";
111
+ const id = pathToFileURL(path.join(rootDir, 'src', 'lib', 'nested', 'index.ts')).href;
112
+ const result = callTransform(plugin, code, id);
113
+ expect(result).not.toBeNull();
114
+ expect(result.code).toContain("/src/lib/nested/helper");
115
+ });
97
116
  it('rewrites relative import to root-relative path when id is under apiDir', () => {
98
117
  const code = "import { getGreeting } from '../lib/greeting';\nexport default function handler() {}";
99
118
  const id = pathToFileURL(path.join(apiDir, 'health.get.ts')).href;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitek-plugin",
3
- "version": "0.1.2-beta.3",
3
+ "version": "0.1.2-beta.5",
4
4
  "description": "Vite plugin for file-based HTTP API generation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",