Skip to content

Commit

Permalink
refactor(contented): move walker & watcher logic into ./contented (#147)
Browse files Browse the repository at this point in the history
<!--  Thanks for sending a pull request! -->

#### What this PR does / why we need it:

As per the title, this refactor keeps `contented` concerns in the `./contented` directory while commands sit at the root.
  • Loading branch information
fuxingloh authored Sep 1, 2022
1 parent 6df0600 commit 7b6eeae
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 80 deletions.
16 changes: 16 additions & 0 deletions packages/contented/src/commands/BaseCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Command } from 'clipanion';
import { join } from 'node:path';

import { ContentedConfig } from '../index.js';

export abstract class BaseCommand extends Command {
static paths = [[`build`]];

async loadConfig(): Promise<ContentedConfig> {
const configPath = join(process.cwd(), 'contented.config.js');
// eslint-disable-next-line no-console
console.log(`Loading config from: ${configPath}`);
const config = await import(configPath);
return config.default;
}
}
41 changes: 11 additions & 30 deletions packages/contented/src/commands/BuildCommand.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,20 @@
import { ContentedProcessor, ContentedProcessorResult } from '@birthdayresearch/contented-processor';
import { Command } from 'clipanion';
import walk from 'ignore-walk';
import { join } from 'node:path';
import { ContentedProcessor } from '@birthdayresearch/contented-processor';

import { ContentedConfig } from '../index.js';
import { BaseCommand } from './BaseCommand.js';
import { ContentedWalker } from './contented/ContentedWalker.js';

