@nx/js
Version:
164 lines (163 loc) • 6.86 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.CopyAssetsHandler = exports.defaultFileEventHandler = void 0;
const picomatch = require("picomatch");
const node_fs_1 = require("node:fs");
const pathPosix = require("node:path/posix");
const path = require("node:path");
const ignore_1 = require("ignore");
const tinyglobby_1 = require("tinyglobby");
const devkit_1 = require("@nx/devkit");
const client_1 = require("nx/src/daemon/client/client");
const picocolors_1 = require("picocolors");
const defaultFileEventHandler = (events) => {
const dirs = new Set(events.map((event) => path.dirname(event.dest)));
dirs.forEach((d) => (0, node_fs_1.mkdirSync)(d, { recursive: true }));
events.forEach((event) => {
if (event.type === 'create' || event.type === 'update') {
if ((0, node_fs_1.lstatSync)(event.src).isFile()) {
(0, node_fs_1.copyFileSync)(event.src, event.dest);
}
}
else if (event.type === 'delete') {
(0, node_fs_1.rmSync)(event.dest, { recursive: true, force: true });
}
else {
devkit_1.logger.error(`Unknown file event: ${event.type}`);
}
const eventDir = path.dirname(event.src);
const relativeDest = path.relative(eventDir, event.dest);
devkit_1.logger.log(`\n${(0, picocolors_1.dim)(relativeDest)}`);
});
};
exports.defaultFileEventHandler = defaultFileEventHandler;
class CopyAssetsHandler {
constructor(opts) {
this.rootDir = opts.rootDir;
this.projectDir = opts.projectDir;
this.outputDir = opts.outputDir;
this.callback = opts.callback ?? exports.defaultFileEventHandler;
// TODO(jack): Should handle nested .gitignore files
this.ignore = (0, ignore_1.default)();
const gitignore = pathPosix.join(opts.rootDir, '.gitignore');
const nxignore = pathPosix.join(opts.rootDir, '.nxignore');
if ((0, node_fs_1.existsSync)(gitignore))
this.ignore.add((0, node_fs_1.readFileSync)(gitignore).toString());
if ((0, node_fs_1.existsSync)(nxignore))
this.ignore.add((0, node_fs_1.readFileSync)(nxignore).toString());
this.assetGlobs = opts.assets.map((f) => {
let isGlob = false;
let pattern;
// Input and output directories are normalized to be relative to root
let input;
let output;
let ignore = null;
if (typeof f === 'string') {
pattern = f;
input = path.relative(opts.rootDir, opts.projectDir);
output = path.relative(opts.rootDir, opts.outputDir);
}
else {
isGlob = true;
pattern = pathPosix.join(f.input, f.glob);
input = f.input;
output = pathPosix.join(path.relative(opts.rootDir, opts.outputDir), f.output);
if (f.ignore)
ignore = f.ignore.map((ig) => pathPosix.join(f.input, ig));
}
return {
isGlob,
input,
pattern,
ignore,
output,
};
});
}
async processAllAssetsOnce() {
await Promise.all(this.assetGlobs.map(async (ag) => {
const pattern = this.normalizeAssetPattern(ag);
// globbing only supports Unix paths
const files = await (0, tinyglobby_1.globSync)(pattern.replace(/\\/g, '/'), {
cwd: this.rootDir,
dot: true, // enable hidden files
expandDirectories: false,
});
this.callback(this.filesToEvent(files, ag));
}));
}
processAllAssetsOnceSync() {
this.assetGlobs.forEach((ag) => {
const pattern = this.normalizeAssetPattern(ag);
// globbing only supports Unix paths
const files = (0, tinyglobby_1.globSync)(pattern.replace(/\\/g, '/'), {
cwd: this.rootDir,
dot: true, // enable hidden files
expandDirectories: false,
});
this.callback(this.filesToEvent(files, ag));
});
}
async watchAndProcessOnAssetChange() {
const unregisterFileWatcher = await client_1.daemonClient.registerFileWatcher({
watchProjects: 'all',
includeGlobalWorkspaceFiles: true,
}, (err, data) => {
if (err === 'closed') {
devkit_1.logger.error(`Watch error: Daemon closed the connection`);
process.exit(1);
}
else if (err) {
devkit_1.logger.error(`Watch error: ${err?.message ?? 'Unknown'}`);
}
else {
this.processWatchEvents(data.changedFiles);
}
});
return () => unregisterFileWatcher();
}
async processWatchEvents(events) {
const fileEvents = [];
for (const event of events) {
const pathFromRoot = event.path.startsWith(this.rootDir)
? path.relative(this.rootDir, event.path)
: event.path;
for (const ag of this.assetGlobs) {
if (picomatch(ag.pattern)(pathFromRoot) &&
!ag.ignore?.some((ig) => picomatch(ig)(pathFromRoot)) &&
!this.ignore.ignores(pathFromRoot)) {
const relPath = path.relative(ag.input, pathFromRoot);
const destPath = relPath.startsWith('..') ? pathFromRoot : relPath;
fileEvents.push({
type: event.type,
src: path.join(this.rootDir, pathFromRoot),
dest: path.join(this.rootDir, ag.output, destPath),
});
// Match first entry and skip the rest for this file.
break;
}
}
}
if (fileEvents.length > 0)
this.callback(fileEvents);
}
filesToEvent(files, assetGlob) {
return files.reduce((acc, src) => {
if (!assetGlob.ignore?.some((ig) => picomatch(ig)(src)) &&
!this.ignore.ignores(src)) {
const relPath = path.relative(assetGlob.input, src);
const dest = relPath.startsWith('..') ? src : relPath;
acc.push({
type: 'create',
src: path.join(this.rootDir, src),
dest: path.join(this.rootDir, assetGlob.output, dest),
});
}
return acc;
}, []);
}
normalizeAssetPattern(assetEntry) {
return typeof assetEntry === 'string' ? assetEntry : assetEntry.pattern;
}
}
exports.CopyAssetsHandler = CopyAssetsHandler;
;