@reverso/scanner 0.1.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.
Files changed (58) hide show
  1. package/README.md +45 -0
  2. package/dist/index.d.ts +40 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +45 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/output/index.d.ts +6 -0
  7. package/dist/output/index.d.ts.map +1 -0
  8. package/dist/output/index.js +6 -0
  9. package/dist/output/index.js.map +1 -0
  10. package/dist/output/json-writer.d.ts +32 -0
  11. package/dist/output/json-writer.d.ts.map +1 -0
  12. package/dist/output/json-writer.js +169 -0
  13. package/dist/output/json-writer.js.map +1 -0
  14. package/dist/output/types-writer.d.ts +24 -0
  15. package/dist/output/types-writer.d.ts.map +1 -0
  16. package/dist/output/types-writer.js +209 -0
  17. package/dist/output/types-writer.js.map +1 -0
  18. package/dist/parser/ast-parser.d.ts +79 -0
  19. package/dist/parser/ast-parser.d.ts.map +1 -0
  20. package/dist/parser/ast-parser.js +201 -0
  21. package/dist/parser/ast-parser.js.map +1 -0
  22. package/dist/parser/attribute-extractor.d.ts +29 -0
  23. package/dist/parser/attribute-extractor.d.ts.map +1 -0
  24. package/dist/parser/attribute-extractor.js +204 -0
  25. package/dist/parser/attribute-extractor.js.map +1 -0
  26. package/dist/parser/index.d.ts +7 -0
  27. package/dist/parser/index.d.ts.map +1 -0
  28. package/dist/parser/index.js +7 -0
  29. package/dist/parser/index.js.map +1 -0
  30. package/dist/parser/jsx-walker.d.ts +31 -0
  31. package/dist/parser/jsx-walker.d.ts.map +1 -0
  32. package/dist/parser/jsx-walker.js +103 -0
  33. package/dist/parser/jsx-walker.js.map +1 -0
  34. package/dist/scanner.d.ts +101 -0
  35. package/dist/scanner.d.ts.map +1 -0
  36. package/dist/scanner.js +204 -0
  37. package/dist/scanner.js.map +1 -0
  38. package/dist/schema/generator.d.ts +22 -0
  39. package/dist/schema/generator.d.ts.map +1 -0
  40. package/dist/schema/generator.js +176 -0
  41. package/dist/schema/generator.js.map +1 -0
  42. package/dist/schema/index.d.ts +6 -0
  43. package/dist/schema/index.d.ts.map +1 -0
  44. package/dist/schema/index.js +6 -0
  45. package/dist/schema/index.js.map +1 -0
  46. package/dist/schema/normalizer.d.ts +45 -0
  47. package/dist/schema/normalizer.d.ts.map +1 -0
  48. package/dist/schema/normalizer.js +165 -0
  49. package/dist/schema/normalizer.js.map +1 -0
  50. package/dist/watch/index.d.ts +5 -0
  51. package/dist/watch/index.d.ts.map +1 -0
  52. package/dist/watch/index.js +5 -0
  53. package/dist/watch/index.js.map +1 -0
  54. package/dist/watch/watcher.d.ts +82 -0
  55. package/dist/watch/watcher.d.ts.map +1 -0
  56. package/dist/watch/watcher.js +141 -0
  57. package/dist/watch/watcher.js.map +1 -0
  58. package/package.json +62 -0
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Schema normalization utilities.
3
+ */
4
+ import { type FieldSchema, type PageSchema, type ProjectSchema, type SectionSchema } from '@reverso/core';
5
+ /**
6
+ * Normalize field paths to ensure consistency.
7
+ */
8
+ export declare function normalizeFieldPath(path: string): string;
9
+ /**
10
+ * Normalize all field paths in a list.
11
+ */
12
+ export declare function normalizeFields(fields: FieldSchema[]): FieldSchema[];
13
+ /**
14
+ * Sort fields within each section by path.
15
+ */
16
+ export declare function sortSectionFields(section: SectionSchema): SectionSchema;
17
+ /**
18
+ * Sort sections within a page by slug.
19
+ */
20
+ export declare function sortPageSections(page: PageSchema): PageSchema;
21
+ /**
22
+ * Sort pages within a schema by slug.
23
+ */
24
+ export declare function sortSchemaPages(schema: ProjectSchema): ProjectSchema;
25
+ /**
26
+ * Ensure all fields have labels (generate from path if missing).
27
+ */
28
+ export declare function ensureLabels(fields: FieldSchema[]): FieldSchema[];
29
+ /**
30
+ * Remove duplicate fields (same path), keeping the first occurrence.
31
+ */
32
+ export declare function deduplicateFields(fields: FieldSchema[]): FieldSchema[];
33
+ /**
34
+ * Merge fields with the same path (combine attributes).
35
+ */
36
+ export declare function mergeFields(fields: FieldSchema[]): FieldSchema[];
37
+ /**
38
+ * Reorder sections within a page according to a predefined order.
39
+ */
40
+ export declare function reorderSections(page: PageSchema, order: string[]): PageSchema;
41
+ /**
42
+ * Validate schema structure and return any issues.
43
+ */
44
+ export declare function validateSchemaStructure(schema: ProjectSchema): string[];
45
+ //# sourceMappingURL=normalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../../src/schema/normalizer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,aAAa,EAInB,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAiBvD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAKpE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,aAAa,CAKvE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAO7D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAKpE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAcjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAYtE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAsBhE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,UAAU,CAW7E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,EAAE,CA6CvE"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Schema normalization utilities.
3
+ */
4
+ import { formatLabel, parsePath, sortPaths, } from '@reverso/core';
5
+ /**
6
+ * Normalize field paths to ensure consistency.
7
+ */
8
+ export function normalizeFieldPath(path) {
9
+ // Trim whitespace
10
+ let normalized = path.trim();
11
+ // Convert to lowercase
12
+ normalized = normalized.toLowerCase();
13
+ // Replace spaces and special characters with underscores
14
+ normalized = normalized.replace(/[^a-z0-9.$_-]/g, '_');
15
+ // Remove consecutive dots or underscores
16
+ normalized = normalized.replace(/\.+/g, '.').replace(/_+/g, '_');
17
+ // Remove leading/trailing dots or underscores
18
+ normalized = normalized.replace(/^[._]+|[._]+$/g, '');
19
+ return normalized;
20
+ }
21
+ /**
22
+ * Normalize all field paths in a list.
23
+ */
24
+ export function normalizeFields(fields) {
25
+ return fields.map((field) => ({
26
+ ...field,
27
+ path: normalizeFieldPath(field.path),
28
+ }));
29
+ }
30
+ /**
31
+ * Sort fields within each section by path.
32
+ */
33
+ export function sortSectionFields(section) {
34
+ return {
35
+ ...section,
36
+ fields: [...section.fields].sort((a, b) => a.path.localeCompare(b.path)),
37
+ };
38
+ }
39
+ /**
40
+ * Sort sections within a page by slug.
41
+ */
42
+ export function sortPageSections(page) {
43
+ return {
44
+ ...page,
45
+ sections: [...page.sections]
46
+ .map(sortSectionFields)
47
+ .sort((a, b) => a.slug.localeCompare(b.slug)),
48
+ };
49
+ }
50
+ /**
51
+ * Sort pages within a schema by slug.
52
+ */
53
+ export function sortSchemaPages(schema) {
54
+ return {
55
+ ...schema,
56
+ pages: [...schema.pages].map(sortPageSections).sort((a, b) => a.slug.localeCompare(b.slug)),
57
+ };
58
+ }
59
+ /**
60
+ * Ensure all fields have labels (generate from path if missing).
61
+ */
62
+ export function ensureLabels(fields) {
63
+ return fields.map((field) => {
64
+ if (field.label) {
65
+ return field;
66
+ }
67
+ const parsed = parsePath(field.path);
68
+ const fieldName = parsed.repeaterField ?? parsed.field;
69
+ return {
70
+ ...field,
71
+ label: formatLabel(fieldName),
72
+ };
73
+ });
74
+ }
75
+ /**
76
+ * Remove duplicate fields (same path), keeping the first occurrence.
77
+ */
78
+ export function deduplicateFields(fields) {
79
+ const seen = new Set();
80
+ const unique = [];
81
+ for (const field of fields) {
82
+ if (!seen.has(field.path)) {
83
+ seen.add(field.path);
84
+ unique.push(field);
85
+ }
86
+ }
87
+ return unique;
88
+ }
89
+ /**
90
+ * Merge fields with the same path (combine attributes).
91
+ */
92
+ export function mergeFields(fields) {
93
+ const merged = new Map();
94
+ for (const field of fields) {
95
+ const existing = merged.get(field.path);
96
+ if (!existing) {
97
+ merged.set(field.path, { ...field });
98
+ }
99
+ else {
100
+ // Merge attributes, preferring new values over existing
101
+ merged.set(field.path, {
102
+ ...existing,
103
+ ...field,
104
+ // Keep the original file/line info
105
+ file: existing.file,
106
+ line: existing.line,
107
+ column: existing.column,
108
+ });
109
+ }
110
+ }
111
+ return Array.from(merged.values());
112
+ }
113
+ /**
114
+ * Reorder sections within a page according to a predefined order.
115
+ */
116
+ export function reorderSections(page, order) {
117
+ const orderMap = new Map(order.map((slug, index) => [slug, index]));
118
+ return {
119
+ ...page,
120
+ sections: [...page.sections].sort((a, b) => {
121
+ const orderA = orderMap.get(a.slug) ?? Number.MAX_SAFE_INTEGER;
122
+ const orderB = orderMap.get(b.slug) ?? Number.MAX_SAFE_INTEGER;
123
+ return orderA - orderB;
124
+ }),
125
+ };
126
+ }
127
+ /**
128
+ * Validate schema structure and return any issues.
129
+ */
130
+ export function validateSchemaStructure(schema) {
131
+ const issues = [];
132
+ for (const page of schema.pages) {
133
+ if (!page.slug) {
134
+ issues.push('Page missing slug');
135
+ }
136
+ for (const section of page.sections) {
137
+ if (!section.slug) {
138
+ issues.push(`Section in page "${page.slug}" missing slug`);
139
+ }
140
+ for (const field of section.fields) {
141
+ if (!field.path) {
142
+ issues.push(`Field in "${page.slug}.${section.slug}" missing path`);
143
+ }
144
+ if (!field.type) {
145
+ issues.push(`Field "${field.path}" missing type`);
146
+ }
147
+ // Validate path structure
148
+ try {
149
+ const parsed = parsePath(field.path);
150
+ if (parsed.page !== page.slug) {
151
+ issues.push(`Field "${field.path}" has page "${parsed.page}" but is in page "${page.slug}"`);
152
+ }
153
+ if (parsed.section !== section.slug) {
154
+ issues.push(`Field "${field.path}" has section "${parsed.section}" but is in section "${section.slug}"`);
155
+ }
156
+ }
157
+ catch (error) {
158
+ issues.push(`Field "${field.path}" has invalid path: ${error instanceof Error ? error.message : String(error)}`);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ return issues;
164
+ }
165
+ //# sourceMappingURL=normalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizer.js","sourceRoot":"","sources":["../../src/schema/normalizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Watch exports for @reverso/scanner
3
+ */
4
+ export { FileWatcher, createWatcher, type WatchEventType, type WatchEvent, type WatchEventHandler, type WatcherOptions, } from './watcher.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/watch/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,WAAW,EACX,aAAa,EACb,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Watch exports for @reverso/scanner
3
+ */
4
+ export { FileWatcher, createWatcher, } from './watcher.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/watch/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,WAAW,EACX,aAAa,GAKd,MAAM,cAAc,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * File watcher for detecting changes in source files.
3
+ */
4
+ /**
5
+ * Events emitted by the watcher.
6
+ */
7
+ export type WatchEventType = 'add' | 'change' | 'unlink';
8
+ /**
9
+ * Watch event data.
10
+ */
11
+ export interface WatchEvent {
12
+ type: WatchEventType;
13
+ path: string;
14
+ timestamp: number;
15
+ }
16
+ /**
17
+ * Watch event handler.
18
+ */
19
+ export type WatchEventHandler = (event: WatchEvent) => void | Promise<void>;
20
+ /**
21
+ * Options for the file watcher.
22
+ */
23
+ export interface WatcherOptions {
24
+ /** Directory to watch */
25
+ srcDir: string;
26
+ /** Glob patterns to include */
27
+ include?: string[];
28
+ /** Glob patterns to exclude */
29
+ exclude?: string[];
30
+ /** Debounce delay in milliseconds */
31
+ debounce?: number;
32
+ /** Event handler */
33
+ onEvent?: WatchEventHandler;
34
+ /** Error handler */
35
+ onError?: (error: Error) => void;
36
+ /** Ready handler */
37
+ onReady?: () => void;
38
+ }
39
+ /**
40
+ * File watcher class.
41
+ */
42
+ export declare class FileWatcher {
43
+ private watcher;
44
+ private options;
45
+ private debounceTimers;
46
+ private debounceDelay;
47
+ private isReady;
48
+ constructor(options: WatcherOptions);
49
+ /**
50
+ * Start watching for file changes.
51
+ */
52
+ start(): void;
53
+ /**
54
+ * Stop watching for file changes.
55
+ */
56
+ stop(): Promise<void>;
57
+ /**
58
+ * Check if the watcher is running.
59
+ */
60
+ isRunning(): boolean;
61
+ /**
62
+ * Check if the watcher is ready (initial scan complete).
63
+ */
64
+ ready(): boolean;
65
+ /**
66
+ * Handle a file system event with debouncing.
67
+ */
68
+ private handleEvent;
69
+ /**
70
+ * Emit an event to the handler.
71
+ */
72
+ private emitEvent;
73
+ /**
74
+ * Get the watched paths.
75
+ */
76
+ getWatchedPaths(): string[];
77
+ }
78
+ /**
79
+ * Create a new file watcher.
80
+ */
81
+ export declare function createWatcher(options: WatcherOptions): FileWatcher;
82
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/watch/watcher.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,oBAAoB;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,cAAc;IAKnC;;OAEG;IACH,KAAK,IAAI,IAAI;IAyCb;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,KAAK,IAAI,OAAO;IAIhB;;OAEG;IACH,OAAO,CAAC,WAAW;IAgBnB;;OAEG;IACH,OAAO,CAAC,SAAS;IAiBjB;;OAEG;IACH,eAAe,IAAI,MAAM,EAAE;CAgB5B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,WAAW,CAElE"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * File watcher for detecting changes in source files.
3
+ */
4
+ import { resolve } from 'node:path';
5
+ import { DEFAULT_EXCLUDE_PATTERNS, DEFAULT_INCLUDE_PATTERNS, DEFAULT_WATCH_DEBOUNCE, } from '@reverso/core';
6
+ import { watch } from 'chokidar';
7
+ /**
8
+ * File watcher class.
9
+ */
10
+ export class FileWatcher {
11
+ watcher = null;
12
+ options;
13
+ debounceTimers = new Map();
14
+ debounceDelay;
15
+ isReady = false;
16
+ constructor(options) {
17
+ this.options = options;
18
+ this.debounceDelay = options.debounce ?? DEFAULT_WATCH_DEBOUNCE;
19
+ }
20
+ /**
21
+ * Start watching for file changes.
22
+ */
23
+ start() {
24
+ if (this.watcher) {
25
+ return;
26
+ }
27
+ const srcDir = resolve(this.options.srcDir);
28
+ const include = this.options.include ?? [...DEFAULT_INCLUDE_PATTERNS];
29
+ const exclude = this.options.exclude ?? [...DEFAULT_EXCLUDE_PATTERNS];
30
+ // Create glob patterns for chokidar
31
+ const patterns = include.map((pattern) => `${srcDir}/${pattern}`);
32
+ this.watcher = watch(patterns, {
33
+ ignored: exclude,
34
+ persistent: true,
35
+ ignoreInitial: true,
36
+ awaitWriteFinish: {
37
+ stabilityThreshold: 100,
38
+ pollInterval: 50,
39
+ },
40
+ });
41
+ // Set up event handlers
42
+ this.watcher.on('add', (path) => this.handleEvent('add', path));
43
+ this.watcher.on('change', (path) => this.handleEvent('change', path));
44
+ this.watcher.on('unlink', (path) => this.handleEvent('unlink', path));
45
+ this.watcher.on('error', (error) => {
46
+ if (this.options.onError) {
47
+ this.options.onError(error instanceof Error ? error : new Error(String(error)));
48
+ }
49
+ });
50
+ this.watcher.on('ready', () => {
51
+ this.isReady = true;
52
+ if (this.options.onReady) {
53
+ this.options.onReady();
54
+ }
55
+ });
56
+ }
57
+ /**
58
+ * Stop watching for file changes.
59
+ */
60
+ async stop() {
61
+ // Clear all debounce timers
62
+ for (const timer of this.debounceTimers.values()) {
63
+ clearTimeout(timer);
64
+ }
65
+ this.debounceTimers.clear();
66
+ if (this.watcher) {
67
+ await this.watcher.close();
68
+ this.watcher = null;
69
+ this.isReady = false;
70
+ }
71
+ }
72
+ /**
73
+ * Check if the watcher is running.
74
+ */
75
+ isRunning() {
76
+ return this.watcher !== null;
77
+ }
78
+ /**
79
+ * Check if the watcher is ready (initial scan complete).
80
+ */
81
+ ready() {
82
+ return this.isReady;
83
+ }
84
+ /**
85
+ * Handle a file system event with debouncing.
86
+ */
87
+ handleEvent(type, path) {
88
+ // Clear existing timer for this path
89
+ const existingTimer = this.debounceTimers.get(path);
90
+ if (existingTimer) {
91
+ clearTimeout(existingTimer);
92
+ }
93
+ // Set new debounced timer
94
+ const timer = setTimeout(() => {
95
+ this.debounceTimers.delete(path);
96
+ this.emitEvent(type, path);
97
+ }, this.debounceDelay);
98
+ this.debounceTimers.set(path, timer);
99
+ }
100
+ /**
101
+ * Emit an event to the handler.
102
+ */
103
+ emitEvent(type, path) {
104
+ if (this.options.onEvent) {
105
+ const event = {
106
+ type,
107
+ path,
108
+ timestamp: Date.now(),
109
+ };
110
+ // Call handler (may be async)
111
+ Promise.resolve(this.options.onEvent(event)).catch((err) => {
112
+ if (this.options.onError) {
113
+ this.options.onError(err instanceof Error ? err : new Error(String(err)));
114
+ }
115
+ });
116
+ }
117
+ }
118
+ /**
119
+ * Get the watched paths.
120
+ */
121
+ getWatchedPaths() {
122
+ if (!this.watcher) {
123
+ return [];
124
+ }
125
+ const watched = this.watcher.getWatched();
126
+ const paths = [];
127
+ for (const [dir, files] of Object.entries(watched)) {
128
+ for (const file of files) {
129
+ paths.push(`${dir}/${file}`);
130
+ }
131
+ }
132
+ return paths;
133
+ }
134
+ }
135
+ /**
136
+ * Create a new file watcher.
137
+ */
138
+ export function createWatcher(options) {
139
+ return new FileWatcher(options);
140
+ }
141
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/watch/watcher.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@reverso/scanner",
3
+ "version": "0.1.0",
4
+ "description": "AST parser for detecting data-reverso markers in React/Next.js code",
5
+ "homepage": "https://reverso.dev/docs/packages/scanner",
6
+ "bugs": {
7
+ "url": "https://github.com/hogrid/reverso/issues"
8
+ },
9
+ "engines": {
10
+ "node": ">=20.0.0"
11
+ },
12
+ "type": "module",
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.build.json",
27
+ "dev": "tsc -p tsconfig.build.json --watch",
28
+ "typecheck": "tsc --noEmit",
29
+ "lint": "biome check src/",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "clean": "rm -rf dist .turbo"
33
+ },
34
+ "dependencies": {
35
+ "@reverso/core": "workspace:*",
36
+ "ts-morph": "^27.0.2",
37
+ "glob": "^13.0.0",
38
+ "chokidar": "^5.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.10.7",
42
+ "typescript": "^5.7.3",
43
+ "vitest": "^2.1.8"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/hogrid/reverso.git",
51
+ "directory": "packages/scanner"
52
+ },
53
+ "keywords": [
54
+ "reverso",
55
+ "cms",
56
+ "scanner",
57
+ "ast",
58
+ "parser"
59
+ ],
60
+ "author": "Emerson Nunes <emerson@hogrid.com>",
61
+ "license": "MIT"
62
+ }