@translation-cms/sync 1.2.28 → 1.2.29

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.
Files changed (197) hide show
  1. package/dist/api.d.ts +105 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/api.js +208 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/bin.d.ts +25 -0
  6. package/dist/bin.d.ts.map +1 -0
  7. package/dist/bin.js +208 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/commands/init.d.ts +10 -0
  10. package/dist/commands/init.d.ts.map +1 -0
  11. package/dist/commands/init.js +107 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/pull.d.ts +10 -0
  14. package/dist/commands/pull.d.ts.map +1 -0
  15. package/dist/commands/pull.js +35 -0
  16. package/dist/commands/pull.js.map +1 -0
  17. package/dist/commands/status.d.ts +15 -0
  18. package/dist/commands/status.d.ts.map +1 -0
  19. package/dist/commands/status.js +62 -0
  20. package/dist/commands/status.js.map +1 -0
  21. package/dist/commands/sync.d.ts +24 -0
  22. package/dist/commands/sync.d.ts.map +1 -0
  23. package/dist/commands/sync.js +131 -0
  24. package/dist/commands/sync.js.map +1 -0
  25. package/dist/commands/watch.d.ts +18 -0
  26. package/dist/commands/watch.d.ts.map +1 -0
  27. package/dist/commands/watch.js +71 -0
  28. package/dist/commands/watch.js.map +1 -0
  29. package/dist/config/config-internals/args.d.ts +11 -0
  30. package/dist/config/config-internals/args.d.ts.map +1 -0
  31. package/dist/config/config-internals/args.js +22 -0
  32. package/dist/config/config-internals/args.js.map +1 -0
  33. package/dist/config/config-internals/env.d.ts +10 -0
  34. package/dist/config/config-internals/env.d.ts.map +1 -0
  35. package/dist/config/config-internals/env.js +35 -0
  36. package/dist/config/config-internals/env.js.map +1 -0
  37. package/dist/config/config-internals/file.d.ts +11 -0
  38. package/dist/config/config-internals/file.d.ts.map +1 -0
  39. package/dist/config/config-internals/file.js +28 -0
  40. package/dist/config/config-internals/file.js.map +1 -0
  41. package/dist/config/config-internals/resolve.d.ts +21 -0
  42. package/dist/config/config-internals/resolve.d.ts.map +1 -0
  43. package/dist/config/config-internals/resolve.js +73 -0
  44. package/dist/config/config-internals/resolve.js.map +1 -0
  45. package/dist/config/config-internals/root.d.ts +9 -0
  46. package/dist/config/config-internals/root.d.ts.map +1 -0
  47. package/dist/config/config-internals/root.js +22 -0
  48. package/dist/config/config-internals/root.js.map +1 -0
  49. package/dist/config/config-internals/types.d.ts +91 -0
  50. package/dist/config/config-internals/types.d.ts.map +1 -0
  51. package/dist/config/config-internals/types.js +9 -0
  52. package/dist/config/config-internals/types.js.map +1 -0
  53. package/dist/config/resolve-config.d.ts +11 -0
  54. package/dist/config/resolve-config.d.ts.map +1 -0
  55. package/dist/config/resolve-config.js +10 -0
  56. package/dist/config/resolve-config.js.map +1 -0
  57. package/dist/core/api-internals/helpers.d.ts +9 -0
  58. package/dist/core/api-internals/helpers.d.ts.map +1 -0
  59. package/dist/core/api-internals/helpers.js +14 -0
  60. package/dist/core/api-internals/helpers.js.map +1 -0
  61. package/dist/core/api-internals/pull.d.ts +19 -0
  62. package/dist/core/api-internals/pull.d.ts.map +1 -0
  63. package/dist/core/api-internals/pull.js +269 -0
  64. package/dist/core/api-internals/pull.js.map +1 -0
  65. package/dist/core/api-internals/route-config.d.ts +13 -0
  66. package/dist/core/api-internals/route-config.d.ts.map +1 -0
  67. package/dist/core/api-internals/route-config.js +34 -0
  68. package/dist/core/api-internals/route-config.js.map +1 -0
  69. package/dist/core/api-internals/sync.d.ts +19 -0
  70. package/dist/core/api-internals/sync.d.ts.map +1 -0
  71. package/dist/core/api-internals/sync.js +139 -0
  72. package/dist/core/api-internals/sync.js.map +1 -0
  73. package/dist/core/api-internals/types.d.ts +33 -0
  74. package/dist/core/api-internals/types.d.ts.map +1 -0
  75. package/dist/core/api-internals/types.js +5 -0
  76. package/dist/core/api-internals/types.js.map +1 -0
  77. package/dist/core/api.d.ts +11 -0
  78. package/dist/core/api.d.ts.map +1 -0
  79. package/dist/core/api.js +11 -0
  80. package/dist/core/api.js.map +1 -0
  81. package/dist/core/cache-internals/format.d.ts +20 -0
  82. package/dist/core/cache-internals/format.d.ts.map +1 -0
  83. package/dist/core/cache-internals/format.js +33 -0
  84. package/dist/core/cache-internals/format.js.map +1 -0
  85. package/dist/core/cache-internals/params.d.ts +33 -0
  86. package/dist/core/cache-internals/params.d.ts.map +1 -0
  87. package/dist/core/cache-internals/params.js +155 -0
  88. package/dist/core/cache-internals/params.js.map +1 -0
  89. package/dist/core/cache-internals/pull.d.ts +17 -0
  90. package/dist/core/cache-internals/pull.d.ts.map +1 -0
  91. package/dist/core/cache-internals/pull.js +34 -0
  92. package/dist/core/cache-internals/pull.js.map +1 -0
  93. package/dist/core/cache-internals/sync.d.ts +29 -0
  94. package/dist/core/cache-internals/sync.d.ts.map +1 -0
  95. package/dist/core/cache-internals/sync.js +104 -0
  96. package/dist/core/cache-internals/sync.js.map +1 -0
  97. package/dist/core/cache-internals/types.d.ts +48 -0
  98. package/dist/core/cache-internals/types.d.ts.map +1 -0
  99. package/dist/core/cache-internals/types.js +2 -0
  100. package/dist/core/cache-internals/types.js.map +1 -0
  101. package/dist/core/cache.d.ts +13 -0
  102. package/dist/core/cache.d.ts.map +1 -0
  103. package/dist/core/cache.js +33 -0
  104. package/dist/core/cache.js.map +1 -0
  105. package/dist/core/scanner-internals/ast.d.ts +40 -0
  106. package/dist/core/scanner-internals/ast.d.ts.map +1 -0
  107. package/dist/core/scanner-internals/ast.js +100 -0
  108. package/dist/core/scanner-internals/ast.js.map +1 -0
  109. package/dist/core/scanner-internals/file-walker.d.ts +10 -0
  110. package/dist/core/scanner-internals/file-walker.d.ts.map +1 -0
  111. package/dist/core/scanner-internals/file-walker.js +23 -0
  112. package/dist/core/scanner-internals/file-walker.js.map +1 -0
  113. package/dist/core/scanner-internals/import-resolver.d.ts +21 -0
  114. package/dist/core/scanner-internals/import-resolver.d.ts.map +1 -0
  115. package/dist/core/scanner-internals/import-resolver.js +119 -0
  116. package/dist/core/scanner-internals/import-resolver.js.map +1 -0
  117. package/dist/core/scanner-internals/key-extractor.d.ts +20 -0
  118. package/dist/core/scanner-internals/key-extractor.d.ts.map +1 -0
  119. package/dist/core/scanner-internals/key-extractor.js +274 -0
  120. package/dist/core/scanner-internals/key-extractor.js.map +1 -0
  121. package/dist/core/scanner-internals/nav-config-scanner.d.ts +30 -0
  122. package/dist/core/scanner-internals/nav-config-scanner.d.ts.map +1 -0
  123. package/dist/core/scanner-internals/nav-config-scanner.js +170 -0
  124. package/dist/core/scanner-internals/nav-config-scanner.js.map +1 -0
  125. package/dist/core/scanner-internals/route-detector.d.ts +27 -0
  126. package/dist/core/scanner-internals/route-detector.d.ts.map +1 -0
  127. package/dist/core/scanner-internals/route-detector.js +78 -0
  128. package/dist/core/scanner-internals/route-detector.js.map +1 -0
  129. package/dist/core/scanner-internals/types.d.ts +60 -0
  130. package/dist/core/scanner-internals/types.d.ts.map +1 -0
  131. package/dist/core/scanner-internals/types.js +29 -0
  132. package/dist/core/scanner-internals/types.js.map +1 -0
  133. package/dist/core/scanner.d.ts +21 -0
  134. package/dist/core/scanner.d.ts.map +1 -0
  135. package/dist/core/scanner.js +162 -0
  136. package/dist/core/scanner.js.map +1 -0
  137. package/dist/index.d.ts +3 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +3 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/next.d.ts +33 -0
  142. package/dist/next.d.ts.map +1 -0
  143. package/dist/next.js +110 -0
  144. package/dist/next.js.map +1 -0
  145. package/dist/preview/index.d.ts +39 -0
  146. package/dist/preview/index.d.ts.map +1 -0
  147. package/dist/preview/index.js +123 -0
  148. package/dist/preview/index.js.map +1 -0
  149. package/dist/preview/internals/highlight.d.ts +31 -0
  150. package/dist/preview/internals/highlight.d.ts.map +1 -0
  151. package/dist/preview/internals/highlight.js +184 -0
  152. package/dist/preview/internals/highlight.js.map +1 -0
  153. package/dist/preview/internals/interactions.d.ts +15 -0
  154. package/dist/preview/internals/interactions.d.ts.map +1 -0
  155. package/dist/preview/internals/interactions.js +38 -0
  156. package/dist/preview/internals/interactions.js.map +1 -0
  157. package/dist/preview/internals/interactive.d.ts +14 -0
  158. package/dist/preview/internals/interactive.d.ts.map +1 -0
  159. package/dist/preview/internals/interactive.js +122 -0
  160. package/dist/preview/internals/interactive.js.map +1 -0
  161. package/dist/preview/internals/locales.d.ts +9 -0
  162. package/dist/preview/internals/locales.d.ts.map +1 -0
  163. package/dist/preview/internals/locales.js +24 -0
  164. package/dist/preview/internals/locales.js.map +1 -0
  165. package/dist/preview/internals/state.d.ts +37 -0
  166. package/dist/preview/internals/state.d.ts.map +1 -0
  167. package/dist/preview/internals/state.js +74 -0
  168. package/dist/preview/internals/state.js.map +1 -0
  169. package/dist/preview/internals/styles.d.ts +8 -0
  170. package/dist/preview/internals/styles.d.ts.map +1 -0
  171. package/dist/preview/internals/styles.js +28 -0
  172. package/dist/preview/internals/styles.js.map +1 -0
  173. package/dist/preview/internals/types.d.ts +74 -0
  174. package/dist/preview/internals/types.d.ts.map +1 -0
  175. package/dist/preview/internals/types.js +5 -0
  176. package/dist/preview/internals/types.js.map +1 -0
  177. package/dist/preview/internals/ui-indicators.d.ts +15 -0
  178. package/dist/preview/internals/ui-indicators.d.ts.map +1 -0
  179. package/dist/preview/internals/ui-indicators.js +92 -0
  180. package/dist/preview/internals/ui-indicators.js.map +1 -0
  181. package/dist/scaffold/index.d.ts +3 -0
  182. package/dist/scaffold/index.d.ts.map +1 -0
  183. package/dist/scaffold/index.js +3 -0
  184. package/dist/scaffold/index.js.map +1 -0
  185. package/dist/scaffold/intenrals/scaffold.d.ts +24 -0
  186. package/dist/scaffold/intenrals/scaffold.d.ts.map +1 -0
  187. package/dist/scaffold/intenrals/scaffold.js +53 -0
  188. package/dist/scaffold/intenrals/scaffold.js.map +1 -0
  189. package/dist/scaffold/intenrals/templates.d.ts +10 -0
  190. package/dist/scaffold/intenrals/templates.d.ts.map +1 -0
  191. package/dist/scaffold/intenrals/templates.js +344 -0
  192. package/dist/scaffold/intenrals/templates.js.map +1 -0
  193. package/dist/scaffold/intenrals/types.d.ts +14 -0
  194. package/dist/scaffold/intenrals/types.d.ts.map +1 -0
  195. package/dist/scaffold/intenrals/types.js +5 -0
  196. package/dist/scaffold/intenrals/types.js.map +1 -0
  197. package/package.json +3 -3
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Navigation config scanner.
3
+ * Extracts href values from navigation configuration files and returns them
4
+ * as preview routes that can be merged into the translation key map.
5
+ *
6
+ * Looks for patterns like:
7
+ * href: `/projects/${id}/settings`
8
+ * href: '/admin/users'
9
+ * href: `/o/${orgId}/projects`
10
+ */
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+ import { parseSource, walk } from './ast.js';
14
+ /**
15
+ * Finds navigation config files in the project.
16
+ * Matches files with patterns like:
17
+ * - **/navigation.config.ts(x)
18
+ * - **/navigation*.ts(x) in config/ directories
19
+ * - **/routes.config.ts(x)
20
+ * - **/app-routes.ts(x)
21
+ */
22
+ export function findNavConfigFiles(root, sourceExtensions) {
23
+ const navFiles = [];
24
+ const navPatterns = [
25
+ /navigation.*config\.[jt]sx?$/i,
26
+ /routes\.config\.[jt]sx?$/i,
27
+ ];
28
+ function walk(dir) {
29
+ try {
30
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
31
+ for (const entry of entries) {
32
+ const fullPath = path.join(dir, entry.name);
33
+ if (entry.isDirectory()) {
34
+ // Skip excluded directories
35
+ if ([
36
+ 'node_modules',
37
+ '.next',
38
+ 'dist',
39
+ '.git',
40
+ 'out',
41
+ ].includes(entry.name)) {
42
+ continue;
43
+ }
44
+ walk(fullPath);
45
+ }
46
+ else if (entry.isFile()) {
47
+ const ext = path.extname(entry.name);
48
+ if (!sourceExtensions.has(ext))
49
+ continue;
50
+ // Check if filename matches expected patterns
51
+ if (navPatterns.some(p => p.test(entry.name))) {
52
+ navFiles.push(fullPath);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ catch {
58
+ // Silently skip unreadable directories
59
+ }
60
+ }
61
+ walk(root);
62
+ return navFiles;
63
+ }
64
+ /**
65
+ * Extract href values from a navigation config file.
66
+ * Returns an array of href strings/routes found in the config.
67
+ */
68
+ export function extractNavRoutes(filePath) {
69
+ try {
70
+ const content = fs.readFileSync(filePath, 'utf-8');
71
+ const ast = parseSource(content, filePath);
72
+ const routes = new Set();
73
+ // Walk the AST looking for ObjectProperty nodes with key "href" or "path"
74
+ walk(ast, (node) => {
75
+ if (!node || node.type !== 'ObjectProperty')
76
+ return;
77
+ const objProp = node;
78
+ const key = objProp.key;
79
+ const keyName = key?.name ?? '';
80
+ if (keyName === 'href' || keyName === 'path') {
81
+ const value = objProp.value;
82
+ if (!value)
83
+ return;
84
+ // String literal: href: "/path"
85
+ if (value.type === 'StringLiteral') {
86
+ const route = value.value;
87
+ if (route && route.startsWith('/')) {
88
+ routes.add(route);
89
+ }
90
+ }
91
+ // Template literal: href: `/path/${id}`
92
+ else if (value.type === 'TemplateLiteral') {
93
+ const route = reconstructTemplatePath(value);
94
+ if (route && route.startsWith('/')) {
95
+ routes.add(route);
96
+ }
97
+ }
98
+ }
99
+ });
100
+ return Array.from(routes);
101
+ }
102
+ catch (err) {
103
+ console.warn(`[nav-config] Failed to parse ${path.basename(filePath)}: ${err instanceof Error ? err.message : 'unknown error'}`);
104
+ return [];
105
+ }
106
+ }
107
+ /**
108
+ * Reconstruct a template path by replacing expressions with parameter placeholders.
109
+ * Example: `${orgId}/projects/${projectId}` → `/[orgId]/projects/[projectId]`
110
+ */
111
+ function reconstructTemplatePath(node) {
112
+ if (node.type !== 'TemplateLiteral')
113
+ return '';
114
+ let result = '';
115
+ const quasis = node.quasis ?? [];
116
+ const expressions = node.expressions ?? [];
117
+ for (let i = 0; i < quasis.length; i++) {
118
+ const quasi = quasis[i];
119
+ const value = quasi.value;
120
+ const cooked = value?.cooked ?? '';
121
+ result += cooked;
122
+ if (i < expressions.length) {
123
+ const expr = expressions[i];
124
+ const varName = extractVariableName(expr);
125
+ if (varName) {
126
+ result += `[${varName}]`;
127
+ }
128
+ else {
129
+ result += '[param]';
130
+ }
131
+ }
132
+ }
133
+ return result;
134
+ }
135
+ /**
136
+ * Extract the variable name from an expression node.
137
+ * Handles simple identifiers like `orgId`, `projectId`, etc.
138
+ */
139
+ function extractVariableName(node) {
140
+ if (!node)
141
+ return null;
142
+ const expr = node;
143
+ if (expr.type === 'Identifier')
144
+ return expr.name;
145
+ return null;
146
+ }
147
+ /**
148
+ * Scan navigation config files and return preview routes for all hrefs found.
149
+ * Returns a map of route patterns to their string arrays.
150
+ */
151
+ export function scanNavConfig(root, sourceExtensions) {
152
+ const navFiles = findNavConfigFiles(root, sourceExtensions);
153
+ if (navFiles.length === 0) {
154
+ return {};
155
+ }
156
+ console.log(`[nav-config] Found ${navFiles.length} navigation config file(s)`);
157
+ const allRoutes = new Map();
158
+ for (const filePath of navFiles) {
159
+ const routes = extractNavRoutes(filePath);
160
+ console.log(`[nav-config] ${path.relative(root, filePath)}: extracted ${routes.length} route(s)`);
161
+ routes.forEach(r => allRoutes.set(r, true));
162
+ }
163
+ // Convert to the format expected: route → routes array
164
+ const result = {};
165
+ if (allRoutes.size > 0) {
166
+ result['__NAV_CONFIG__'] = Array.from(allRoutes.keys());
167
+ }
168
+ return result;
169
+ }
170
+ //# sourceMappingURL=nav-config-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav-config-scanner.js","sourceRoot":"","sources":["../../../src/core/scanner-internals/nav-config-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Route detection from Next.js file paths.
3
+ * Maps source files (page.tsx, layout.tsx) to their corresponding route strings.
4
+ */
5
+ /**
6
+ * Returns true when a page file only calls `redirect()` without rendering any JSX.
7
+ *
8
+ * Such pages have no visible UI, so they are excluded from the preview route list —
9
+ * there is nothing for a translator to preview.
10
+ */
11
+ export declare function isRedirectOnlyPage(filePath: string): boolean;
12
+ /**
13
+ * Map a source file path to the Next.js route string(s) it represents.
14
+ *
15
+ * Rules:
16
+ * - Only `page.[jt]sx?` files produce a route; all other files return `[]`.
17
+ * - Pages that only call `redirect()` (no JSX) return `[]` — no UI to preview.
18
+ * - Route groups `(groupName)/` are stripped from the route path.
19
+ * - Dynamic segments `[param]` are kept as-is; they are filled in at preview
20
+ * time using the project's `preview_params` configuration.
21
+ *
22
+ * @example
23
+ * filePathToRoute(root, 'src/app/[locale]/blog/[slug]/page.tsx')
24
+ * // → ['/[locale]/blog/[slug]']
25
+ */
26
+ export declare function filePathToRoute(root: string, filePath: string): string[];
27
+ //# sourceMappingURL=route-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-detector.d.ts","sourceRoot":"","sources":["../../../src/core/scanner-internals/route-detector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAoC5D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAgBxE"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Route detection from Next.js file paths.
3
+ * Maps source files (page.tsx, layout.tsx) to their corresponding route strings.
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { parseSource, walk } from './ast.js';
8
+ /**
9
+ * Returns true when a page file only calls `redirect()` without rendering any JSX.
10
+ *
11
+ * Such pages have no visible UI, so they are excluded from the preview route list —
12
+ * there is nothing for a translator to preview.
13
+ */
14
+ export function isRedirectOnlyPage(filePath) {
15
+ let ast;
16
+ try {
17
+ // Single try/catch covers both file read and parse
18
+ ast = parseSource(filePath, fs.readFileSync(filePath, 'utf-8'));
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ let hasRedirect = false;
24
+ let hasJsxReturn = false;
25
+ walk(ast, node => {
26
+ // Detect redirect() calls
27
+ if (node.type === 'CallExpression') {
28
+ const callee = node.callee;
29
+ if (callee.type === 'Identifier' && callee.name === 'redirect')
30
+ hasRedirect = true;
31
+ return;
32
+ }
33
+ // Detect return statements that yield JSX (page has visible UI)
34
+ if (node.type !== 'ReturnStatement')
35
+ return;
36
+ const arg = node.argument;
37
+ if (!arg)
38
+ return;
39
+ // Unwrap parenthesised expressions: return (<div>…</div>)
40
+ const expr = arg.type === 'ParenthesizedExpression'
41
+ ? arg.expression
42
+ : arg;
43
+ if (expr?.type === 'JSXElement' || expr?.type === 'JSXFragment')
44
+ hasJsxReturn = true;
45
+ });
46
+ return hasRedirect && !hasJsxReturn;
47
+ }
48
+ /**
49
+ * Map a source file path to the Next.js route string(s) it represents.
50
+ *
51
+ * Rules:
52
+ * - Only `page.[jt]sx?` files produce a route; all other files return `[]`.
53
+ * - Pages that only call `redirect()` (no JSX) return `[]` — no UI to preview.
54
+ * - Route groups `(groupName)/` are stripped from the route path.
55
+ * - Dynamic segments `[param]` are kept as-is; they are filled in at preview
56
+ * time using the project's `preview_params` configuration.
57
+ *
58
+ * @example
59
+ * filePathToRoute(root, 'src/app/[locale]/blog/[slug]/page.tsx')
60
+ * // → ['/[locale]/blog/[slug]']
61
+ */
62
+ export function filePathToRoute(root, filePath) {
63
+ // Only page files map to routes; layouts, components, hooks, etc. do not
64
+ if (path.basename(filePath, path.extname(filePath)) !== 'page')
65
+ return [];
66
+ // Skip redirect-only pages — no UI to preview
67
+ if (isRedirectOnlyPage(filePath))
68
+ return [];
69
+ const rel = path.relative(root, filePath).replace(/\\/g, '/');
70
+ const appMatch = rel.match(/^(?:src\/)?app\/(.*?)page\.[jt]sx?$/);
71
+ if (!appMatch)
72
+ return [];
73
+ let routePath = appMatch[1] ?? '';
74
+ routePath = routePath.replace(/\([^)]+\)\//g, ''); // strip (routeGroup)/ segments
75
+ routePath = routePath.replace(/\/$/, ''); // strip trailing slash
76
+ return ['/' + routePath];
77
+ }
78
+ //# sourceMappingURL=route-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-detector.js","sourceRoot":"","sources":["../../../src/core/scanner-internals/route-detector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAG7C;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IAC/C,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACD,mDAAmD;QACnD,GAAG,GAAG,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE;QACb,0BAA0B;QAC1B,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAiB,CAAC;YACtC,IAAI,MAAM,CAAC,IAAI,KAAK,YAAY,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU;gBAC1D,WAAW,GAAG,IAAI,CAAC;YACvB,OAAO;QACX,CAAC;QAED,gEAAgE;QAChE,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB;YAAE,OAAO;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAsC,CAAC;QACxD,IAAI,CAAC,GAAG;YAAE,OAAO;QAEjB,0DAA0D;QAC1D,MAAM,IAAI,GACN,GAAG,CAAC,IAAI,KAAK,yBAAyB;YAClC,CAAC,CAAE,GAAG,CAAC,UAAsB;YAC7B,CAAC,CAAC,GAAG,CAAC;QACd,IAAI,IAAI,EAAE,IAAI,KAAK,YAAY,IAAI,IAAI,EAAE,IAAI,KAAK,aAAa;YAC3D,YAAY,GAAG,IAAI,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,OAAO,WAAW,IAAI,CAAC,YAAY,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,QAAgB;IAC1D,yEAAyE;IACzE,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,KAAK,MAAM;QAAE,OAAO,EAAE,CAAC;IAE1E,8CAA8C;IAC9C,IAAI,kBAAkB,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAE5C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAClE,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IAEzB,IAAI,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,CAAC,+BAA+B;IAClF,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,uBAAuB;IAEjE,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Types and constants used throughout the scanner module.
3
+ */
4
+ export declare const DEFAULT_EXCLUDED_DIRS: Set<string>;
5
+ export declare const DEFAULT_SOURCE_EXTENSIONS: Set<string>;
6
+ /** Options that control the file-system scan behaviour. */
7
+ export interface ScanOptions {
8
+ /**
9
+ * Extra directories to exclude on top of the built-in defaults.
10
+ * @example ["e2e", "fixtures"]
11
+ */
12
+ excludedDirs?: string[];
13
+ /**
14
+ * Override the full set of file extensions to scan.
15
+ * When omitted the defaults (.ts .tsx .js .jsx) are used.
16
+ */
17
+ sourceExtensions?: string[];
18
+ /**
19
+ * External preview routes from database or config (namespace:key → routes[])
20
+ * These are applied in Pass 3.
21
+ * Keys are route patterns, values are mock params for dynamic segments.
22
+ * @example
23
+ * {
24
+ * "/[locale]/blog/[slug]": { "slug": "first-post" },
25
+ * "/[locale]/products/[slug]": { "slug": "product-a" }
26
+ * }
27
+ */
28
+ routeParams?: Record<string, Record<string, string>>;
29
+ /**
30
+ * TypeScript path mappings from tsconfig.json (for resolving aliased imports).
31
+ * @example { "@/*": ["./src/*"] }
32
+ */
33
+ pathMappings?: Record<string, string[]>;
34
+ /**
35
+ * Project root directory (used for resolving path aliases).
36
+ * Defaults to the root directory passed to scanProject.
37
+ */
38
+ projectRoot?: string;
39
+ /**
40
+ * Include navigation config files when scanning for preview routes.
41
+ * When enabled, hrefs from navigation.config.ts files are automatically
42
+ * discovered and added as preview routes.
43
+ * @default false
44
+ */
45
+ includeNavRoutes?: boolean;
46
+ }
47
+ /** A preview route — either a plain path string or an object with per-route params. */
48
+ export type PreviewRoute = string | {
49
+ route: string;
50
+ params?: Record<string, string>;
51
+ };
52
+ /** namespace → (key → previewRoutes[]) */
53
+ export type NamespaceMap = Record<string, Map<string, PreviewRoute[]>>;
54
+ /** Return the route string key used for deduplication. */
55
+ export declare function routeKey(r: PreviewRoute): string;
56
+ /** AST node type for type-safe walking. */
57
+ export type AstNode = {
58
+ type: string;
59
+ } & Record<string, unknown>;
60
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/scanner-internals/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,eAAO,MAAM,qBAAqB,aAWhC,CAAC;AAEH,eAAO,MAAM,yBAAyB,aAKpC,CAAC;AAMH,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACxC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,uFAAuF;AACvF,MAAM,MAAM,YAAY,GAClB,MAAM,GACN;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAEzD,0CAA0C;AAC1C,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;AAEvE,0DAA0D;AAC1D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAEhD;AAED,2CAA2C;AAC3C,MAAM,MAAM,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Types and constants used throughout the scanner module.
3
+ */
4
+ // ---------------------------------------------------------------------------
5
+ // Constants
6
+ // ---------------------------------------------------------------------------
7
+ export const DEFAULT_EXCLUDED_DIRS = new Set([
8
+ 'node_modules',
9
+ '.next',
10
+ 'dist',
11
+ '.git',
12
+ 'out',
13
+ '.turbo',
14
+ 'scripts',
15
+ 'packages',
16
+ '.storybook',
17
+ 'coverage',
18
+ ]);
19
+ export const DEFAULT_SOURCE_EXTENSIONS = new Set([
20
+ '.ts',
21
+ '.tsx',
22
+ '.js',
23
+ '.jsx',
24
+ ]);
25
+ /** Return the route string key used for deduplication. */
26
+ export function routeKey(r) {
27
+ return typeof r === 'string' ? r : r.route;
28
+ }
29
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/scanner-internals/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACzC,cAAc;IACd,OAAO;IACP,MAAM;IACN,MAAM;IACN,KAAK;IACL,QAAQ;IACR,SAAS;IACT,UAAU;IACV,YAAY;IACZ,UAAU;CACb,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC;IAC7C,KAAK;IACL,MAAM;IACN,KAAK;IACL,MAAM;CACT,CAAC,CAAC;AAwDH,0DAA0D;AAC1D,MAAM,UAAU,QAAQ,CAAC,CAAe;IACpC,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Main scanner orchestration.
3
+ * Coordinates the multi-pass scanning process for translation keys.
4
+ * Re-exports public types and functions for backward compatibility.
5
+ */
6
+ export { DEFAULT_EXCLUDED_DIRS, DEFAULT_SOURCE_EXTENSIONS, type ScanOptions, type PreviewRoute, type NamespaceMap, routeKey, } from './scanner-internals/types.js';
7
+ export { walkFiles } from './scanner-internals/file-walker.js';
8
+ export { isRedirectOnlyPage, filePathToRoute, } from './scanner-internals/route-detector.js';
9
+ export { extractKeysFromFile } from './scanner-internals/key-extractor.js';
10
+ import { type ScanOptions, type NamespaceMap } from './scanner-internals/types.js';
11
+ /**
12
+ * Scans the project for translation keys and optionally merges external preview routes.
13
+ *
14
+ * Pass 1: Scan all files, extracting keys and associating them with page/layout routes.
15
+ * Pass 2: For each page/layout, walk its import tree to assign keys in deeply-nested
16
+ * components to the correct preview route.
17
+ * Pass 3a: Optionally scan navigation config files and merge their routes (if includeNavRoutes is true).
18
+ * Pass 3b: Merge external preview routes from database or config (routeParams).
19
+ */
20
+ export declare function scanProject(root: string, opts?: ScanOptions): NamespaceMap;
21
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EACH,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,QAAQ,GACX,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,EACH,kBAAkB,EAClB,eAAe,GAClB,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAG3E,OAAO,EAGH,KAAK,WAAW,EAChB,KAAK,YAAY,EAEpB,MAAM,8BAA8B,CAAC;AA0CtC;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,YAAY,CAmI1E"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Main scanner orchestration.
3
+ * Coordinates the multi-pass scanning process for translation keys.
4
+ * Re-exports public types and functions for backward compatibility.
5
+ */
6
+ import path from 'path';
7
+ // Re-export types and constants
8
+ export { DEFAULT_EXCLUDED_DIRS, DEFAULT_SOURCE_EXTENSIONS, routeKey, } from './scanner-internals/types.js';
9
+ // Re-export public functions from submodules
10
+ export { walkFiles } from './scanner-internals/file-walker.js';
11
+ export { isRedirectOnlyPage, filePathToRoute, } from './scanner-internals/route-detector.js';
12
+ export { extractKeysFromFile } from './scanner-internals/key-extractor.js';
13
+ // Import utilities needed for orchestration
14
+ import { DEFAULT_EXCLUDED_DIRS, DEFAULT_SOURCE_EXTENSIONS, routeKey, } from './scanner-internals/types.js';
15
+ /**
16
+ * Returns true for component files that require user interaction to appear
17
+ * (dialogs, modals, drawers, menus, …). Keys in these files are synced to the
18
+ * CMS but get no previewRoutes because simply navigating to a route won't show them.
19
+ *
20
+ * Conservative matching: only flags files whose name ENDS with an overlay keyword
21
+ * (before extension), and files inside overlay-specific directories.
22
+ * Files like "MenuNavigation.tsx" are NOT matched — only "UserMenu.tsx".
23
+ */
24
+ function isInteractiveOverlay(filePath) {
25
+ const lower = filePath.toLowerCase();
26
+ // File whose name ENDS with an overlay keyword (e.g. UserMenu.tsx, ConfirmDialog.tsx)
27
+ if (/[\\/][^/\\]*(dialog|modal|drawer|sheet|popover|dropdown|menu|combobox|tooltip)\.[jt]sx?$/i.test(lower))
28
+ return true;
29
+ // File inside a dedicated overlay directory (dialogs/, modals/, etc.)
30
+ if (/[\\/](dialogs|modals|drawers|sheets|popovers|dropdowns|menus|comboboxes|tooltips)(\/|\\)/.test(lower))
31
+ return true;
32
+ return false;
33
+ }
34
+ import { walkFiles } from './scanner-internals/file-walker.js';
35
+ import { filePathToRoute } from './scanner-internals/route-detector.js';
36
+ import { extractKeysFromFile } from './scanner-internals/key-extractor.js';
37
+ import { extractLocalImports, getLayoutChain, } from './scanner-internals/import-resolver.js';
38
+ import { scanNavConfig } from './scanner-internals/nav-config-scanner.js';
39
+ // ---------------------------------------------------------------------------
40
+ // Project scan
41
+ // ---------------------------------------------------------------------------
42
+ /**
43
+ * Scans the project for translation keys and optionally merges external preview routes.
44
+ *
45
+ * Pass 1: Scan all files, extracting keys and associating them with page/layout routes.
46
+ * Pass 2: For each page/layout, walk its import tree to assign keys in deeply-nested
47
+ * components to the correct preview route.
48
+ * Pass 3a: Optionally scan navigation config files and merge their routes (if includeNavRoutes is true).
49
+ * Pass 3b: Merge external preview routes from database or config (routeParams).
50
+ */
51
+ export function scanProject(root, opts) {
52
+ const excludedDirs = new Set([
53
+ ...DEFAULT_EXCLUDED_DIRS,
54
+ ...(opts?.excludedDirs ?? []),
55
+ ]);
56
+ const sourceExtensions = opts?.sourceExtensions
57
+ ? new Set(opts.sourceExtensions)
58
+ : DEFAULT_SOURCE_EXTENSIONS;
59
+ const routeParams = opts?.routeParams;
60
+ const pathMappings = opts?.pathMappings;
61
+ const projectRoot = opts?.projectRoot ?? root;
62
+ const result = {};
63
+ let fileCount = 0;
64
+ // Pass 1: scan all files normally
65
+ const routeByFile = new Map();
66
+ for (const file of walkFiles(root, excludedDirs, sourceExtensions)) {
67
+ const routes = filePathToRoute(root, file);
68
+ routeByFile.set(file, routes);
69
+ extractKeysFromFile(file, routes, result);
70
+ fileCount++;
71
+ }
72
+ // Pass 2: for each page/layout file, transitively walk its import tree and
73
+ // re-scan every non-page/layout component with the same routes. This ensures
74
+ // keys used in deeply-nested components (e.g. layout → Nav → NavItem) are
75
+ // associated with the correct preview routes.
76
+ const scannedWithRoute = new Set(); // "file::route" pairs already done
77
+ for (const [file, routes] of routeByFile) {
78
+ if (routes.length === 0)
79
+ continue;
80
+ const isPage = path.basename(file).startsWith('page.');
81
+ const layoutChain = isPage
82
+ ? getLayoutChain(root, file, sourceExtensions)
83
+ : [];
84
+ for (const layout of layoutChain) {
85
+ const layoutKey = `${layout}::${routes.join('|')}`;
86
+ if (!scannedWithRoute.has(layoutKey)) {
87
+ scannedWithRoute.add(layoutKey);
88
+ extractKeysFromFile(layout, routes, result);
89
+ }
90
+ }
91
+ // Queue carries the effective routes that each file was scanned with, so that
92
+ // sub-imports of overlays (dialogs, menus, …) inherit [] routes rather than
93
+ // the page routes from the outer loop.
94
+ const queue = [
95
+ { file, currentRoutes: routes },
96
+ ...layoutChain.map(l => ({ file: l, currentRoutes: routes })),
97
+ ];
98
+ while (queue.length > 0) {
99
+ const { file: current, currentRoutes } = queue.shift();
100
+ const imports = extractLocalImports(current, sourceExtensions, projectRoot, pathMappings);
101
+ if (imports.length > 0) {
102
+ console.log(`[Pass 2] ${path.relative(root, current)} (${currentRoutes.join(', ') || 'overlay'}) imports: ${imports.map(i => path.relative(root, i)).join(', ')}`);
103
+ }
104
+ for (const imported of imports) {
105
+ const key = `${imported}::${currentRoutes.join('|')}`;
106
+ if (scannedWithRoute.has(key))
107
+ continue;
108
+ scannedWithRoute.add(key);
109
+ if (!routeByFile.has(imported)) {
110
+ console.log(`[Pass 2] Skipping ${path.relative(root, imported)} — not in walk result`);
111
+ continue;
112
+ }
113
+ if (routeByFile.get(imported).length > 0)
114
+ continue;
115
+ // Don't propagate routes into components that need user interaction to appear.
116
+ // Also: if the parent was already an overlay (currentRoutes = []), keep [] so
117
+ // that all transitive imports of an overlay stay route-free.
118
+ const effectiveRoutes = isInteractiveOverlay(imported)
119
+ ? []
120
+ : currentRoutes;
121
+ console.log(`[Pass 2] Re-scanning ${path.relative(root, imported)} with routes: ${effectiveRoutes.join(', ') || '(none — interactive overlay)'}`);
122
+ extractKeysFromFile(imported, effectiveRoutes, result);
123
+ queue.push({ file: imported, currentRoutes: effectiveRoutes });
124
+ }
125
+ }
126
+ }
127
+ // Pass 3a: Scan navigation config files and merge their routes if enabled
128
+ if (opts?.includeNavRoutes) {
129
+ const navRoutes = scanNavConfig(root, sourceExtensions);
130
+ if (navRoutes['__NAV_CONFIG__']) {
131
+ const navRoutesArray = navRoutes['__NAV_CONFIG__'];
132
+ console.log(`[Pass 3a] Merged ${navRoutesArray.length} navigation route(s)`);
133
+ // Add nav routes to all keys that don't have routes yet (accessibility fallback)
134
+ for (const [, keyMap] of Object.entries(result)) {
135
+ for (const [key, routes] of keyMap.entries()) {
136
+ if (routes.length === 0) {
137
+ keyMap.set(key, navRoutesArray);
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ // Pass 3b: upgrade plain dynamic routes to { route, params } objects using routeParams
144
+ if (routeParams) {
145
+ for (const [, keyMap] of Object.entries(result)) {
146
+ for (const [key, routes] of keyMap.entries()) {
147
+ const upgraded = routes.map(r => {
148
+ const rk = routeKey(r);
149
+ const params = routeParams[rk];
150
+ if (params && typeof r === 'string') {
151
+ return { route: rk, params };
152
+ }
153
+ return r;
154
+ });
155
+ keyMap.set(key, upgraded);
156
+ }
157
+ }
158
+ }
159
+ console.log(`Scanned ${fileCount} files.`);
160
+ return result;
161
+ }
162
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/core/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { initPreviewListener, cleanupPreviewListener, type PreviewListenerOptions, } from './preview/index.js';
2
+ export { type TranslationsConfig } from './config/resolve-config.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACH,mBAAmB,EACnB,sBAAsB,EACtB,KAAK,sBAAsB,GAC9B,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ // Browser: in-context preview listener
2
+ export { initPreviewListener, cleanupPreviewListener, } from './preview/index.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,OAAO,EACH,mBAAmB,EACnB,sBAAsB,GAEzB,MAAM,oBAAoB,CAAC"}
package/dist/next.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Next.js Plugin for automatic translation sync
3
+ *
4
+ * Usage in next.config.ts:
5
+ * import withTranslationsCMS from '@translation-cms/sync/next';
6
+ *
7
+ * export default withTranslationsCMS(nextConfig, {
8
+ * pullOnBuild: true, // default
9
+ * pullOnDev: true, // default
10
+ * });
11
+ */
12
+ import type { NextConfig } from './next';
13
+ export interface TranslationsCMSPluginOptions {
14
+ /** Automatically pull translations on `next build` (default: true) */
15
+ pullOnBuild?: boolean;
16
+ /** Automatically pull translations on `next dev` (default: true) */
17
+ pullOnDev?: boolean;
18
+ /**
19
+ * How often (in ms) to poll the CMS for new translations in dev mode.
20
+ * Set to 0 to disable. Default: 10000 (10 seconds).
21
+ */
22
+ watchInterval?: number;
23
+ }
24
+ /**
25
+ * Higher-order function that wraps your Next.js config with automatic translation syncing.
26
+ *
27
+ * @param userConfig - Your existing next.config.ts configuration
28
+ * @param pluginOptions - Plugin-specific options
29
+ * @returns Enhanced Next.js configuration with sync hooks
30
+ */
31
+ export declare function withTranslationsCMS(userConfig: NextConfig | undefined, pluginOptions?: TranslationsCMSPluginOptions): NextConfig;
32
+ export default withTranslationsCMS;
33
+ //# sourceMappingURL=next.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAWvC,MAAM,WAAW,4BAA4B;IACzC,sEAAsE;IACtE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oEAAoE;IACpE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAC/B,UAAU,EAAE,UAAU,GAAG,SAAS,EAClC,aAAa,GAAE,4BAAiC,GACjD,UAAU,CAiEZ;AAgDD,eAAe,mBAAmB,CAAC"}