kishare 1.0.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.
- package/LICENSE +674 -0
- package/README.md +91 -0
- package/bin/kishare.js +27 -0
- package/index.html +15 -0
- package/package.json +53 -0
- package/src/components/project-list.ts +326 -0
- package/src/components/viewer-panel.ts +546 -0
- package/src/components/workspace-app.ts +270 -0
- package/src/indexer/copy-public-files.ts +65 -0
- package/src/indexer/indexer-utils.ts +111 -0
- package/src/indexer/scan-projects.ts +399 -0
- package/src/lib/constants.ts +44 -0
- package/src/lib/html-utils.ts +20 -0
- package/src/lib/project-index.ts +60 -0
- package/src/lib/router.ts +208 -0
- package/src/main.ts +13 -0
- package/src/node/cli.ts +161 -0
- package/src/styles/main.css +698 -0
- package/tsconfig.json +26 -0
- package/vendor/kicanvas/tsconfig.json +48 -0
- package/vite.config.ts +69 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main application component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import DOMPurify from 'dompurify';
|
|
6
|
+
import { loadProjectIndex, type GitInfo, type ProjectMetadata } from '../lib/project-index.js';
|
|
7
|
+
import { router } from '../lib/router.js';
|
|
8
|
+
import { isSafeUrl, githubIcon } from '../lib/html-utils.js';
|
|
9
|
+
import { SIDEBAR } from '../lib/constants.js';
|
|
10
|
+
import { ProjectList } from './project-list.js';
|
|
11
|
+
import { ViewerPanel } from './viewer-panel.js';
|
|
12
|
+
|
|
13
|
+
export class WorkspaceApp extends HTMLElement {
|
|
14
|
+
private projectList!: ProjectList;
|
|
15
|
+
private viewerPanel!: ViewerPanel;
|
|
16
|
+
private projects: ProjectMetadata[] = [];
|
|
17
|
+
private title: string = 'KiShare';
|
|
18
|
+
private sidebarCollapsed: boolean = false;
|
|
19
|
+
private sidebarWidth: number = 300; // Track uncollapsed width
|
|
20
|
+
private gitInfo: GitInfo | null = null;
|
|
21
|
+
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async connectedCallback() {
|
|
27
|
+
this.render();
|
|
28
|
+
await this.loadProjects();
|
|
29
|
+
this.setupRouting();
|
|
30
|
+
this.setupSidebarToggle();
|
|
31
|
+
this.setupSidebarResize();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Load project index from server
|
|
36
|
+
*/
|
|
37
|
+
private async loadProjects() {
|
|
38
|
+
try {
|
|
39
|
+
const index = await loadProjectIndex();
|
|
40
|
+
this.projects = index.projects;
|
|
41
|
+
this.title = DOMPurify.sanitize(index.title);
|
|
42
|
+
|
|
43
|
+
// Update title in UI
|
|
44
|
+
const titleEl = this.querySelector('.sidebar-title');
|
|
45
|
+
if (titleEl) {
|
|
46
|
+
titleEl.textContent = this.title;
|
|
47
|
+
}
|
|
48
|
+
document.title = this.title;
|
|
49
|
+
|
|
50
|
+
// Update git info in footer and viewer
|
|
51
|
+
this.gitInfo = index.git;
|
|
52
|
+
this.updateGitInfo(index.git);
|
|
53
|
+
this.viewerPanel.setGitInfo(index.git);
|
|
54
|
+
|
|
55
|
+
// Update project list
|
|
56
|
+
this.projectList.setGitInfo(index.git);
|
|
57
|
+
this.projectList.setProjects(this.projects);
|
|
58
|
+
|
|
59
|
+
console.log(`Loaded ${this.projects.length} projects`);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error('Failed to load projects:', error);
|
|
62
|
+
this.viewerPanel.showError('Failed to load project index');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Update git info in sidebar footer
|
|
68
|
+
*/
|
|
69
|
+
private updateGitInfo(git: GitInfo) {
|
|
70
|
+
const footer = this.querySelector('.sidebar-footer');
|
|
71
|
+
if (!footer) return;
|
|
72
|
+
|
|
73
|
+
// Extract user/repo from repoUrl (e.g., "https://github.com/user/repo" -> "user/repo")
|
|
74
|
+
let repoPath = '';
|
|
75
|
+
if (git.repoUrl) {
|
|
76
|
+
const match = git.repoUrl.match(/https?:\/\/[^/]+\/(.+)/);
|
|
77
|
+
if (match) {
|
|
78
|
+
repoPath = match[1];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (git.repoUrl && repoPath) {
|
|
83
|
+
const commitUrl = git.commitUrl || git.repoUrl;
|
|
84
|
+
if (!isSafeUrl(commitUrl)) {
|
|
85
|
+
console.warn('Invalid URL protocol in git info:', commitUrl);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const html = `
|
|
90
|
+
<a href="${commitUrl}" target="_blank" rel="noopener" class="commit-link">
|
|
91
|
+
${githubIcon(14)}
|
|
92
|
+
<span class="repo-path">${repoPath}</span>
|
|
93
|
+
${git.commitHashShort ? `<span class="commit-separator">-</span><span class="commit-hash">${git.commitHashShort}</span>` : ''}
|
|
94
|
+
</a>
|
|
95
|
+
`;
|
|
96
|
+
footer.innerHTML = DOMPurify.sanitize(html);
|
|
97
|
+
} else if (git.commitHashShort) {
|
|
98
|
+
footer.innerHTML = DOMPurify.sanitize(`<span class="commit-hash">${git.commitHashShort}</span>`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Setup sidebar toggle functionality
|
|
104
|
+
*/
|
|
105
|
+
private setupSidebarToggle() {
|
|
106
|
+
const toggleBtn = this.querySelector('.sidebar-toggle') as HTMLElement;
|
|
107
|
+
const sidebar = this.querySelector('.sidebar') as HTMLElement;
|
|
108
|
+
|
|
109
|
+
toggleBtn?.addEventListener('click', () => {
|
|
110
|
+
this.sidebarCollapsed = !this.sidebarCollapsed;
|
|
111
|
+
sidebar?.classList.toggle('collapsed', this.sidebarCollapsed);
|
|
112
|
+
toggleBtn?.classList.toggle('collapsed', this.sidebarCollapsed);
|
|
113
|
+
|
|
114
|
+
// Update button position
|
|
115
|
+
if (this.sidebarCollapsed) {
|
|
116
|
+
toggleBtn.style.left = '8px';
|
|
117
|
+
} else {
|
|
118
|
+
// Restore position based on tracked width
|
|
119
|
+
toggleBtn.style.left = `${this.sidebarWidth + 8}px`;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Setup sidebar resize functionality
|
|
126
|
+
*/
|
|
127
|
+
private setupSidebarResize() {
|
|
128
|
+
const resizeHandle = this.querySelector('.sidebar-resize-handle') as HTMLElement;
|
|
129
|
+
const sidebar = this.querySelector('.sidebar') as HTMLElement;
|
|
130
|
+
const toggleBtn = this.querySelector('.sidebar-toggle') as HTMLElement;
|
|
131
|
+
|
|
132
|
+
if (!resizeHandle || !sidebar) return;
|
|
133
|
+
|
|
134
|
+
// Load saved width from localStorage
|
|
135
|
+
const savedWidth = localStorage.getItem(SIDEBAR.STORAGE_KEY);
|
|
136
|
+
if (savedWidth) {
|
|
137
|
+
const width = parseInt(savedWidth, 10);
|
|
138
|
+
this.sidebarWidth = width;
|
|
139
|
+
sidebar.style.width = `${width}px`;
|
|
140
|
+
sidebar.style.minWidth = `${width}px`;
|
|
141
|
+
if (toggleBtn) {
|
|
142
|
+
toggleBtn.style.left = `${width + 8}px`;
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
// Initialize with default width
|
|
146
|
+
this.sidebarWidth = sidebar.offsetWidth || 300;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let isResizing = false;
|
|
150
|
+
let startX = 0;
|
|
151
|
+
let startWidth = 0;
|
|
152
|
+
|
|
153
|
+
const onMouseDown = (e: MouseEvent) => {
|
|
154
|
+
isResizing = true;
|
|
155
|
+
startX = e.clientX;
|
|
156
|
+
startWidth = sidebar.offsetWidth;
|
|
157
|
+
document.body.style.cursor = 'col-resize';
|
|
158
|
+
document.body.style.userSelect = 'none';
|
|
159
|
+
e.preventDefault();
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const onMouseMove = (e: MouseEvent) => {
|
|
163
|
+
if (!isResizing) return;
|
|
164
|
+
|
|
165
|
+
const delta = e.clientX - startX;
|
|
166
|
+
const newWidth = Math.max(SIDEBAR.MIN_WIDTH, Math.min(SIDEBAR.MAX_WIDTH, startWidth + delta));
|
|
167
|
+
|
|
168
|
+
this.sidebarWidth = newWidth;
|
|
169
|
+
sidebar.style.width = `${newWidth}px`;
|
|
170
|
+
sidebar.style.minWidth = `${newWidth}px`;
|
|
171
|
+
if (toggleBtn) {
|
|
172
|
+
toggleBtn.style.left = `${newWidth + 8}px`;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const onMouseUp = () => {
|
|
177
|
+
if (isResizing) {
|
|
178
|
+
isResizing = false;
|
|
179
|
+
document.body.style.cursor = '';
|
|
180
|
+
document.body.style.userSelect = '';
|
|
181
|
+
|
|
182
|
+
// Save width to localStorage
|
|
183
|
+
this.sidebarWidth = sidebar.offsetWidth;
|
|
184
|
+
localStorage.setItem(SIDEBAR.STORAGE_KEY, this.sidebarWidth.toString());
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
resizeHandle.addEventListener('mousedown', onMouseDown);
|
|
189
|
+
document.addEventListener('mousemove', onMouseMove);
|
|
190
|
+
document.addEventListener('mouseup', onMouseUp);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Setup routing and handle route changes
|
|
195
|
+
*/
|
|
196
|
+
private setupRouting() {
|
|
197
|
+
router.onRouteChange((route) => {
|
|
198
|
+
if (route.path === '/project' && route.projectId) {
|
|
199
|
+
this.loadProject(route.projectId, route.position, route.marker);
|
|
200
|
+
} else {
|
|
201
|
+
// Default route - show welcome
|
|
202
|
+
this.viewerPanel.showWelcome();
|
|
203
|
+
this.projectList.setSelectedProject(null);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Load and display a specific project
|
|
210
|
+
*/
|
|
211
|
+
private loadProject(
|
|
212
|
+
projectId: string,
|
|
213
|
+
position?: import('../lib/router.js').ViewPosition,
|
|
214
|
+
marker?: import('../lib/router.js').MarkerBounds
|
|
215
|
+
) {
|
|
216
|
+
const project = this.projects.find(p => p.id === projectId);
|
|
217
|
+
|
|
218
|
+
if (!project) {
|
|
219
|
+
console.error(`Project not found: ${projectId}`);
|
|
220
|
+
this.viewerPanel.showError(`Project "${projectId}" not found`);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
console.log(`Loading project: ${project.name}`);
|
|
225
|
+
|
|
226
|
+
// Update UI
|
|
227
|
+
this.projectList.setSelectedProject(projectId);
|
|
228
|
+
this.viewerPanel.loadProject(project, position, marker);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Initial render of the workspace structure
|
|
233
|
+
*/
|
|
234
|
+
private render() {
|
|
235
|
+
this.innerHTML = `
|
|
236
|
+
<div class="workspace">
|
|
237
|
+
<aside class="sidebar">
|
|
238
|
+
<div class="sidebar-header">
|
|
239
|
+
<h1 class="sidebar-title">${this.title}</h1>
|
|
240
|
+
</div>
|
|
241
|
+
<div class="sidebar-content">
|
|
242
|
+
<project-list></project-list>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="sidebar-hints">
|
|
245
|
+
<div class="hint">Right-click to pan</div>
|
|
246
|
+
<div class="hint">Press 'C' to open GitHub issue</div>
|
|
247
|
+
<div class="hint">Press 'T' to toggle markers</div>
|
|
248
|
+
</div>
|
|
249
|
+
<div class="sidebar-footer"></div>
|
|
250
|
+
<div class="sidebar-resize-handle"></div>
|
|
251
|
+
</aside>
|
|
252
|
+
<button class="sidebar-toggle" title="Toggle sidebar">
|
|
253
|
+
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
|
|
254
|
+
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
|
|
255
|
+
</svg>
|
|
256
|
+
</button>
|
|
257
|
+
<main class="viewer-area">
|
|
258
|
+
<viewer-panel></viewer-panel>
|
|
259
|
+
</main>
|
|
260
|
+
</div>
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
// Get references to child components
|
|
264
|
+
this.projectList = this.querySelector('project-list') as ProjectList;
|
|
265
|
+
this.viewerPanel = this.querySelector('viewer-panel') as ViewerPanel;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Register custom element
|
|
270
|
+
customElements.define('workspace-app', WorkspaceApp);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Copy relevant project files to output directory
|
|
4
|
+
* Respects .gitignore
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import type { WorkspaceConfig } from '../lib/project-index.js';
|
|
10
|
+
import { initializePaths, loadConfig, getProjectDirs, getRootDir, getOutputDir, getTrackedFiles } from './indexer-utils.js';
|
|
11
|
+
|
|
12
|
+
export function copyPublicFiles() {
|
|
13
|
+
initializePaths();
|
|
14
|
+
const config = loadConfig();
|
|
15
|
+
const projectDirs = getProjectDirs(config);
|
|
16
|
+
|
|
17
|
+
console.log(`Copying from directories: ${projectDirs.map(d => path.relative(getRootDir(), d)).join(', ')}`);
|
|
18
|
+
|
|
19
|
+
// Clean public directories for each project directory
|
|
20
|
+
for (const projectDir of projectDirs) {
|
|
21
|
+
const relativeDir = path.relative(getRootDir(), projectDir);
|
|
22
|
+
const publicDir = path.join(getOutputDir(), relativeDir);
|
|
23
|
+
if (fs.existsSync(publicDir)) {
|
|
24
|
+
fs.rmSync(publicDir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Get tracked files from all project directories
|
|
29
|
+
const trackedFiles = getTrackedFiles(projectDirs);
|
|
30
|
+
if (trackedFiles.size === 0) {
|
|
31
|
+
console.log('No tracked files found in configured directories');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`Found ${trackedFiles.size} tracked files`);
|
|
36
|
+
|
|
37
|
+
// Copy each tracked file, preserving the source directory structure
|
|
38
|
+
let copiedCount = 0;
|
|
39
|
+
for (const relativePath of trackedFiles) {
|
|
40
|
+
const srcPath = path.join(getRootDir(), relativePath);
|
|
41
|
+
const destPath = path.join(getOutputDir(), relativePath);
|
|
42
|
+
|
|
43
|
+
// Check if source is a directory (submodule)
|
|
44
|
+
const stat = fs.statSync(srcPath);
|
|
45
|
+
if (stat.isDirectory()) {
|
|
46
|
+
// Recursively copy submodule directory
|
|
47
|
+
fs.cpSync(srcPath, destPath, { recursive: true });
|
|
48
|
+
console.log(` Copied submodule: ${relativePath}`);
|
|
49
|
+
copiedCount++;
|
|
50
|
+
} else {
|
|
51
|
+
// Create destination directory and copy file
|
|
52
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
53
|
+
fs.copyFileSync(srcPath, destPath);
|
|
54
|
+
copiedCount++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(`Copied ${copiedCount} entries to ${getOutputDir()}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Run if this is the main module
|
|
62
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
63
|
+
if (isMainModule) {
|
|
64
|
+
copyPublicFiles();
|
|
65
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for indexer scripts
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import type { WorkspaceConfig } from '../lib/project-index.js';
|
|
10
|
+
|
|
11
|
+
// Module-level variables set at runtime
|
|
12
|
+
let ROOT_DIR: string;
|
|
13
|
+
let OUTPUT_DIR: string;
|
|
14
|
+
let KISHARE_ROOT: string;
|
|
15
|
+
|
|
16
|
+
// Get kishare installation root from environment variable or resolve from this file's location
|
|
17
|
+
function getKishareRoot(): string {
|
|
18
|
+
if (process.env.KISHARE_ROOT) {
|
|
19
|
+
return path.resolve(process.env.KISHARE_ROOT);
|
|
20
|
+
}
|
|
21
|
+
// Fallback: resolve from this file's location (two levels up from src/indexer/)
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
return path.resolve(path.dirname(__filename), '../..');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Get user's project root (where kishare-config.json and KiCad files live)
|
|
27
|
+
function getProjectRoot(): string {
|
|
28
|
+
// KISHARE_PROJECT_ROOT is set by CLI, otherwise use cwd
|
|
29
|
+
return process.env.KISHARE_PROJECT_ROOT || process.cwd();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check if one path is a parent of another
|
|
33
|
+
export function isParentOf(parent: string, child: string): boolean {
|
|
34
|
+
const relative = path.relative(parent, child);
|
|
35
|
+
return !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Read configuration from user's project directory
|
|
39
|
+
export function loadConfig(): WorkspaceConfig {
|
|
40
|
+
const configPath = path.join(getProjectRoot(), 'kishare-config.json');
|
|
41
|
+
if (!fs.existsSync(configPath)) {
|
|
42
|
+
console.warn(`No config file found at ${configPath}, using defaults`);
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
const configData = fs.readFileSync(configPath, 'utf-8');
|
|
46
|
+
return JSON.parse(configData) as WorkspaceConfig;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Initialize paths
|
|
50
|
+
// ROOT_DIR = user's project (where KiCad files live)
|
|
51
|
+
// KISHARE_ROOT = kishare package installation
|
|
52
|
+
// OUTPUT_DIR = kishare's public directory (for Vite to serve)
|
|
53
|
+
export function initializePaths() {
|
|
54
|
+
ROOT_DIR = getProjectRoot();
|
|
55
|
+
KISHARE_ROOT = getKishareRoot();
|
|
56
|
+
OUTPUT_DIR = path.join(KISHARE_ROOT, 'public');
|
|
57
|
+
|
|
58
|
+
console.log(`Config file: ${path.join(ROOT_DIR, 'kishare-config.json')}`);
|
|
59
|
+
console.log(`Root directory (project files): ${ROOT_DIR}`);
|
|
60
|
+
console.log(`KiShare root: ${KISHARE_ROOT}`);
|
|
61
|
+
console.log(`Output directory: ${OUTPUT_DIR}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Get project directories from config (defaults to ['projects'])
|
|
65
|
+
export function getProjectDirs(config: WorkspaceConfig): string[] {
|
|
66
|
+
const dirs = config.projectDirs || ['projects'];
|
|
67
|
+
return dirs.map(dir => path.join(ROOT_DIR, dir));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Getters for initialized paths
|
|
71
|
+
export function getRootDir(): string {
|
|
72
|
+
if (!ROOT_DIR) {
|
|
73
|
+
throw new Error('Paths not initialized. Call initializePaths() first.');
|
|
74
|
+
}
|
|
75
|
+
return ROOT_DIR;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getOutputDir(): string {
|
|
79
|
+
if (!OUTPUT_DIR) {
|
|
80
|
+
throw new Error('Paths not initialized. Call initializePaths() first.');
|
|
81
|
+
}
|
|
82
|
+
return OUTPUT_DIR;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function getKishareRootDir(): string {
|
|
86
|
+
if (!KISHARE_ROOT) {
|
|
87
|
+
throw new Error('Paths not initialized. Call initializePaths() first.');
|
|
88
|
+
}
|
|
89
|
+
return KISHARE_ROOT;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Get list of git-tracked files in the specified project directories
|
|
93
|
+
export function getTrackedFiles(projectDirs: string[]): Set<string> {
|
|
94
|
+
const allFiles = new Set<string>();
|
|
95
|
+
for (const projectDir of projectDirs) {
|
|
96
|
+
try {
|
|
97
|
+
const relativeDir = path.relative(getRootDir(), projectDir);
|
|
98
|
+
console.log(`Getting git tracked files for ${relativeDir}...`);
|
|
99
|
+
const output = execSync(`git ls-files --recurse-submodules "${relativeDir}/"`, {
|
|
100
|
+
cwd: getRootDir(),
|
|
101
|
+
encoding: 'utf-8',
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
output.trim().split('\n').filter(f => f).forEach(f => allFiles.add(f));
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.warn(`Could not get git tracked files for ${projectDir}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return allFiles;
|
|
111
|
+
}
|