Skip to content

Commit

Permalink
refactor(preset): add watcher util
Browse files Browse the repository at this point in the history
  • Loading branch information
PeachScript committed Apr 20, 2020
1 parent 671c7be commit cf5d5ca
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 21 deletions.
24 changes: 3 additions & 21 deletions packages/preset-dumi/src/transformer/remark/externalDemo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import ctx from '../../context';
import { getModuleResolvePath } from '../../utils/moduleResolver';
import transformer, { TransformResult } from '../index';
import { addDemoRoute } from '../../routes/getDemoRoutes';
import { saveFileOnDepChange, closeWatchersForFile } from '../../utils/watcher';

const DEMO_TOKEN_EXP = /<(code) ([^>]+?)\/?>/;
const fileWatchers: { [key: string]: fs.FSWatcher[] } = {};

/**
* simple parser for parse HTML attributes
Expand All @@ -24,27 +24,9 @@ export function HTMLAttrParser(str: string): { [key: string]: any } {
return attrs;
}

function watchExternalDemoChange(demoPath: string, parentPath: string) {
if (process.env.NODE_ENV === 'development') {
// only watch under development mode
fileWatchers[parentPath] = (fileWatchers[parentPath] || []).concat(
fs.watch(demoPath, () => {
// trigger parent file change to update frontmatter when demo file change
fs.writeFileSync(parentPath, fs.readFileSync(parentPath));
}),
);
}
}

export default function externalDemo() {
// clear exist watchers, use for unlink some demo from md file
if (fileWatchers[this.data('fileAbsPath')]) {
fileWatchers[this.data('fileAbsPath')].forEach(watcher => {
watcher.close();
});

delete fileWatchers[this.data('fileAbsPath')];
}
closeWatchersForFile(this.data('fileAbsPath'));

return ast => {
visit(ast, 'html', (node, i, parent) => {
Expand Down Expand Up @@ -82,7 +64,7 @@ export default function externalDemo() {
},
});

watchExternalDemoChange(absPath, this.data('fileAbsPath'));
saveFileOnDepChange(this.data('fileAbsPath'), absPath);
} else if (matches[1]) {
ctx.umi.logger.error(
`[dumi]: expected a code element with valid src property but got ${node.value}`,
Expand Down
81 changes: 81 additions & 0 deletions packages/preset-dumi/src/utils/watcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import fs from 'fs';

interface IWatcherItem {
options: {
parentFilePath?: string;
watchFilePath?: string;
listener?: (event: string, filename: string) => void;
};
watcher: fs.FSWatcher;
}

const isDev = () => process.env.NODE_ENV === 'development' || process.env.TEST_WATCHER;
let watchers: IWatcherItem[] = [];

export const watchFileChange = (
filePath: string,
listener: IWatcherItem['options']['listener'],
): IWatcherItem => {
if (isDev()) {
// save watcher
const watcher = {
options: { watchFilePath: filePath, listener },
watcher: fs.watch(filePath, listener),
};
watchers.push(watcher);

return watcher;
}
};

export const saveFileOnDepChange = (parentFilePath: string, depPath: string) => {
if (isDev()) {
const watcher = watchFileChange(depPath, () => {
triggerFileChange(parentFilePath);
});

watcher.options.parentFilePath = parentFilePath;
}
};

export const closeWatcher = (item: IWatcherItem) => {
// close & remove watcher
item.watcher.close();
watchers.splice(watchers.indexOf(item), 1);
};

export const closeWatchersForFile = (filePath: string) => {
const relatedWatchers = getWatchersForFile(filePath);

// close all related watchers
relatedWatchers.forEach(item => closeWatcher(item));
};

export const getWatchersForFile = (filePath: string) => {
const result = new Set<IWatcherItem>();

function loop(loopFilePath: string) {
// find all related watchers, include dep file
watchers.forEach(item => {
if (
item.options.watchFilePath === loopFilePath ||
item.options.parentFilePath === loopFilePath
) {
result.add(item);

// continue to close watcher for related files
if (item.options.watchFilePath !== loopFilePath) {
loop(item.options.watchFilePath);
}
}
});
}

loop(filePath);

return Array.from(result);
};

export const triggerFileChange = (filePath: string) => {
fs.writeFileSync(filePath, fs.readFileSync(filePath));
};

0 comments on commit cf5d5ca

Please sign in to comment.