export class BuildCommand extends Command {
/* eslint-disable no-console */
/**
* `contented build` the dist
*/
export class BuildCommand extends BaseCommand {
static paths = [[`build`]];

async execute() {
const config = await this.loadConfig();
config.processor.outDir = './dist';
const processor = new ContentedProcessor(config.processor);
await this.walk(processor);
}

async walk(processor: ContentedProcessor): Promise<ContentedProcessorResult> {
const files = await walk({
path: processor.rootPath,
ignoreFiles: ['.contentedignore', '.gitignore', '.npmignore'],
});
config.processor.outDir = config.processor.outDir ?? './dist';

const result: ContentedProcessorResult = await processor.build(...files);
Object.entries(result.pipelines).forEach(([key, value]) => {
console.log(`Processed ${value.length} files for pipeline "${key}".`);
});
return result;
}

async loadConfig(): Promise<ContentedConfig> {
const configPath = join(process.cwd(), 'contented.config.js');
console.log(`Loading config from: ${configPath}`);
const config = await import(configPath);
return config.default;
const processor = new ContentedProcessor(config.processor);
const walker = new ContentedWalker(processor);
await walker.walk();
}
/* eslint-enable no-console */
}
14 changes: 10 additions & 4 deletions packages/contented/src/commands/GenerateCommand.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { ContentedProcessor } from '@birthdayresearch/contented-processor';

import { BuildCommand } from './BuildCommand.js';
import { ContentedPreview } from './ContentedPreview.js';
import { BaseCommand } from './BaseCommand.js';
import { ContentedPreview } from './contented/ContentedPreview.js';
import { ContentedWalker } from './contented/ContentedWalker.js';

export class GenerateCommand extends BuildCommand {
/**
* `contented generate` the preview website
*/
export class GenerateCommand extends BaseCommand {
static paths = [[`generate`]];

async execute() {
const config = await this.loadConfig();
const processor = new ContentedProcessor(config.processor);
await this.walk(processor);

const walker = new ContentedWalker(processor);
await walker.walk();

const preview = new ContentedPreview(config.preview);
await preview.init();
Expand Down
50 changes: 11 additions & 39 deletions packages/contented/src/commands/WatchCommand.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,23 @@
import { ContentedProcessor } from '@birthdayresearch/contented-processor';
import watcher, { Event } from '@parcel/watcher';
import debounce from 'debounce';
import { join, relative } from 'node:path';

import { BuildCommand } from './BuildCommand.js';
import { BaseCommand } from './BaseCommand.js';
import { ContentedWalker } from './contented/ContentedWalker.js';
import { ContentedWatcher } from './contented/ContentedWatcher.js';

export class WatchCommand extends BuildCommand {
/* eslint-disable no-console */
/**
* `contented watch` files and automatically rebuild when changed into output directory `.contented`
*/
export class WatchCommand extends BaseCommand {
static paths = [[`watch`]];

async execute() {
const config = await this.loadConfig();
const processor = new ContentedProcessor(config.processor);

await this.walk(processor);
await this.watch(processor);
}

async watch(processor: ContentedProcessor) {
const processWalk = debounce(() => {
this.walk(processor);
}, 1000);

const processFile = async (path: string) => {
const file = relative(processor.rootPath, path);
const contents = await processor.process(file);
contents.forEach((content) => {
console.log(`Processed "${file}" as "${content?.path}"`);
});
};

const subscribe = (err: Error | null, events: Event[]) => {
const filtered = events.filter((value) => !value.path.endsWith('~'));
if (filtered.some((value) => value.type !== 'update')) {
processWalk();
} else {
for (const event of filtered) {
// TODO(fuxingloh): when a file generate multi FileContent, it should automatically refresh index
processFile(event.path);
}
}
};
const walker = new ContentedWalker(processor);
const watcher = new ContentedWatcher(processor);

const relativeDir = relative(processor.rootPath, process.cwd());
await watcher.subscribe(processor.rootPath, subscribe, {
ignore: ['.contented', join(relativeDir, '.contented'), 'node_modules', '.git', '.idea', '.vscode'],
});
await walker.walk();
await watcher.watch();
}
/* eslint-enable no-console */
}
19 changes: 14 additions & 5 deletions packages/contented/src/commands/WriteCommand.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import { ContentedProcessor } from '@birthdayresearch/contented-processor';

import { ContentedPreview } from './ContentedPreview.js';
import { WatchCommand } from './WatchCommand.js';
import { BaseCommand } from './BaseCommand.js';
import { ContentedPreview } from './contented/ContentedPreview.js';
import { ContentedWalker } from './contented/ContentedWalker.js';
import { ContentedWatcher } from './contented/ContentedWatcher.js';

export class WriteCommand extends WatchCommand {
/**
* `contented write` to watch files and automatically rebuild when changed into output directory `.contented`
* with a preview website
*/
export class WriteCommand extends BaseCommand {
static paths = [[`write`]];

async execute() {
const config = await this.loadConfig();
const processor = new ContentedProcessor(config.processor);

await this.walk(processor);
await this.watch(processor);
const walker = new ContentedWalker(processor);
const watcher = new ContentedWatcher(processor);

await walker.walk();
await watcher.watch();

const preview = new ContentedPreview(config.preview);
await preview.init();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { cp, writeFile } from 'node:fs/promises';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

import { PreviewConfig } from '../index.js';
import { PreviewConfig } from '../../index.js';

export class ContentedPreview {
previewDir = `${process.cwd()}/.contented/.preview`;

constructor(protected readonly config: PreviewConfig) {}

async init() {
const source = join(this.getDirname(), '/../.preview');
const source = join(this.getDirname(), '/../../.preview');
await cp(source, this.previewDir, { recursive: true });
await writeFile(join(this.previewDir, '.env'), generateEnvData(this.config));
}
Expand Down
20 changes: 20 additions & 0 deletions packages/contented/src/commands/contented/ContentedWalker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ContentedProcessor, ContentedProcessorResult } from '@birthdayresearch/contented-processor';
import walk from 'ignore-walk';

export class ContentedWalker {
constructor(protected processor: ContentedProcessor) {}

async walk(): Promise<ContentedProcessorResult> {
const files = await walk({
path: this.processor.rootPath,
ignoreFiles: ['.contentedignore', '.gitignore', '.npmignore'],
});

const result: ContentedProcessorResult = await this.processor.build(...files);
Object.entries(result.pipelines).forEach(([key, value]) => {
// eslint-disable-next-line no-console
console.log(`Processed ${value.length} files for pipeline "${key}".`);
});
return result;
}
}
36 changes: 36 additions & 0 deletions packages/contented/src/commands/contented/ContentedWatcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { FileContent } from '@birthdayresearch/contented-pipeline';
import watcher, { Event } from '@parcel/watcher';
import { relative } from 'node:path';

import { ContentedWalker } from './ContentedWalker.js';

export class ContentedWatcher extends ContentedWalker {
async watch(): Promise<void> {
const listen = (err: Error | null, events: Event[]) => {
const filtered = events.filter((value) => !value.path.endsWith('~'));
if (filtered.some((value) => value.type !== 'update')) {
this.walk();
} else {
for (const event of filtered) {
// TODO(fuxingloh): when a file generate multi FileContent, it should automatically refresh index
this.processFile(event.path);
}
}
};

const dotContentedDir = relative(this.processor.rootPath, process.cwd());
await watcher.subscribe(this.processor.rootPath, listen, {
ignore: [dotContentedDir, '.contented', 'node_modules', '.git', '.idea', '.vscode'],
});
}

private async processFile(path: string): Promise<FileContent[]> {
const file = relative(this.processor.rootPath, path);
const contents = await this.processor.process(file);
contents.forEach((content) => {
// eslint-disable-next-line no-console
console.log(`Processed "${file}" as "${content?.path}"`);
});
return contents;
}
}

0 comments on commit 7b6eeae

Please sign in to comment.