'use strict'; const fs = require('fs'); const klawSync = require('klaw-sync'); const glob = require('glob'); const path = require('path'); const LessCache = require('less-cache'); const CONFIG = require('../config'); const LESS_CACHE_VERSION = require('less-cache/package.json').version; const FALLBACK_VARIABLE_IMPORTS = '@import "variables/ui-variables";\n@import "variables/syntax-variables";\n'; module.exports = function() { const cacheDirPath = path.join( CONFIG.intermediateAppPath, 'less-compile-cache' ); console.log(`Generating pre-built less cache in ${cacheDirPath}`); // Group bundled packages into UI themes, syntax themes, and non-theme packages const uiThemes = []; const syntaxThemes = []; const nonThemePackages = []; for (let packageName in CONFIG.appMetadata.packageDependencies) { const packageMetadata = require(path.join( CONFIG.intermediateAppPath, 'node_modules', packageName, 'package.json' )); if (packageMetadata.theme === 'ui') { uiThemes.push(packageName); } else if (packageMetadata.theme === 'syntax') { syntaxThemes.push(packageName); } else { nonThemePackages.push(packageName); } } CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath = {}; function saveIntoSnapshotAuxiliaryData(absoluteFilePath, content) { const relativeFilePath = path.relative( CONFIG.intermediateAppPath, absoluteFilePath ); if ( !CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath.hasOwnProperty( relativeFilePath ) ) { CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath[ relativeFilePath ] = { content: content, digest: LessCache.digestForContent(content) }; } } CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath = {}; // Warm cache for every combination of the default UI and syntax themes, // because themes assign variables which may be used in any style sheet. for (let uiTheme of uiThemes) { for (let syntaxTheme of syntaxThemes) { // Build a LessCache instance with import paths based on the current theme combination const lessCache = new LessCache({ cacheDir: cacheDirPath, fallbackDir: path.join( CONFIG.atomHomeDirPath, 'compile-cache', 'prebuild-less', LESS_CACHE_VERSION ), syncCaches: true, resourcePath: CONFIG.intermediateAppPath, importPaths: [ path.join( CONFIG.intermediateAppPath, 'node_modules', syntaxTheme, 'styles' ), path.join( CONFIG.intermediateAppPath, 'node_modules', uiTheme, 'styles' ), path.join(CONFIG.intermediateAppPath, 'static', 'variables'), path.join(CONFIG.intermediateAppPath, 'static') ] }); // Store file paths located at the import paths so that we can avoid scanning them at runtime. for (const absoluteImportPath of lessCache.getImportPaths()) { const relativeImportPath = path.relative( CONFIG.intermediateAppPath, absoluteImportPath ); if ( !CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath.hasOwnProperty( relativeImportPath ) ) { CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath[ relativeImportPath ] = []; for (const importedFile of klawSync(absoluteImportPath, { nodir: true })) { CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath[ relativeImportPath ].push( path.relative(CONFIG.intermediateAppPath, importedFile.path) ); } } } // Cache all styles in static; don't append variable imports for (let lessFilePath of glob.sync( path.join(CONFIG.intermediateAppPath, 'static', '**', '*.less') )) { cacheCompiledCSS(lessCache, lessFilePath, false); } // Cache styles for all bundled non-theme packages for (let nonThemePackage of nonThemePackages) { for (let lessFilePath of glob.sync( path.join( CONFIG.intermediateAppPath, 'node_modules', nonThemePackage, '**', '*.less' ) )) { cacheCompiledCSS(lessCache, lessFilePath, true); } } // Cache styles for this UI theme const uiThemeMainPath = path.join( CONFIG.intermediateAppPath, 'node_modules', uiTheme, 'index.less' ); cacheCompiledCSS(lessCache, uiThemeMainPath, true); for (let lessFilePath of glob.sync( path.join( CONFIG.intermediateAppPath, 'node_modules', uiTheme, '**', '*.less' ) )) { if (lessFilePath !== uiThemeMainPath) { saveIntoSnapshotAuxiliaryData( lessFilePath, fs.readFileSync(lessFilePath, 'utf8') ); } } // Cache styles for this syntax theme const syntaxThemeMainPath = path.join( CONFIG.intermediateAppPath, 'node_modules', syntaxTheme, 'index.less' ); cacheCompiledCSS(lessCache, syntaxThemeMainPath, true); for (let lessFilePath of glob.sync( path.join( CONFIG.intermediateAppPath, 'node_modules', syntaxTheme, '**', '*.less' ) )) { if (lessFilePath !== syntaxThemeMainPath) { saveIntoSnapshotAuxiliaryData( lessFilePath, fs.readFileSync(lessFilePath, 'utf8') ); } } } } for (let lessFilePath of glob.sync( path.join( CONFIG.intermediateAppPath, 'node_modules', 'atom-ui', '**', '*.less' ) )) { saveIntoSnapshotAuxiliaryData( lessFilePath, fs.readFileSync(lessFilePath, 'utf8') ); } function cacheCompiledCSS(lessCache, lessFilePath, importFallbackVariables) { let lessSource = fs.readFileSync(lessFilePath, 'utf8'); if (importFallbackVariables) { lessSource = FALLBACK_VARIABLE_IMPORTS + lessSource; } lessCache.cssForFile(lessFilePath, lessSource); saveIntoSnapshotAuxiliaryData(lessFilePath, lessSource); } };