vite-plugin-ferry 0.1.5 → 0.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.
@@ -4,20 +4,25 @@ export type EnumGeneratorOptions = {
4
4
  outputDir: string;
5
5
  packageName: string;
6
6
  prettyPrint?: boolean;
7
+ cwd: string;
7
8
  };
8
9
  /**
9
- * Generate TypeScript type declarations for enums.
10
+ * Generate TypeScript type declaration for a single enum.
10
11
  */
11
- export declare function generateEnumTypeScript(enums: Record<string, EnumDefinition>): string;
12
+ export declare function generateSingleEnumTypeScript(enumDef: EnumDefinition, phpFile?: string): string;
12
13
  /**
13
- * Generate runtime JavaScript for enums.
14
+ * Generate source map for a single enum.
14
15
  */
15
- export declare function generateEnumRuntime(enums: Record<string, EnumDefinition>, prettyPrint?: boolean): string;
16
+ export declare function generateEnumSourceMap(enumDef: EnumDefinition, generatedFile: string, phpFile: string, outputDir: string): string;
17
+ /**
18
+ * Generate runtime JavaScript for a single enum.
19
+ */
20
+ export declare function generateSingleEnumRuntime(enumDef: EnumDefinition, prettyPrint?: boolean): string;
16
21
  /**
17
22
  * Collect all enum definitions from the enums directory.
18
23
  * This is a plugin-level function that handles file I/O.
19
24
  */
20
- export declare function collectEnums(enumsDir: string): Record<string, EnumDefinition>;
25
+ export declare function collectEnums(enumsDir: string, cwd: string): Record<string, EnumDefinition>;
21
26
  /**
22
27
  * Generate enum files (TypeScript declarations and runtime JavaScript).
23
28
  */
