UNPKG

@puls-atlas/cli

Version:

The Puls Atlas CLI tool for managing Atlas projects

156 lines 4.97 kB
import { extract } from 'tar'; import { globby } from 'globby'; import { rimraf } from 'rimraf'; import checksum from 'checksum'; import path from 'path'; import fs from 'fs'; import { execSync, logger } from './../../utils/index.js'; const libraryHasChanged = async (name, libDir, targetDir, hashStore) => { const hashFile = path.join(targetDir, 'node_modules', name, '.link-deps-hash'); const referenceContents = fs.existsSync(hashFile) ? fs.readFileSync(hashFile, 'utf8') : ''; const libFiles = await findFiles(libDir, targetDir); const hashes = []; for await (const file of libFiles) { if (file) { hashes.push(await getFileHash(path.join(libDir, file))); } } const contents = libFiles.map((file, index) => `${hashes[index]} ${file}`).join('\n'); hashStore.file = hashFile; hashStore.hash = contents; if (referenceContents === '') { logger.info('[link] First time linking'); return true; } if (contents === referenceContents) { logger.info('[link] No changes'); return false; } const contentsLines = contents.split('\n'); const refLines = referenceContents.split('\n'); for (let i = 0; i < contentsLines.length; i++) { if (contentsLines[i] !== refLines[i]) { logger.info(`[link] Changed file: ${libFiles[i]}`); break; } } return true; }; const findFiles = async (libDir, targetDir) => { const ignore = ['**/*', '!node_modules', '!.git']; if (targetDir.indexOf(libDir) === 0) { ignore.push(`!${targetDir.substr(libDir.length + 1).split(path.sep)[0]}`); } const files = await globby(ignore, { gitignore: true, cwd: libDir, nodir: true }); return files.sort(); }; const buildLibrary = (name, dir) => { const libraryPkgJson = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')); if (!libraryPkgJson.name === name) { logger.error(`[link][ERROR] Mismatch in package name: found '${libraryPkgJson.name}', expected '${name}'`); process.exit(1); } if (libraryPkgJson.scripts && libraryPkgJson.scripts.build) { logger.info(`[link] Building ${name} in ${dir}`); execSync('npm run build', { cwd: dir, stdio: 'inherit' }); } }; const packAndInstallLibrary = (name, dir, targetDir) => { const libDestDir = path.join(targetDir, 'node_modules', name); let fullPackageName; try { logger.info('[link] Copying to local node_modules'); execSync('npm pack', { cwd: dir }); if (fs.existsSync(libDestDir)) { rimraf.sync(libDestDir); } fs.mkdirSync(libDestDir, { recursive: true }); const tmpName = name.replace(/[\s\/]/g, '-').replace(/@/g, ''); const regex = new RegExp(`^(at-)?${tmpName}(.*).tgz$`); const packagedName = fs.readdirSync(dir).find(file => regex.test(file)); fullPackageName = path.join(dir, packagedName); logger.info(`[link] Extracting "${fullPackageName}" to ${libDestDir}`); const [cwd, file] = [libDestDir, fullPackageName].map(absolutePath => path.relative(process.cwd(), absolutePath)); extract({ cwd, file, gzip: true, stripComponents: 1, sync: true }); } finally { if (fullPackageName) { fs.unlinkSync(fullPackageName); } } }; const getFileHash = async file => await new Promise((resolve, reject) => { checksum.file(file, (error, hash) => { if (error) { reject(error); } else { resolve(hash); } }); }); const clearCache = () => { const target = './app/node_modules/.cache'; if (fs.existsSync(target)) { logger.info('[link] Clearing cache'); rimraf.sync('./app/node_modules/.cache'); } }; const getLinkJson = () => JSON.parse(fs.readFileSync('./app/link.json', 'utf-8')); export default async filePath => { const deps = new Map(); if (fs.existsSync('./app/link.json')) { const linkJson = getLinkJson(); for (const [key, value] of Object.entries(linkJson)) { const src = value.src || value.path; if (value.enabled) { if (filePath) { if (!filePath.startsWith(src)) { continue; } } logger.info(`[link] Linking ${key}...`); deps.set(key, src); } } } if (deps.size > 0) { const targetDir = './app'; const depNames = deps.keys(); for await (const name of depNames) { const libDir = path.resolve(targetDir, deps.get(name)); const hashStore = { hash: '', file: '' }; if (!fs.existsSync(libDir)) { logger.error(`[link][ERROR] Directory ${libDir} does not exist`); process.exit(1); } const hasChanges = await libraryHasChanged(name, libDir, targetDir, hashStore); if (hasChanges) { clearCache(); buildLibrary(name, libDir); packAndInstallLibrary(name, libDir, targetDir); fs.writeFileSync(hashStore.file, hashStore.hash); logger.info(`[link] Re-installing ${name}...`); } } } return deps; };