@puls-atlas/cli
Version:
The Puls Atlas CLI tool for managing Atlas projects
156 lines • 4.97 kB
JavaScript
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;
};