@@ -1 +1 @@
1
- {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/generators/enums.ts"],"names":[],"mappings":"AAIA,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAc/E,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,MAAM,CA4BpF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,WAAW,UAAO,GAAG,MAAM,CAgCrG;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA0B7E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CA6BjE"}
1
+ {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/generators/enums.ts"],"names":[],"mappings":"AAIA,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAiB/E,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAmC9F;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,MAAM,CAsCR;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,cAAc,EAAE,WAAW,UAAO,GAAG,MAAM,CAsB7F;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA4B1F;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAmDjE"}
@@ -1,73 +1,109 @@
1
1
  import ts from 'typescript';
2
2
  import { existsSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import { getPhpFiles, readFileSafe, writeFileEnsureDir } from '../utils/file.js';
3
+ import { join, relative } from 'node:path';
4
+ import { getPhpFiles, readFileSafe, writeFileEnsureDir, cleanOutputDir } from '../utils/file.js';
5
5
  import { parseEnumContent } from '../utils/php-parser.js';
6
- import { printNodes, createEnum, createConstObject, createObjectLiteral, createDeclareConstWithType, createTypeLiteral, createStringLiteral, createNumericLiteral, createExportDefault, printNode, } from '../utils/ts-generator.js';
6
+ import { createEnum, createConstObject, createObjectLiteral, createDeclareConstWithType, createTypeLiteral, createStringLiteral, createNumericLiteral, printNode, } from '../utils/ts-generator.js';
7
+ import { generateSourceMap, createSourceMapComment, } from '../utils/source-map.js';
7
8
  /**
8
- * Generate TypeScript type declarations for enums.
9
+ * Generate TypeScript type declaration for a single enum.
9
10
  */
10
- export function generateEnumTypeScript(enums) {
11
- const nodes = [];
12
- for (const enumName of Object.keys(enums)) {
13
- const enumDef = enums[enumName];
14
- const hasLabels = enumDef.cases.some((c) => c.label);
15
- if (hasLabels) {
16
- // Generate a declare const with typed properties for enums with labels
17
- const properties = enumDef.cases.map((c) => ({
18
- name: c.key,
19
- type: createTypeLiteral([
20
- { name: 'value', type: ts.factory.createLiteralTypeNode(createStringLiteral(String(c.value))) },
21
- {
22
- name: 'label',
23
- type: ts.factory.createLiteralTypeNode(createStringLiteral(c.label || String(c.value))),
24
- },
25
- ]),
26
- }));
27
- nodes.push(createDeclareConstWithType(enumDef.name, createTypeLiteral(properties)));
28
- }
29
- else {
30
- // Generate a traditional enum
31
- nodes.push(createEnum(enumDef.name, enumDef.cases.map((c) => ({ key: c.key, value: c.value }))));
32
- }
11
+ export function generateSingleEnumTypeScript(enumDef, phpFile) {
12
+ const hasLabels = enumDef.cases.some((c) => c.label);
13
+ let node;
14
+ if (hasLabels) {
15
+ const properties = enumDef.cases.map((c) => ({
16
+ name: c.key,
17
+ type: createTypeLiteral([
18
+ { name: 'value', type: ts.factory.createLiteralTypeNode(createStringLiteral(String(c.value))) },
19
+ {
20
+ name: 'label',
21
+ type: ts.factory.createLiteralTypeNode(createStringLiteral(c.label || String(c.value))),
22
+ },
23
+ ]),
24
+ }));
25
+ node = createDeclareConstWithType(enumDef.name, createTypeLiteral(properties));
26
+ }
27
+ else {
28
+ node = createEnum(enumDef.name, enumDef.cases.map((c) => ({ key: c.key, value: c.value })));
29
+ }
30
+ const lines = [];
31
+ // Add JSDoc with source reference
32
+ if (phpFile) {
33
+ lines.push(`/** @see ${phpFile} */`);
33
34
  }
34
- return nodes.length > 0 ? printNodes(nodes) + '\n' : '';
35
+ lines.push(printNode(node));
36
+ // Add source map comment
37
+ if (phpFile) {
38
+ lines.push(createSourceMapComment(`${enumDef.name}.d.ts.map`));
39
+ }
40
+ return lines.join('\n') + '\n';
35
41
  }
36
42
  /**
37
- * Generate runtime JavaScript for enums.
43
+ * Generate source map for a single enum.
38
44
  */
39
- export function generateEnumRuntime(enums, prettyPrint = true) {
40
- const nodes = [];
41
- for (const enumName of Object.keys(enums)) {
42
- const enumDef = enums[enumName];
43
- const hasLabels = enumDef.cases.some((c) => c.label);
44
- const properties = enumDef.cases.map((c) => {
45
- let value;
46
- if (hasLabels) {
47
- value = createObjectLiteral([
48
- { key: 'value', value: createStringLiteral(String(c.value)) },
49
- { key: 'label', value: createStringLiteral(c.label || String(c.value)) },
50
- ], prettyPrint);
51
- }
52
- else if (typeof c.value === 'number') {
53
- value = createNumericLiteral(c.value);
54
- }
55
- else {
56
- value = createStringLiteral(String(c.value));
57
- }
58
- return { key: c.key, value };
45
+ export function generateEnumSourceMap(enumDef, generatedFile, phpFile, outputDir) {
46
+ const mappings = [];
47
+ // Map enum declaration to its source location
48
+ if (enumDef.loc) {
49
+ mappings.push({
50
+ generatedLine: 2, // Line after JSDoc comment
51
+ generatedColumn: 0,
52
+ sourceLine: enumDef.loc.line,
53
+ sourceColumn: enumDef.loc.column || 0,
59
54
  });
60
- nodes.push(createConstObject(enumDef.name, properties));
61
55
  }
62
- // Add export default {}
63
- nodes.push(createExportDefault(ts.factory.createObjectLiteralExpression([])));
64
- return nodes.map(printNode).join('\n\n') + '\n';
56
+ // Map each enum case to its source location
57
+ const hasLabels = enumDef.cases.some((c) => c.label);
58
+ let currentLine = 3; // Start after "export enum Name {" or "export declare const Name: {"
59
+ for (const enumCase of enumDef.cases) {
60
+ if (enumCase.loc) {
61
+ mappings.push({
62
+ generatedLine: currentLine,
63
+ generatedColumn: 4, // Indented
64
+ sourceLine: enumCase.loc.line,
65
+ sourceColumn: enumCase.loc.column || 0,
66
+ });
67
+ }
68
+ // For labeled enums, each case takes multiple lines
69
+ currentLine += hasLabels ? 4 : 1;
70
+ }
71
+ // Calculate relative path from output dir to PHP file
72
+ const relativeSource = relative(outputDir, join(process.cwd(), phpFile)).replace(/\\/g, '/');
73
+ return generateSourceMap({
74
+ file: generatedFile,
75
+ sources: [relativeSource],
76
+ mappings,
77
+ });
78
+ }
79
+ /**
80
+ * Generate runtime JavaScript for a single enum.
81
+ */
82
+ export function generateSingleEnumRuntime(enumDef, prettyPrint = true) {
83
+ const hasLabels = enumDef.cases.some((c) => c.label);
84
+ const properties = enumDef.cases.map((c) => {
85
+ let value;
86
+ if (hasLabels) {
87
+ value = createObjectLiteral([
88
+ { key: 'value', value: createStringLiteral(String(c.value)) },
89
+ { key: 'label', value: createStringLiteral(c.label || String(c.value)) },
90
+ ], prettyPrint);
91
+ }
92
+ else if (typeof c.value === 'number') {
93
+ value = createNumericLiteral(c.value);
94
+ }
95
+ else {
96
+ value = createStringLiteral(String(c.value));
97
+ }
98
+ return { key: c.key, value };
99
+ });
100
+ return printNode(createConstObject(enumDef.name, properties)) + '\n';
65
101
  }
66
102
  /**
67
103
  * Collect all enum definitions from the enums directory.
68
104
  * This is a plugin-level function that handles file I/O.
69
105
  */
70
- export function collectEnums(enumsDir) {
106
+ export function collectEnums(enumsDir, cwd) {
71
107
  const enums = {};
72
108
  if (!existsSync(enumsDir)) {
73
109
  return enums;
@@ -79,7 +115,9 @@ export function collectEnums(enumsDir) {
79
115
  const content = readFileSafe(enumPath);
80
116
  if (!content)
81
117
  continue;
82
- const def = parseEnumContent(content);
118
+ // Calculate relative path from project root for source mapping
119
+ const relativePhpPath = relative(cwd, enumPath);
120
+ const def = parseEnumContent(content, relativePhpPath);
83
121
  if (def) {
84
122
  enums[def.name] = def;
85
123
  }
@@ -95,17 +133,34 @@ export function collectEnums(enumsDir) {
95
133
  * Generate enum files (TypeScript declarations and runtime JavaScript).
96
134
  */
97
135
  export function generateEnums(options) {
98
- const { enumsDir, outputDir, packageName, prettyPrint = true } = options;
136
+ const { enumsDir, outputDir, packageName, prettyPrint = true, cwd } = options;
137
+ // Clean existing generated files
138
+ cleanOutputDir(outputDir);
99
139
  // Collect all enums
100
- const enums = collectEnums(enumsDir);
101
- // Generate TypeScript declarations
102
- const dtsContent = generateEnumTypeScript(enums);
103
- const dtsPath = join(outputDir, 'index.d.ts');
104
- writeFileEnsureDir(dtsPath, dtsContent);
105
- // Generate runtime JavaScript
106
- const jsContent = generateEnumRuntime(enums, prettyPrint);
107
- const jsPath = join(outputDir, 'index.js');
108
- writeFileEnsureDir(jsPath, jsContent);
140
+ const enums = collectEnums(enumsDir, cwd);
141
+ const enumNames = Object.keys(enums);
142
+ // Generate individual files for each enum
143
+ for (const enumName of enumNames) {
144
+ const enumDef = enums[enumName];
145
+ const phpFile = enumDef.loc?.file;
146
+ // Generate {EnumName}.d.ts with JSDoc and source map comment
147
+ const dtsContent = generateSingleEnumTypeScript(enumDef, phpFile);
148
+ writeFileEnsureDir(join(outputDir, `${enumName}.d.ts`), dtsContent);
149
+ // Generate {EnumName}.d.ts.map
150
+ if (phpFile) {
151
+ const sourceMap = generateEnumSourceMap(enumDef, `${enumName}.d.ts`, phpFile, outputDir);
152
+ writeFileEnsureDir(join(outputDir, `${enumName}.d.ts.map`), sourceMap);
153
+ }
154
+ // Generate {EnumName}.js
155
+ const jsContent = generateSingleEnumRuntime(enumDef, prettyPrint);
156
+ writeFileEnsureDir(join(outputDir, `${enumName}.js`), jsContent);
157
+ }
158
+ // Generate barrel index.d.ts
159
+ const indexDts = enumNames.map((n) => `export { ${n} } from './${n}.js';`).join('\n') + '\n';
160
+ writeFileEnsureDir(join(outputDir, 'index.d.ts'), indexDts);
161
+ // Generate barrel index.js
162
+ const indexJs = enumNames.map((n) => `export { ${n} } from './${n}.js';`).join('\n') + '\n';
163
+ writeFileEnsureDir(join(outputDir, 'index.js'), indexJs);
109
164
  // Generate package.json
110
165
  const pkgJson = JSON.stringify({
111
166
  name: packageName,
@@ -6,12 +6,17 @@ export type ResourceGeneratorOptions = {
6
6
  outputDir: string;
7
7
  packageName: string;
8
8
  prettyPrint?: boolean;
9
+ cwd: string;
9
10
  };
10
11
  export type FieldInfo = ResourceFieldInfo;
11
12
  /**
12
- * Generate TypeScript type declarations for resources.
13
+ * Generate TypeScript type declaration for a single resource.
13
14
  */
14
- export declare function generateResourceTypeScript(resources: Record<string, Record<string, ResourceFieldInfo>>, fallbacks: string[], referencedEnums: Set<string>): string;
15
+ export declare function generateSingleResourceTypeScript(className: string, fields: Record<string, ResourceFieldInfo>, isFallback: boolean, referencedEnums: Set<string>, phpFile?: string): string;
16
+ /**
17
+ * Generate source map for a single resource.
18
+ */
19
+ export declare function generateResourceSourceMap(className: string, fields: Record<string, ResourceFieldInfo>, generatedFile: string, phpFile: string, outputDir: string, hasEnumImports: boolean): string;
15
20
  /**
16
21
  * Generate runtime JavaScript for resources.
17
22
  * Resources are type-only, so this just exports an empty object.
@@ -1 +1 @@
1
- {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/generators/resources.ts"],"names":[],"mappings":"AAIA,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,wBAAwB,CAAC;AAUhC,MAAM,MAAM,wBAAwB,GAAG;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAGF,MAAM,MAAM,SAAS,GAAG,iBAAiB,CAAC;AAE1C;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,EAC5D,SAAS,EAAE,MAAM,EAAE,EACnB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,GAC3B,MAAM,CAuCR;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAoEzE"}
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/generators/resources.ts"],"names":[],"mappings":"AAIA,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,wBAAwB,CAAC;AAehC,MAAM,MAAM,wBAAwB,GAAG;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAGF,MAAM,MAAM,SAAS,GAAG,iBAAiB,CAAC;AAE1C;;GAEG;AACH,wBAAgB,gCAAgC,CAC9C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACzC,UAAU,EAAE,OAAO,EACnB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,EAC5B,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAuDR;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACzC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,OAAO,GACtB,MAAM,CAiCR;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAmHzE"}
@@ -1,32 +1,40 @@
1
1
  import ts from 'typescript';
2
2
  import { existsSync } from 'node:fs';
3
- import { join, parse } from 'node:path';
4
- import { getPhpFiles, readFileSafe, writeFileEnsureDir } from '../utils/file.js';
3
+ import { join, parse, relative } from 'node:path';
4
+ import { getPhpFiles, readFileSafe, writeFileEnsureDir, cleanOutputDir } from '../utils/file.js';
5
5
  import { extractDocblockArrayShape, parseResourceFieldsAst, } from '../utils/php-parser.js';
6
6
  import { mapDocTypeToTs } from '../utils/type-mapper.js';
7
7
  import { printNode, createTypeAlias, createImportType, parseTypeString, createTypeLiteral, } from '../utils/ts-generator.js';
8
+ import { generateSourceMap, createSourceMapComment, } from '../utils/source-map.js';
8
9
  /**
9
- * Generate TypeScript type declarations for resources.
10
+ * Generate TypeScript type declaration for a single resource.
10
11
  */
11
- export function generateResourceTypeScript(resources, fallbacks, referencedEnums) {
12
+ export function generateSingleResourceTypeScript(className, fields, isFallback, referencedEnums, phpFile) {
12
13
  const nodes = [];
14
+ // Find which enums this resource actually uses
15
+ const usedEnums = new Set();
16
+ for (const info of Object.values(fields)) {
17
+ const type = info.type || '';
18
+ for (const enumName of referencedEnums) {
19
+ if (type.includes(enumName)) {
20
+ usedEnums.add(enumName);
21
+ }
22
+ }
23
+ }
13
24
  // Import referenced enums from @app/enums
14
- if (referencedEnums.size > 0) {
15
- const enumImports = Array.from(referencedEnums).sort();
25
+ if (usedEnums.size > 0) {
26
+ const enumImports = Array.from(usedEnums).sort();
16
27
  nodes.push(createImportType(enumImports, '@app/enums'));
17
28
  }
18
- // Generate resource types
19
- for (const className of Object.keys(resources)) {
20
- const fields = resources[className];
21
- if (fallbacks.includes(className)) {
22
- // Fallback type: Record<string, any>
23
- const recordType = ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
24
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
25
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
26
- ]);
27
- nodes.push(createTypeAlias(className, recordType));
28
- continue;
29
- }
29
+ if (isFallback) {
30
+ // Fallback type: Record<string, any>
31
+ const recordType = ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
32
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
33
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
34
+ ]);
35
+ nodes.push(createTypeAlias(className, recordType));
36
+ }
37
+ else {
30
38
  // Create type literal with all fields
31
39
  const properties = Object.keys(fields).map((key) => {
32
40
  const info = fields[key];
@@ -38,9 +46,49 @@ export function generateResourceTypeScript(resources, fallbacks, referencedEnums
38
46
  });
39
47
  nodes.push(createTypeAlias(className, createTypeLiteral(properties)));
40
48
  }
41
- if (nodes.length === 0)
42
- return '';
43
- return nodes.map(printNode).join('\n\n') + '\n';
49
+ const lines = [];
50
+ // Add JSDoc with source reference
51
+ if (phpFile) {
52
+ lines.push(`/** @see ${phpFile} */`);
53
+ }
54
+ lines.push(nodes.map(printNode).join('\n\n'));
55
+ // Add source map comment
56
+ if (phpFile) {
57
+ lines.push(createSourceMapComment(`${className}.d.ts.map`));
58
+ }
59
+ return lines.join('\n') + '\n';
60
+ }
61
+ /**
62
+ * Generate source map for a single resource.
63
+ */
64
+ export function generateResourceSourceMap(className, fields, generatedFile, phpFile, outputDir, hasEnumImports) {
65
+ const mappings = [];
66
+ // Calculate base line (after JSDoc comment and optional import)
67
+ let currentLine = 2; // Start after JSDoc comment
68
+ if (hasEnumImports) {
69
+ currentLine += 2; // Import statement + blank line
70
+ }
71
+ // Map each field to its source location
72
+ const fieldKeys = Object.keys(fields);
73
+ for (let i = 0; i < fieldKeys.length; i++) {
74
+ const key = fieldKeys[i];
75
+ const info = fields[key];
76
+ if (info.loc) {
77
+ mappings.push({
78
+ generatedLine: currentLine + 1 + i, // +1 for the "export type X = {" line
79
+ generatedColumn: 4, // Indented
80
+ sourceLine: info.loc.line,
81
+ sourceColumn: info.loc.column || 0,
82
+ });
83
+ }
84
+ }
85
+ // Calculate relative path from output dir to PHP file
86
+ const relativeSource = relative(outputDir, join(process.cwd(), phpFile)).replace(/\\/g, '/');
87
+ return generateSourceMap({
88
+ file: generatedFile,
89
+ sources: [relativeSource],
90
+ mappings,
91
+ });
44
92
  }
45
93
  /**
46
94
  * Generate runtime JavaScript for resources.
@@ -53,9 +101,12 @@ export function generateResourceRuntime() {
53
101
  * Generate resource type files (TypeScript declarations and runtime JavaScript).
54
102
  */
55
103
  export function generateResources(options) {
56
- const { resourcesDir, enumsDir, modelsDir, outputDir, packageName } = options;
104
+ const { resourcesDir, enumsDir, modelsDir, outputDir, packageName, cwd } = options;
105
+ // Clean existing generated files
106
+ cleanOutputDir(outputDir);
57
107
  const collectedEnums = {};
58
108
  const resources = {};
109
+ const resourcePhpFiles = {};
59
110
  const fallbacks = [];
60
111
  if (!existsSync(resourcesDir)) {
61
112
  console.warn(`Resources directory not found: ${resourcesDir}`);
@@ -67,6 +118,8 @@ export function generateResources(options) {
67
118
  const filePath = join(resourcesDir, file);
68
119
  const content = readFileSafe(filePath) || '';
69
120
  const className = parse(file).name;
121
+ // Calculate relative path from project root for source mapping
122
+ const relativePhpPath = relative(cwd, filePath);
70
123
  const docShape = extractDocblockArrayShape(content);
71
124
  const mappedDocShape = docShape ? mapDocTypeToTsForShape(docShape) : null;
72
125
  const fields = parseResourceFieldsAst(content, {
@@ -75,6 +128,7 @@ export function generateResources(options) {
75
128
  enumsDir,
76
129
  docShape: mappedDocShape,
77
130
  collectedEnums,
131
+ filePath: relativePhpPath,
78
132
  });
79
133
  if (!fields) {
80
134
  fallbacks.push(className);
@@ -83,6 +137,7 @@ export function generateResources(options) {
83
137
  else {
84
138
  resources[className] = fields;
85
139
  }
140
+ resourcePhpFiles[className] = relativePhpPath;
86
141
  }
87
142
  catch (e) {
88
143
  console.warn(`Failed to parse resource file: ${file}`, e);
@@ -90,14 +145,40 @@ export function generateResources(options) {
90
145
  }
91
146
  // Track which enums are actually referenced
92
147
  const referencedEnums = new Set(Object.keys(collectedEnums));
93
- // Generate TypeScript declarations
94
- const dtsContent = generateResourceTypeScript(resources, fallbacks, referencedEnums);
95
- const dtsPath = join(outputDir, 'index.d.ts');
96
- writeFileEnsureDir(dtsPath, dtsContent);
97
- // Generate runtime JavaScript
98
- const jsContent = generateResourceRuntime();
99
- const jsPath = join(outputDir, 'index.js');
100
- writeFileEnsureDir(jsPath, jsContent);
148
+ const resourceNames = Object.keys(resources);
149
+ // Generate individual files for each resource
150
+ for (const className of resourceNames) {
151
+ const fields = resources[className];
152
+ const isFallback = fallbacks.includes(className);
153
+ const phpFile = resourcePhpFiles[className];
154
+ // Check if this resource has enum imports
155
+ const usedEnums = new Set();
156
+ for (const info of Object.values(fields)) {
157
+ const type = info.type || '';
158
+ for (const enumName of referencedEnums) {
159
+ if (type.includes(enumName)) {
160
+ usedEnums.add(enumName);
161
+ }
162
+ }
163
+ }
164
+ const hasEnumImports = usedEnums.size > 0;
165
+ // Generate {ResourceName}.d.ts with JSDoc and source map comment
166
+ const dtsContent = generateSingleResourceTypeScript(className, fields, isFallback, referencedEnums, phpFile);
167
+ writeFileEnsureDir(join(outputDir, `${className}.d.ts`), dtsContent);
168
+ // Generate {ResourceName}.d.ts.map
169
+ if (phpFile && !isFallback) {
170
+ const sourceMap = generateResourceSourceMap(className, fields, `${className}.d.ts`, phpFile, outputDir, hasEnumImports);
171
+ writeFileEnsureDir(join(outputDir, `${className}.d.ts.map`), sourceMap);
172
+ }
173
+ // Generate {ResourceName}.js (empty export for type-only)
174
+ writeFileEnsureDir(join(outputDir, `${className}.js`), 'export {};\n');
175
+ }
176
+ // Generate barrel index.d.ts
177
+ const indexDts = resourceNames.map((n) => `export type { ${n} } from './${n}.js';`).join('\n') + '\n';
178
+ writeFileEnsureDir(join(outputDir, 'index.d.ts'), indexDts);
179
+ // Generate barrel index.js
180
+ const indexJs = resourceNames.map((n) => `export * from './${n}.js';`).join('\n') + '\n';
181
+ writeFileEnsureDir(join(outputDir, 'index.js'), indexJs);
101
182
  // Generate package.json
102
183
  const pkgJson = JSON.stringify({
103
184
  name: packageName,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAMnC,MAAM,MAAM,0BAA0B,GAAG;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,OAAO,GAAE,0BAA+B,GAAG,MAAM,CAyF9E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAMnC,MAAM,MAAM,0BAA0B,GAAG;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,OAAO,GAAE,0BAA+B,GAAG,MAAM,CA6F9E"}
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ export default function ferry(options = {}) {
34
34
  outputDir: enumsOutputDir,
35
35
  packageName: `${namespace}/enums`,
36
36
  prettyPrint,
37
+ cwd,
37
38
  });
38
39
  // Generate @app/resources package
39
40
  generateResources({
@@ -43,6 +44,7 @@ export default function ferry(options = {}) {
43
44
  outputDir: resourcesOutputDir,
44
45
  packageName: `${namespace}/resources`,
45
46
  prettyPrint,
47
+ cwd,
46
48
  });
47
49
  }
48
50
  return {
@@ -78,6 +80,7 @@ export default function ferry(options = {}) {
78
80
  enumsDir,
79
81
  outputDir: enumsOutputDir,
80
82
  packageName: `${namespace}/enums`,
83
+ cwd,
81
84
  server,
82
85
  });
83
86
  // Set up resource watcher
@@ -87,6 +90,7 @@ export default function ferry(options = {}) {
87
90
  modelsDir,
88
91
  outputDir: resourcesOutputDir,
89
92
  packageName: `${namespace}/resources`,
93
+ cwd,
90
94
  server,
91
95
  });
92
96
  },
@@ -10,4 +10,8 @@ export declare function writeFileEnsureDir(filePath: string, content: string): v
10
10
  * Get all PHP files from a directory.
11
11
  */
12
12
  export declare function getPhpFiles(dir: string): string[];
13
+ /**
14
+ * Clean generated files from output directory, keeping package.json.
15
+ */
16
+ export declare function cleanOutputDir(outputDir: string): void;
13
17
  //# sourceMappingURL=file.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/utils/file.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM5D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI1E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAKjD"}
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/utils/file.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM5D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI1E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAKjD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAStD"}
@@ -1,5 +1,5 @@
1
- import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
2
- import { dirname } from 'node:path';
1
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync, unlinkSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
3
  /**
4
4
  * Safely read a file, returning null if it doesn't exist or can't be read.
5
5
  */
@@ -28,3 +28,16 @@ export function getPhpFiles(dir) {
28
28
  }
29
29
  return readdirSync(dir).filter((f) => f.endsWith('.php'));
30
30
  }
31
+ /**
32
+ * Clean generated files from output directory, keeping package.json.
33
+ */
34
+ export function cleanOutputDir(outputDir) {
35
+ if (!existsSync(outputDir))
36
+ return;
37
+ const files = readdirSync(outputDir);
38
+ for (const file of files) {
39
+ if (file.endsWith('.d.ts') || file.endsWith('.js') || file.endsWith('.d.ts.map')) {
40
+ unlinkSync(join(outputDir, file));
41
+ }
42
+ }
43
+ }
@@ -1,18 +1,25 @@
1
+ export type SourceLocation = {
2
+ file: string;
3
+ line: number;
4
+ column?: number;
5
+ };
1
6
  export type EnumCase = {
2
7
  key: string;
3
8
  value: string | number;
4
9
  label?: string;
10
+ loc?: SourceLocation;
5
11
  };
6
12
  export type EnumDefinition = {
7
13
  name: string;
8
14
  backing: string | null;
9
15
  cases: EnumCase[];
16
+ loc?: SourceLocation;
10
17
  };
11
18
  /**
12
19
  * Parse PHP enum content and extract its definition.
13
20
  * This is a pure function that takes PHP source code as input.
14
21
  */
15
- export declare function parseEnumContent(phpContent: string): EnumDefinition | null;
22
+ export declare function parseEnumContent(phpContent: string, filePath?: string): EnumDefinition | null;
16
23
  /**
17
24
  * Parse model casts from PHP model content.
18
25
  * This is a pure function that takes PHP source code as input.
@@ -26,6 +33,7 @@ export declare function extractDocblockArrayShape(phpContent: string): Record<st
26
33
  export type ResourceFieldInfo = {
27
34
  type: string;
28
35
  optional: boolean;
36
+ loc?: SourceLocation;
29
37
  };
30
38
  export type ResourceArrayEntry = {
31
39
  key: string;
@@ -39,6 +47,7 @@ export type ParseResourceOptions = {
39
47
  docShape?: Record<string, string> | null;
40
48
  collectedEnums?: Record<string, EnumDefinition>;
41
49
  resourceClass?: string;
50
+ filePath?: string;
42
51
  };
43
52
  /**
44
53
  * Parse resource fields from PHP content using AST.
@@ -1 +1 @@
1
- {"version":3,"file":"php-parser.d.ts","sourceRoot":"","sources":["../../src/utils/php-parser.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AA6FF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAsF1E;AA4CD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAwC1E;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAwE3F;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,iBAAiB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AA8RF;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,eAAe,CAAM,GACxD,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,IAAI,CAqC1C"}
1
+ {"version":3,"file":"php-parser.d.ts","sourceRoot":"","sources":["../../src/utils/php-parser.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB,CAAC;AA6FF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAgG7F;AA4CD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAwC1E;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAwE3F;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,iBAAiB,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAgTF;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,eAAe,CAAM,GACxD,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,IAAI,CAqC1C"}
@@ -13,7 +13,7 @@ const parser = new PhpParser({
13
13
  php8: true,
14
14
  },
15
15
  ast: {
16
- withPositions: false,
16
+ withPositions: true,
17
17
  },
18
18
  });
19
19
  /**
@@ -108,7 +108,7 @@ function getStringValue(node) {
108
108
  * Parse PHP enum content and extract its definition.
109
109
  * This is a pure function that takes PHP source code as input.
110
110
  */
111
- export function parseEnumContent(phpContent) {
111
+ export function parseEnumContent(phpContent, filePath) {
112
112
  const ast = parsePhp(phpContent);
113
113
  if (!ast)
114
114
  return null;
@@ -118,6 +118,10 @@ export function parseEnumContent(phpContent) {
118
118
  return null;
119
119
  const name = typeof enumNode.name === 'string' ? enumNode.name : enumNode.name.name;
120
120
  const backing = enumNode.valueType ? enumNode.valueType.name.toLowerCase() : null;
121
+ // Capture enum location
122
+ const enumLoc = filePath && enumNode.loc?.start
123
+ ? { file: filePath, line: enumNode.loc.start.line, column: enumNode.loc.start.column }
124
+ : undefined;
121
125
  // Extract enum cases
122
126
  const cases = [];
123
127
  const enumCases = findAllNodesByKind(enumNode, 'enumcase');
@@ -147,7 +151,11 @@ export function parseEnumContent(phpContent) {
147
151
  else {
148
152
  value = key;
149
153
  }
150
- cases.push({ key, value });
154
+ // Capture case location
155
+ const caseLoc = filePath && enumCase.loc?.start
156
+ ? { file: filePath, line: enumCase.loc.start.line, column: enumCase.loc.start.column }
157
+ : undefined;
158
+ cases.push({ key, value, loc: caseLoc });
151
159
  }
152
160
  // Parse label() method if it exists
153
161
  const methods = findAllNodesByKind(enumNode, 'method');
@@ -186,7 +194,7 @@ export function parseEnumContent(phpContent) {
186
194
  }
187
195
  }
188
196
  }
189
- return { name, backing, cases };
197
+ return { name, backing, cases, loc: enumLoc };
190
198
  }
191
199
  /**
192
200
  * Extract key-value pairs from a PHP array node.
@@ -474,13 +482,20 @@ function inferTypeFromAstNode(node, key, options = {}) {
474
482
  if (resource === 'Collection') {
475
483
  return { type: 'any[]', optional };
476
484
  }
477
- // Resource::collection or Resource::make returns Resource[]
478
- if (method === 'collection' || method === 'make') {
485
+ // Resource::collection returns Resource[] (array of resources)
486
+ if (method === 'collection') {
479
487
  if (resourceExists(resource, resourcesDir)) {
480
488
  return { type: `${resource}[]`, optional };
481
489
  }
482
490
  return { type: 'any[]', optional };
483
491
  }
492
+ // Resource::make returns a single Resource
493
+ if (method === 'make') {
494
+ if (resourceExists(resource, resourcesDir)) {
495
+ return { type: resource, optional };
496
+ }
497
+ return { type: 'any', optional };
498
+ }
484
499
  }
485
500
  // Check if it's a whenLoaded call without a wrapper resource
486
501
  if (call.what.kind === 'propertylookup') {
@@ -572,6 +587,7 @@ function inferTypeFromAstNode(node, key, options = {}) {
572
587
  */
573
588
  function parseArrayEntries(items, options = {}) {
574
589
  const result = {};
590
+ const { filePath } = options;
575
591
  for (const item of items) {
576
592
  if (item.kind !== 'entry')
577
593
  continue;
@@ -582,6 +598,14 @@ function parseArrayEntries(items, options = {}) {
582
598
  if (!key)
583
599
  continue;
584
600
  const fieldInfo = inferTypeFromAstNode(entry.value, key, options);
601
+ // Capture field location from the entry key
602
+ if (filePath && entry.key.loc?.start) {
603
+ fieldInfo.loc = {
604
+ file: filePath,
605
+ line: entry.key.loc.start.line,
606
+ column: entry.key.loc.start.column,
607
+ };
608
+ }
585
609
  result[key] = { key, fieldInfo };
586
610
  // Handle nested arrays
587
611
  if (entry.value.kind === 'array') {
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Source map generation utilities.
3
+ * Implements the Source Map v3 format with VLQ encoding.
4
+ */
5
+ export type SourceMapping = {
6
+ generatedLine: number;
7
+ generatedColumn: number;
8
+ sourceLine: number;
9
+ sourceColumn: number;
10
+ name?: string;
11
+ };
12
+ export type SourceMapOptions = {
13
+ file: string;
14
+ sourceRoot?: string;
15
+ sources: string[];
16
+ sourcesContent?: (string | null)[];
17
+ names?: string[];
18
+ mappings: SourceMapping[];
19
+ };
20
+ /**
21
+ * Generate a source map JSON string.
22
+ */
23
+ export declare function generateSourceMap(options: SourceMapOptions): string;
24
+ /**
25
+ * Create the sourceMappingURL comment to append to generated files.
26
+ */
27
+ export declare function createSourceMapComment(mapFileName: string): string;
28
+ /**
29
+ * Calculate relative path from one file to another.
30
+ * Used to compute the source path relative to the generated .d.ts.map file.
31
+ */
32
+ export declare function relativePath(from: string, to: string): string;
33
+ //# sourceMappingURL=source-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-map.d.ts","sourceRoot":"","sources":["../../src/utils/source-map.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,aAAa,GAAG;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAqFF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAcnE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAmB7D"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Source map generation utilities.
3
+ * Implements the Source Map v3 format with VLQ encoding.
4
+ */
5
+ const BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
6
+ /**
7
+ * Encode a single number as VLQ (Variable Length Quantity).
8
+ */
9
+ function encodeVLQ(value) {
10
+ let result = '';
11
+ let vlq = value < 0 ? (-value << 1) | 1 : value << 1;
12
+ do {
13
+ let digit = vlq & 0x1f;
14
+ vlq >>>= 5;
15
+ if (vlq > 0) {
16
+ digit |= 0x20; // continuation bit
17
+ }
18
+ result += BASE64_CHARS[digit];
19
+ } while (vlq > 0);
20
+ return result;
21
+ }
22
+ /**
23
+ * Encode a source map segment.
24
+ * Each segment contains: generatedColumn, sourceIndex, sourceLine, sourceColumn, [nameIndex]
25
+ */
26
+ function encodeSegment(generatedColumn, sourceIndex, sourceLine, sourceColumn, prevState) {
27
+ const result = encodeVLQ(generatedColumn - prevState.genCol) +
28
+ encodeVLQ(sourceIndex - prevState.srcIdx) +
29
+ encodeVLQ(sourceLine - prevState.srcLine) +
30
+ encodeVLQ(sourceColumn - prevState.srcCol);
31
+ prevState.genCol = generatedColumn;
32
+ prevState.srcIdx = sourceIndex;
33
+ prevState.srcLine = sourceLine;
34
+ prevState.srcCol = sourceColumn;
35
+ return result;
36
+ }
37
+ /**
38
+ * Generate VLQ-encoded mappings string from mapping entries.
39
+ */
40
+ function encodeMappings(mappings) {
41
+ if (mappings.length === 0)
42
+ return '';
43
+ // Sort by generated line, then column
44
+ const sorted = [...mappings].sort((a, b) => {
45
+ if (a.generatedLine !== b.generatedLine)
46
+ return a.generatedLine - b.generatedLine;
47
+ return a.generatedColumn - b.generatedColumn;
48
+ });
49
+ const lines = [];
50
+ const state = { genCol: 0, srcIdx: 0, srcLine: 0, srcCol: 0 };
51
+ for (const mapping of sorted) {
52
+ // Ensure we have enough lines
53
+ while (lines.length < mapping.generatedLine) {
54
+ lines.push([]);
55
+ state.genCol = 0; // Reset column at start of each line
56
+ }
57
+ const lineIndex = mapping.generatedLine - 1;
58
+ const segment = encodeSegment(mapping.generatedColumn, 0, // sourceIndex - always 0 since we have one source file per map
59
+ mapping.sourceLine - 1, // 0-indexed in source maps
60
+ mapping.sourceColumn, state);
61
+ lines[lineIndex].push(segment);
62
+ }
63
+ return lines.map((segments) => segments.join(',')).join(';');
64
+ }
65
+ /**
66
+ * Generate a source map JSON string.
67
+ */
68
+ export function generateSourceMap(options) {
69
+ const { file, sourceRoot = '', sources, sourcesContent, names = [], mappings } = options;
70
+ const map = {
71
+ version: 3,
72
+ file,
73
+ sourceRoot,
74
+ sources,
75
+ ...(sourcesContent ? { sourcesContent } : {}),
76
+ names,
77
+ mappings: encodeMappings(mappings),
78
+ };
79
+ return JSON.stringify(map);
80
+ }
81
+ /**
82
+ * Create the sourceMappingURL comment to append to generated files.
83
+ */
84
+ export function createSourceMapComment(mapFileName) {
85
+ return `//# sourceMappingURL=${mapFileName}`;
86
+ }
87
+ /**
88
+ * Calculate relative path from one file to another.
89
+ * Used to compute the source path relative to the generated .d.ts.map file.
90
+ */
91
+ export function relativePath(from, to) {
92
+ const fromParts = from.split('/').slice(0, -1); // Remove filename
93
+ const toParts = to.split('/');
94
+ // Find common prefix
95
+ let commonLength = 0;
96
+ while (commonLength < fromParts.length &&
97
+ commonLength < toParts.length &&
98
+ fromParts[commonLength] === toParts[commonLength]) {
99
+ commonLength++;
100
+ }
101
+ // Build relative path
102
+ const ups = fromParts.length - commonLength;
103
+ const remaining = toParts.slice(commonLength);
104
+ return [...Array(ups).fill('..'), ...remaining].join('/');
105
+ }
@@ -5,7 +5,7 @@ import { logError, logFileChange, logRegeneration } from '../utils/banner.js';
5
5
  * Set up a watcher for enum files.
6
6
  */
7
7
  export function setupEnumWatcher(options) {
8
- const { enumsDir, outputDir, packageName, server } = options;
8
+ const { enumsDir, outputDir, packageName, cwd, server } = options;
9
9
  const enumPattern = join(enumsDir, '*.php');
10
10
  const generatedJsPath = join(outputDir, 'index.js');
11
11
  // Watch PHP enum files
@@ -17,7 +17,7 @@ export function setupEnumWatcher(options) {
17
17
  try {
18
18
  logFileChange('enums', basename(filePath));
19
19
  // Regenerate enum files
20
- generateEnums({ enumsDir, outputDir, packageName });
20
+ generateEnums({ enumsDir, outputDir, packageName, cwd });
21
21
  // Tell Vite the generated file changed (triggers normal HMR)
22
22
  server.watcher.emit('change', generatedJsPath);
23
23
  logRegeneration('enums');
@@ -5,7 +5,7 @@ import { logError, logFileChange, logRegeneration } from '../utils/banner.js';
5
5
  * Set up a watcher for resource and model files.
6
6
  */
7
7
  export function setupResourceWatcher(options) {
8
- const { resourcesDir, enumsDir, modelsDir, outputDir, packageName, server } = options;
8
+ const { resourcesDir, enumsDir, modelsDir, outputDir, packageName, cwd, server } = options;
9
9
  const resourcePattern = join(resourcesDir, '*.php');
10
10
  const modelPattern = join(modelsDir, '*.php');
11
11
  const generatedDtsPath = join(outputDir, 'index.d.ts');
@@ -21,7 +21,7 @@ export function setupResourceWatcher(options) {
21
21
  const fileType = isModel ? 'model' : 'resource';
22
22
  logFileChange(fileType, basename(filePath));
23
23
  // Regenerate resource types
24
- generateResources({ resourcesDir, enumsDir, modelsDir, outputDir, packageName });
24
+ generateResources({ resourcesDir, enumsDir, modelsDir, outputDir, packageName, cwd });
25
25
  // Tell Vite the generated type file changed
26
26
  // TypeScript will pick up changes automatically
27
27
  server.watcher.emit('change', generatedDtsPath);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "prettier": "@aniftyco/prettier",
4
4
  "description": "Ferries Laravel types to your TypeScript frontend",
5
5
  "type": "module",
6
- "version": "0.1.5",
6
+ "version": "0.2.0",
7
7
  "repository": "https://github.com/aniftyco/vite-plugin-ferry",
8
8
  "main": "dist/index.js",
9
9
  "types": "dist/index.d.ts",