UNPKG

rws-js-client

Version:
338 lines (242 loc) 9.81 kB
const sass = require('sass'); const fs = require('fs'); const path = require('path'); const { getTokenSourceMapRange } = require('typescript'); const _tools = require('../_tools'); const _COMPILE_DIR_NAME = 'compiled'; const FONT_REGEX = /url\(['"]?(.+?\.(woff|woff2|eot|ttf|otf))['"]?\)/g; const _DEV = true; const log = (args) => { if(_DEV){ console.log(args); } } class RWSPlugin { autoCompile = []; constructor(params){ this.node_modules_dir = (fileDir) => path.relative(fileDir, _tools.findRootWorkspacePath(process.cwd())) + '/node_modules/' if(!params){ params = {}; } if(!!params.autoCompile && params.autoCompile.length > 0){ this.autoCompile = params.autoCompile; } for (let index in this.autoCompile){ const sassFile = this.autoCompile[index]; this.compileFile(sassFile, true); } } extractCssImports(fileContent) { const regex = /@import\s+['"](.+?\.css)['"];?/g; let match; const imports = []; while ((match = regex.exec(fileContent)) !== null) { let importPath = match[1]; // Convert to absolute path or handle it as required imports.push(importPath); } fileContent = fileContent.replace(regex, ''); return [imports, fileContent]; } extractScssImports(fileContent) { const regex = /@import\s+['"](.+?\.scss)['"];?/g; let match; const imports = []; while ((match = regex.exec(fileContent)) !== null) { let importPath = match[1]; imports.push(importPath); } fileContent = fileContent.replace(regex, ''); return [imports, fileContent]; } replaceNodeModulesImport(fileContent, fileDir) { const regex = /@import\s+['"](.+?\.scss)['"];?/g; const resultCode = fileContent.replace(regex, (match, importPath) => { return `@import "${path.join(this.node_modules_dir(fileDir), importPath)}";`; }); return resultCode; } detectImports(code){ const regex = /@import\s+['"](.+?\.(css|scss))['"];?/g; return regex.test(code); } writeCssFile(scssFilePath, cssContent) { const cssFilePath = scssFilePath.replace('.scss', '.css'); let endCssFilePath = cssFilePath.split('/'); let endCssDir = [...endCssFilePath]; endCssDir[endCssDir.length - 1] = `${_COMPILE_DIR_NAME}`; endCssDir = endCssDir.join('/'); if (!fs.existsSync(endCssDir)) { fs.mkdirSync(endCssDir); } endCssFilePath[endCssFilePath.length - 1] = `${_COMPILE_DIR_NAME}/` + endCssFilePath[endCssFilePath.length - 1]; endCssFilePath = endCssFilePath.join('/'); fs.writeFileSync(endCssFilePath, cssContent); log('Saved CSS file: ' + endCssFilePath); } hasFontEmbeds(css){ return FONT_REGEX.test() } embedFontsInCss(css, cssFilePath) { let match; while ((match = FONT_REGEX.exec(css)) !== null) { const fontPath = match[1]; const absoluteFontPath = path.resolve(path.dirname(cssFilePath), fontPath); if (fs.existsSync(absoluteFontPath)) { const fontData = fs.readFileSync(absoluteFontPath); const base64Font = fontData.toString('base64'); const fontMimeType = this.getFontMimeType(path.extname(absoluteFontPath)); const fontDataURL = `data:${fontMimeType};base64,${base64Font}`; css = css.replace(new RegExp(match[0], 'g'), `url(${fontDataURL})`); } } return css; } getFontMimeType(extension) { switch (extension) { case '.woff': return 'font/woff'; case '.woff2': return 'font/woff2'; case '.eot': return 'application/vnd.ms-fontobject'; case '.ttf': return 'font/ttf'; case '.otf': return 'font/otf'; default: return 'application/octet-stream'; } } apply(compiler) { const _self = this; return; compiler.hooks.thisCompilation.tap('RWSSassPlugin', (compilation) => { compilation.hooks.buildModule.tap('RWSSassPlugin', (module) => { if (module.resource && /\.scss$/.test(module.resource)) { let scssPath = module.resource; this.compileFile(scssPath, true).catch((e) => { throw e; }) } }); }); } getCodeFromFile(filePath){ return fs.readFileSync(filePath, 'utf-8'); } replaceWithNodeModules(input, fileDir, absolute = false, token = '~'){ return input.replace(token, absolute ? `${path.resolve(_tools.findRootWorkspacePath(process.cwd()), 'node_modules')}/` : this.node_modules_dir(fileDir)); } compileFile(scssPath){ if(scssPath.split('')[0] === '~'){ scssPath = _self.replaceWithNodeModules(scssPath, path.dirname(scssPath)); } const cwdRequest = scssPath.indexOf('@cwd'); if( cwdRequest > -1){ scssPath = process.cwd() + '/' + scssPath.slice(cwdRequest+1); } let scssCode = this.getCodeFromFile(scssPath); return this.compileScssCode(scssCode, path.dirname(scssPath)); } processImports(imports, fileRootDir, importStorage = {}, sub = false){ let finalCode = ''; const getStorage = (sourceComponentPath, importedFileContent) => { const sourceComponentPathFormatted = sourceComponentPath.replace('/','_'); if(!(sourceComponentPathFormatted in importStorage)){ importStorage[sourceComponentPathFormatted] = importedFileContent; return importedFileContent; } return ''; } imports.forEach(originalImportPath => { let importPath = originalImportPath; // log(`Processing '${importPath}' import in: ` + fileRootDir); if(originalImportPath.split('')[0] === '~'){ importPath = this.replaceWithNodeModules(importPath, path.dirname(fileRootDir), true); } const cwdRequest = originalImportPath.indexOf('@cwd'); if( cwdRequest > -1){ importPath = process.cwd() + '/' + originalImportPath.slice(cwdRequest+4); } // log(`Procesed '${importPath}' import to: ` + importPath); importPath = path.resolve(fileRootDir, importPath); const replacedScssContent = getStorage(importPath, this.getCodeFromFile(importPath).replace(/\/\*[\s\S]*?\*\//g, '')); finalCode = replacedScssContent; let recursiveCode = ''; if(this.detectImports(finalCode)){ recursiveCode = this.processImports(this.extractScssImports(finalCode)[0], fileRootDir, importStorage, true); } finalCode = recursiveCode + '\n' + finalCode; finalCode = this.deleteImports(finalCode); }); return finalCode; } deleteImports(code = '') { const delregex = /@import\s+['"].+\.(scss|css)['"];\s*/g; return code.replace(delregex, ''); } compileScssCode(scssCode, fileRootDir, createFile = false){ const _self = this; // scssCode = this.replaceNodeModulesImport(scssCode, fileRootDir); const [scssImports] = this.extractScssImports(scssCode); let dotest = false; if(fileRootDir.indexOf('chat-message') > -1){ dotest = true; } if(scssImports && scssImports.length){ scssCode = this.deleteImports(scssCode); scssCode = this.processImports(scssImports, fileRootDir) + scssCode; } try { // if(fileRootDir.indexOf('chat-message') > -1){ // log(`Processing 0 cnt '${fileRootDir}' import : \n`); // console.log(scssCode); // } const result = sass.compileString(scssCode, { loadPaths: [fileRootDir] }) // if(fileRootDir.indexOf('chat-message') > -1){ // log(`Processing cnt '${fileRootDir}' import : \n` + result.css); // } let finalCss = result.css; // Check for CSS imports const [cssImports] = this.extractCssImports(scssCode); if(cssImports && cssImports.length){ // finalCss = this.processImports(cssImports, fileRootDir); } cssImports.forEach(originalImportPath => { let importPath = originalImportPath; if(originalImportPath.split('')[0] === '~'){ //console.warn(`RWS Sass compiler did not find '${importPath}' from '${module.resource}' looking inside node_modules...`); importPath = _self.replaceWithNodeModules(importPath, path.dirname(fileRootDir), true); if(!fs.existsSync(importPath)){ throw new Error(`No ${importPath} found.`) } } finalCss = finalCss.replace(/@import\s+['"](.+?)['"]\s*;/g, ''); const cssContent = _self.getCodeFromFile(importPath).replace(/\/\*[\s\S]*?\*\//g, ''); finalCss = cssContent + finalCss; if(_self.hasFontEmbeds()){ finalCss = _self.embedFontsInCss(finalCss); } }); // if(createFile){ // this.writeCssFile(scssPath, finalCss); // } return finalCss; } catch(err) { console.error('SASS Error in', fileRootDir); // console.log(err); console.error(err); throw err; return ''; }; } checkForImporterType(_module, checkTypeExt){ let importingFileExtension = ''; if (_module && _module.issuer && _module.issuer.resource) { importingFileExtension = path.extname(_module.issuer.resource); if(importingFileExtension === ('.' + checkTypeExt)){ return true; } }else{ return false; } return false } } module.exports = RWSPlugin;