prebuild-less-cache.js
1 'use strict'; 2 3 const fs = require('fs'); 4 const klawSync = require('klaw-sync'); 5 const glob = require('glob'); 6 const path = require('path'); 7 const LessCache = require('less-cache'); 8 9 const CONFIG = require('../config'); 10 const LESS_CACHE_VERSION = require('less-cache/package.json').version; 11 const FALLBACK_VARIABLE_IMPORTS = 12 '@import "variables/ui-variables";\n@import "variables/syntax-variables";\n'; 13 14 module.exports = function() { 15 const cacheDirPath = path.join( 16 CONFIG.intermediateAppPath, 17 'less-compile-cache' 18 ); 19 console.log(`Generating pre-built less cache in ${cacheDirPath}`); 20 21 // Group bundled packages into UI themes, syntax themes, and non-theme packages 22 const uiThemes = []; 23 const syntaxThemes = []; 24 const nonThemePackages = []; 25 for (let packageName in CONFIG.appMetadata.packageDependencies) { 26 const packageMetadata = require(path.join( 27 CONFIG.intermediateAppPath, 28 'node_modules', 29 packageName, 30 'package.json' 31 )); 32 if (packageMetadata.theme === 'ui') { 33 uiThemes.push(packageName); 34 } else if (packageMetadata.theme === 'syntax') { 35 syntaxThemes.push(packageName); 36 } else { 37 nonThemePackages.push(packageName); 38 } 39 } 40 41 CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath = {}; 42 function saveIntoSnapshotAuxiliaryData(absoluteFilePath, content) { 43 const relativeFilePath = path.relative( 44 CONFIG.intermediateAppPath, 45 absoluteFilePath 46 ); 47 if ( 48 !CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath.hasOwnProperty( 49 relativeFilePath 50 ) 51 ) { 52 CONFIG.snapshotAuxiliaryData.lessSourcesByRelativeFilePath[ 53 relativeFilePath 54 ] = { 55 content: content, 56 digest: LessCache.digestForContent(content) 57 }; 58 } 59 } 60 61 CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath = {}; 62 // Warm cache for every combination of the default UI and syntax themes, 63 // because themes assign variables which may be used in any style sheet. 64 for (let uiTheme of uiThemes) { 65 for (let syntaxTheme of syntaxThemes) { 66 // Build a LessCache instance with import paths based on the current theme combination 67 const lessCache = new LessCache({ 68 cacheDir: cacheDirPath, 69 fallbackDir: path.join( 70 CONFIG.atomHomeDirPath, 71 'compile-cache', 72 'prebuild-less', 73 LESS_CACHE_VERSION 74 ), 75 syncCaches: true, 76 resourcePath: CONFIG.intermediateAppPath, 77 importPaths: [ 78 path.join( 79 CONFIG.intermediateAppPath, 80 'node_modules', 81 syntaxTheme, 82 'styles' 83 ), 84 path.join( 85 CONFIG.intermediateAppPath, 86 'node_modules', 87 uiTheme, 88 'styles' 89 ), 90 path.join(CONFIG.intermediateAppPath, 'static', 'variables'), 91 path.join(CONFIG.intermediateAppPath, 'static') 92 ] 93 }); 94 95 // Store file paths located at the import paths so that we can avoid scanning them at runtime. 96 for (const absoluteImportPath of lessCache.getImportPaths()) { 97 const relativeImportPath = path.relative( 98 CONFIG.intermediateAppPath, 99 absoluteImportPath 100 ); 101 if ( 102 !CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath.hasOwnProperty( 103 relativeImportPath 104 ) 105 ) { 106 CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath[ 107 relativeImportPath 108 ] = []; 109 for (const importedFile of klawSync(absoluteImportPath, { 110 nodir: true 111 })) { 112 CONFIG.snapshotAuxiliaryData.importedFilePathsByRelativeImportPath[ 113 relativeImportPath 114 ].push( 115 path.relative(CONFIG.intermediateAppPath, importedFile.path) 116 ); 117 } 118 } 119 } 120 121 // Cache all styles in static; don't append variable imports 122 for (let lessFilePath of glob.sync( 123 path.join(CONFIG.intermediateAppPath, 'static', '**', '*.less') 124 )) { 125 cacheCompiledCSS(lessCache, lessFilePath, false); 126 } 127 128 // Cache styles for all bundled non-theme packages 129 for (let nonThemePackage of nonThemePackages) { 130 for (let lessFilePath of glob.sync( 131 path.join( 132 CONFIG.intermediateAppPath, 133 'node_modules', 134 nonThemePackage, 135 '**', 136 '*.less' 137 ) 138 )) { 139 cacheCompiledCSS(lessCache, lessFilePath, true); 140 } 141 } 142 143 // Cache styles for this UI theme 144 const uiThemeMainPath = path.join( 145 CONFIG.intermediateAppPath, 146 'node_modules', 147 uiTheme, 148 'index.less' 149 ); 150 cacheCompiledCSS(lessCache, uiThemeMainPath, true); 151 for (let lessFilePath of glob.sync( 152 path.join( 153 CONFIG.intermediateAppPath, 154 'node_modules', 155 uiTheme, 156 '**', 157 '*.less' 158 ) 159 )) { 160 if (lessFilePath !== uiThemeMainPath) { 161 saveIntoSnapshotAuxiliaryData( 162 lessFilePath, 163 fs.readFileSync(lessFilePath, 'utf8') 164 ); 165 } 166 } 167 168 // Cache styles for this syntax theme 169 const syntaxThemeMainPath = path.join( 170 CONFIG.intermediateAppPath, 171 'node_modules', 172 syntaxTheme, 173 'index.less' 174 ); 175 cacheCompiledCSS(lessCache, syntaxThemeMainPath, true); 176 for (let lessFilePath of glob.sync( 177 path.join( 178 CONFIG.intermediateAppPath, 179 'node_modules', 180 syntaxTheme, 181 '**', 182 '*.less' 183 ) 184 )) { 185 if (lessFilePath !== syntaxThemeMainPath) { 186 saveIntoSnapshotAuxiliaryData( 187 lessFilePath, 188 fs.readFileSync(lessFilePath, 'utf8') 189 ); 190 } 191 } 192 } 193 } 194 195 for (let lessFilePath of glob.sync( 196 path.join( 197 CONFIG.intermediateAppPath, 198 'node_modules', 199 'atom-ui', 200 '**', 201 '*.less' 202 ) 203 )) { 204 saveIntoSnapshotAuxiliaryData( 205 lessFilePath, 206 fs.readFileSync(lessFilePath, 'utf8') 207 ); 208 } 209 210 function cacheCompiledCSS(lessCache, lessFilePath, importFallbackVariables) { 211 let lessSource = fs.readFileSync(lessFilePath, 'utf8'); 212 if (importFallbackVariables) { 213 lessSource = FALLBACK_VARIABLE_IMPORTS + lessSource; 214 } 215 lessCache.cssForFile(lessFilePath, lessSource); 216 saveIntoSnapshotAuxiliaryData(lessFilePath, lessSource); 217 } 218 };