Skip to content

Instantly share code, notes, and snippets.

@iohub
Created February 20, 2025 01:45
Show Gist options
  • Select an option

  • Save iohub/00a9406b317d3865d4ea51222c13b356 to your computer and use it in GitHub Desktop.

Select an option

Save iohub/00a9406b317d3865d4ea51222c13b356 to your computer and use it in GitHub Desktop.

Revisions

  1. iohub created this gist Feb 20, 2025.
    85 changes: 85 additions & 0 deletions cache.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,85 @@
    import * as fs from 'fs';
    import * as path from 'path';
    import * as crypto from 'crypto';

    export interface CacheEntry {
    key: string;
    timestamp: number;
    files: string[];
    }

    export class BuildCache {
    private cacheDir: string;
    private cacheFile: string;
    private entries: Map<string, CacheEntry>;

    constructor(cacheDir: string) {
    this.cacheDir = cacheDir;
    this.cacheFile = path.join(cacheDir, 'cache.json');
    this.entries = new Map();
    this.load();
    }

    private load() {
    try {
    if (fs.existsSync(this.cacheFile)) {
    const data = JSON.parse(fs.readFileSync(this.cacheFile, 'utf8'));
    this.entries = new Map(Object.entries(data));
    }
    } catch (err) {
    console.warn('Failed to load build cache:', err);
    }
    }

    private save() {
    try {
    const data = Object.fromEntries(this.entries);
    fs.writeFileSync(this.cacheFile, JSON.stringify(data, null, 2));
    } catch (err) {
    console.warn('Failed to save build cache:', err);
    }
    }

    public get(key: string): string[] | undefined {
    const entry = this.entries.get(key);
    if (!entry) return undefined;

    // Validate files still exist and haven't changed
    for (const file of entry.files) {
    try {
    const stat = fs.statSync(file);
    if (stat.mtimeMs > entry.timestamp) {
    this.entries.delete(key);
    return undefined;
    }
    } catch {
    this.entries.delete(key);
    return undefined;
    }
    }

    return entry.files;
    }

    public set(key: string, files: string[]) {
    this.entries.set(key, {
    key,
    timestamp: Date.now(),
    files
    });
    this.save();
    }

    public clear() {
    this.entries.clear();
    this.save();
    }
    }

    export function createHash(...inputs: string[]): string {
    const hash = crypto.createHash('sha256');
    for (const input of inputs) {
    hash.update(input);
    }
    return hash.digest('hex');
    }
    2 changes: 2 additions & 0 deletions cachesalt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    2024-12-11T00:28:56.838Z
    cache-version=1.0.0
    12 changes: 12 additions & 0 deletions compilecache.d.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    export interface CompilationCacheEntry {
    hash: string;
    outputFiles: { [path: string]: string };
    timestamp: number;
    }

    export declare class CompilationCache {
    constructor(cacheDir: string);
    getEntry(key: string, sourceHash: string): CompilationCacheEntry | undefined;
    setEntry(key: string, sourceHash: string, outputFiles: { [path: string]: string }): void;
    clear(): void;
    }
    67 changes: 67 additions & 0 deletions compilecache.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,67 @@
    import fs from 'fs';
    import path from 'path';
    import crypto from 'crypto';

    export interface CompilationCacheEntry {
    hash: string;
    outputFiles: { [path: string]: string }; // path -> content hash
    timestamp: number;
    }

    export class CompilationCache {
    private cacheDir: string;
    private cache: { [key: string]: CompilationCacheEntry } = {};

    constructor(cacheDir: string) {
    this.cacheDir = cacheDir;
    this.loadCache();
    }

    private loadCache() {
    try {
    const cacheFile = path.join(this.cacheDir, 'compilation-cache.json');
    if (fs.existsSync(cacheFile)) {
    this.cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
    }
    } catch (err) {
    // Start with empty cache if loading fails
    this.cache = {};
    }
    }

    private saveCache() {
    try {
    if (!fs.existsSync(this.cacheDir)) {
    fs.mkdirSync(this.cacheDir, { recursive: true });
    }
    fs.writeFileSync(
    path.join(this.cacheDir, 'compilation-cache.json'),
    JSON.stringify(this.cache, null, 2)
    );
    } catch (err) {
    console.error('Failed to save compilation cache:', err);
    }
    }

    public getEntry(key: string, sourceHash: string): CompilationCacheEntry | undefined {
    const entry = this.cache[key];
    if (entry && entry.hash === sourceHash) {
    return entry;
    }
    return undefined;
    }

    public setEntry(key: string, sourceHash: string, outputFiles: { [path: string]: string }) {
    this.cache[key] = {
    hash: sourceHash,
    outputFiles,
    timestamp: Date.now()
    };
    this.saveCache();
    }

    public clear() {
    this.cache = {};
    this.saveCache();
    }
    }
    77 changes: 77 additions & 0 deletions computeNodeModulesCacheKey.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,77 @@
    /*---------------------------------------------------------------------------------------------
    * Copyright (c) Microsoft Corporation. All rights reserved.
    * Licensed under the MIT License. See License.txt in the project root for license information.
    *--------------------------------------------------------------------------------------------*/

    import fs from 'fs';
    import path from 'path';
    import crypto from 'crypto';
    const { dirs } = require('../../npm/dirs');

    const ROOT = path.join(__dirname, '../../../');

    const shasum = crypto.createHash('sha256');

    // Add build configuration files
    shasum.update(fs.readFileSync(path.join(ROOT, 'build/.cachesalt')));
    shasum.update(fs.readFileSync(path.join(ROOT, '.npmrc')));
    shasum.update(fs.readFileSync(path.join(ROOT, 'build', '.npmrc')));
    shasum.update(fs.readFileSync(path.join(ROOT, 'remote', '.npmrc')));

    // Add build scripts that could affect the build
    const buildScripts = [
    'gulpfile.js',
    'build/gulpfile.js',
    'build/gulpfile.vscode.js',
    'build/lib/util.js'
    ];

    for (const script of buildScripts) {
    try {
    const scriptPath = path.join(ROOT, script);
    if (fs.existsSync(scriptPath)) {
    shasum.update(fs.readFileSync(scriptPath));
    }
    } catch (err) {
    console.warn(`Warning: Could not read build script ${script}`);
    }
    }

    // Add package.json and package-lock.json files
    for (const dir of dirs) {
    const packageJsonPath = path.join(ROOT, dir, 'package.json');
    const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString());
    const relevantPackageJsonSections = {
    dependencies: packageJson.dependencies,
    devDependencies: packageJson.devDependencies,
    optionalDependencies: packageJson.optionalDependencies,
    resolutions: packageJson.resolutions,
    distro: packageJson.distro,
    scripts: packageJson.scripts // Include build scripts
    };
    shasum.update(JSON.stringify(relevantPackageJsonSections));

    const packageLockPath = path.join(ROOT, dir, 'package-lock.json');
    shasum.update(fs.readFileSync(packageLockPath));
    }

    // Add environment variables that could affect the build
    const relevantEnvVars = [
    'VSCODE_QUALITY',
    'VSCODE_BUILD_STAGE',
    'VSCODE_BUILD_ARCH',
    'ENABLE_EXTENSIONS'
    ];

    for (const envVar of relevantEnvVars) {
    if (process.env[envVar]) {
    shasum.update(envVar + '=' + process.env[envVar]);
    }
    }

    // Add any other command line arguments
    for (let i = 2; i < process.argv.length; i++) {
    shasum.update(process.argv[i]);
    }

    process.stdout.write(shasum.digest('hex'));