From 814a9ce765445a004810bd4331f303349fd5dd70 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 13 Feb 2026 15:38:39 +0100 Subject: [PATCH] Refactor resource copying for transpile and bundle builds - Implemented copyAllNonTsFiles() for transpile/dev builds to copy all non-TS files from src/ to output, aligning with previous gulp behavior. - Retained copyResources() for production builds using curated resource patterns. - Removed unnecessary devOnlyResourcePatterns and testFixturePatterns. - Updated watch mode to accept any non-TS file changes. --- build/next/index.ts | 145 ++++++++++++++---------------------------- build/next/working.md | 14 ++++ 2 files changed, 60 insertions(+), 99 deletions(-) diff --git a/build/next/index.ts b/build/next/index.ts index 21a6aa99f8a..73a08933db1 100644 --- a/build/next/index.ts +++ b/build/next/index.ts @@ -229,18 +229,6 @@ const commonResourcePatterns = [ 'vs/workbench/browser/parts/editor/media/letterpress*.svg', ]; -// Resources only needed for dev/transpile builds (these get bundled into the main -// JS/CSS bundles for production, so separate copies are redundant) -const devOnlyResourcePatterns = [ - // Fonts (esbuild file loader copies to media/codicon.ttf for production) - 'vs/base/browser/ui/codicons/codicon/codicon.ttf', - - // Vendor JavaScript libraries (bundled into workbench main JS for production) - 'vs/base/common/marked/marked.js', - 'vs/base/common/semver/semver.js', - 'vs/base/browser/dompurify/dompurify.js', -]; - // Resources for desktop target const desktopResourcePatterns = [ ...commonResourcePatterns, @@ -368,28 +356,6 @@ function getResourcePatternsForTarget(target: BuildTarget): string[] { } } -// Test fixtures (only copied for development builds, not production) -const testFixturePatterns = [ - '**/test/**/*.json', - '**/test/**/*.txt', - '**/test/**/*.snap', - '**/test/**/*.tst', - '**/test/**/*.html', - '**/test/**/*.js', - '**/test/**/*.jxs', - '**/test/**/*.tsx', - '**/test/**/*.css', - '**/test/**/*.png', - '**/test/**/*.md', - '**/test/**/*.zip', - '**/test/**/*.pdf', - '**/test/**/*.qwoff', - '**/test/**/*.wuff', - '**/test/**/*.less', - // Files without extensions (executables, etc.) - '**/test/**/fixtures/executable/*', -]; - // ============================================================================ // Utilities // ============================================================================ @@ -511,34 +477,57 @@ async function compileStandaloneFiles(outDir: string, doMinify: boolean, target: console.log(`[standalone] Done`); } -async function copyCssFiles(outDir: string, excludeTests = false): Promise { - // Copy all CSS files from src to output (they're imported by JS) - const cssFiles = await globAsync('**/*.css', { +/** + * Copy ALL non-TypeScript files from src/ to the output directory. + * This matches the old gulp build behavior where `gulp.src('src/**')` streams + * every file and non-TS files bypass the compiler via tsFilter.restore. + * Used for development/transpile builds only - production bundles use + * copyResources() with curated per-target patterns instead. + */ +async function copyAllNonTsFiles(outDir: string, excludeTests: boolean): Promise { + console.log(`[resources] Copying all non-TS files to ${outDir}...`); + + const ignorePatterns = [ + // Exclude .ts files but keep .d.ts files (they're needed at runtime for type references) + '**/*.ts', + ]; + if (excludeTests) { + ignorePatterns.push('**/test/**'); + } + + const files = await globAsync('**/*', { + cwd: path.join(REPO_ROOT, SRC_DIR), + nodir: true, + ignore: ignorePatterns, + }); + + // Re-include .d.ts files that were excluded by the *.ts ignore + const dtsFiles = await globAsync('**/*.d.ts', { cwd: path.join(REPO_ROOT, SRC_DIR), ignore: excludeTests ? ['**/test/**'] : [], }); - for (const file of cssFiles) { + const allFiles = [...new Set([...files, ...dtsFiles])]; + + await Promise.all(allFiles.map(file => { const srcPath = path.join(REPO_ROOT, SRC_DIR, file); const destPath = path.join(REPO_ROOT, outDir, file); + return copyFile(srcPath, destPath); + })); - await copyFile(srcPath, destPath); - } - - return cssFiles.length; + console.log(`[resources] Copied ${allFiles.length} files`); } -async function copyResources(outDir: string, target: BuildTarget, excludeDevFiles = false, excludeTests = false): Promise { +/** + * Copy curated resource files for production bundles. + * Uses specific per-target patterns matching the old build's vscodeResourceIncludes, + * serverResourceIncludes, etc. Only called by bundle() - transpile uses copyAllNonTsFiles(). + */ +async function copyResources(outDir: string, target: BuildTarget): Promise { console.log(`[resources] Copying to ${outDir} for target '${target}'...`); let copied = 0; - const ignorePatterns: string[] = []; - if (excludeTests) { - ignorePatterns.push('**/test/**'); - } - if (excludeDevFiles) { - ignorePatterns.push('**/*-dev.html'); - } + const ignorePatterns = ['**/test/**', '**/*-dev.html']; const resourcePatterns = getResourcePatternsForTarget(target); for (const pattern of resourcePatterns) { @@ -556,47 +545,7 @@ async function copyResources(outDir: string, target: BuildTarget, excludeDevFile } } - // Copy test fixtures (only for development builds) - if (!excludeTests) { - for (const pattern of testFixturePatterns) { - const files = await globAsync(pattern, { - cwd: path.join(REPO_ROOT, SRC_DIR), - }); - - for (const file of files) { - const srcPath = path.join(REPO_ROOT, SRC_DIR, file); - const destPath = path.join(REPO_ROOT, outDir, file); - - await copyFile(srcPath, destPath); - copied++; - } - } - } - - // Copy dev-only resources (vendor JS, codicon font) - only for development/transpile - // builds. In production bundles these are inlined by esbuild. - if (!excludeDevFiles) { - for (const pattern of devOnlyResourcePatterns) { - const files = await globAsync(pattern, { - cwd: path.join(REPO_ROOT, SRC_DIR), - ignore: ignorePatterns, - }); - for (const file of files) { - await copyFile(path.join(REPO_ROOT, SRC_DIR, file), path.join(REPO_ROOT, outDir, file)); - copied++; - } - } - } - - // Copy CSS files (only for development/transpile builds, not production bundles - // where CSS is already bundled into combined files like workbench.desktop.main.css) - if (!excludeDevFiles) { - const cssCount = await copyCssFiles(outDir, excludeTests); - copied += cssCount; - console.log(`[resources] Copied ${copied} files (${cssCount} CSS)`); - } else { - console.log(`[resources] Copied ${copied} files (CSS skipped - bundled)`); - } + console.log(`[resources] Copied ${copied} files`); } // ============================================================================ @@ -978,8 +927,8 @@ ${tslib}`, console.log(`[mangle-privates] Total: ${totalClasses} classes, ${totalFields} fields, ${totalEdits} edits, ${totalElapsed}ms`); } - // Copy resources (exclude dev files and tests for production) - await copyResources(outDir, target, true, true); + // Copy resources (curated per-target patterns for production) + await copyResources(outDir, target); // Compile standalone TypeScript files (like Electron preload scripts) that cannot be bundled await compileStandaloneFiles(outDir, doMinify, target); @@ -1012,7 +961,7 @@ async function watch(): Promise { const t1 = Date.now(); try { await transpile(outDir, false); - await copyResources(outDir, 'desktop', false, false); + await copyAllNonTsFiles(outDir, false); console.log(`Finished transpilation with 0 errors after ${Date.now() - t1} ms`); } catch (err) { console.error('[watch] Initial build failed:', err); @@ -1063,9 +1012,6 @@ async function watch(): Promise { } }; - // Extensions to watch and copy (non-TypeScript resources) - const copyExtensions = ['.css', '.html', '.js', '.json', '.ttf', '.svg', '.png', '.mp3', '.scm', '.sh', '.ps1', '.psm1', '.fish', '.zsh', '.scpt']; - // Watch src directory using existing gulp-watch based watcher let debounceTimer: ReturnType | undefined; const srcDir = path.join(REPO_ROOT, SRC_DIR); @@ -1074,7 +1020,8 @@ async function watch(): Promise { watchStream.on('data', (file: { path: string }) => { if (file.path.endsWith('.ts') && !file.path.endsWith('.d.ts')) { pendingTsFiles.add(file.path); - } else if (copyExtensions.some(ext => file.path.endsWith(ext))) { + } else { + // Copy any non-TS file (matches old gulp build's `src/**` behavior) pendingCopyFiles.add(file.path); } @@ -1151,7 +1098,7 @@ async function main(): Promise { console.log(`[transpile] ${SRC_DIR} → ${outDir}${options.excludeTests ? ' (excluding tests)' : ''}`); const t1 = Date.now(); await transpile(outDir, options.excludeTests); - await copyResources(outDir, 'desktop', false, options.excludeTests); + await copyAllNonTsFiles(outDir, options.excludeTests); console.log(`[transpile] Done in ${Date.now() - t1}ms`); } break; diff --git a/build/next/working.md b/build/next/working.md index bbf23a99806..71aac3fbdaf 100644 --- a/build/next/working.md +++ b/build/next/working.md @@ -97,6 +97,20 @@ Two placeholders that need injection: **Lesson:** Don't add new output file formats that create parity differences with the old build. The old build is the reference. +### 7. Resource Copying: Transpile vs Bundle + +**Problem:** The new build used curated, specific resource pattern lists (e.g., `desktopResourcePatterns`) for **both** transpile/dev and production/bundle builds. Team members kept discovering missing resources because every new non-TS file in `src/` required manually adding its pattern. + +**Root cause:** The old gulp build uses `gulp.src('src/**')` for dev/transpile — a catch-all glob that streams **every file** in `src/`. Non-TS files bypass the compiler via `tsFilter` + `tsFilter.restore` and land in `out/` untouched. This is inherently complete. The old build only uses curated resource lists for **production packaging** (`vscodeResourceIncludes`, `serverResourceIncludes` in the gulpfiles). + +**Fix:** +- **Transpile/dev path** (`transpile` command, `--watch` mode): Now uses `copyAllNonTsFiles()` which copies ALL non-TS files from `src/` to the output, matching old `gulp.src('src/**')` behavior. No curated patterns needed. +- **Bundle/production path** (`bundle` command): Continues using `copyResources()` with curated per-target patterns, matching old `vscodeResourceIncludes` etc. +- Removed `devOnlyResourcePatterns` and `testFixturePatterns` — no longer needed since the broad copy handles all dev resources. +- Watch mode incremental copy now accepts **any** non-`.ts` file change (removed the `copyExtensions` allowlist). + +**Lesson:** Dev builds should copy everything (completeness matters); production builds should be selective (size matters). Don't mix the two strategies. + --- ## Testing the Fix