Skip to content

Commit

Permalink
feat(cli): adds poll alternative to watch mode (#50)
Browse files Browse the repository at this point in the history
* feat(cli): adds poll alternative to watch mode

* fix(watch): changes default interval to 10s

* refactor(watch): better exit hook handling
  • Loading branch information
bezoerb authored Sep 15, 2022
1 parent 9fc0275 commit 626b70f
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 45 deletions.
118 changes: 76 additions & 42 deletions packages/contentful-ssg/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ program
.option('-v, --verbose', 'Verbose output')
.option('--url <url>', 'Webhook url.\nCan also be set via environment variable CSSG_WEBHOOK_URL')
.option('--no-cache', "Don't cache sync data")
.option('--poll', 'Use polling (usefull when ngrok tunnel is not an option)')
.option('--poll-intervall <intervall>', 'Change default intervall of 10000ms', '10000')
.option(
'--port <port>',
'Overwrite internal listener port. Useful for running the watcher in an environment with a single public port and a proxy configuration.\nCan also be set via environment variable CSSG_WEBHOOK_PORT'
Expand All @@ -184,7 +186,7 @@ program
const verified = await askMissing(config);
const useCache = Boolean(cmd?.cache ?? true);
const cache = initializeCache(verified);
// Await resetSync();

let prev: RunResult;
if (useCache && cache.hasSyncState()) {
prev = await cache.getSyncState();
Expand All @@ -197,58 +199,90 @@ program
await cache.setSyncState(prev);
}

let port = await getPort({ port: 1314 });
// Handle cache on exit
exitHook(async (cb: () => void) => {
try {
await Promise.all([
!useCache && cache.reset(),
useCache && prev && cache.setSyncState(prev),
]);
} catch (error: unknown) {
console.error('\nError:', error);
} finally {
cb();
}
});

if (process.env.CSSG_WEBHOOK_URL || cmd.url) {
const url = new URL(process.env.CSSG_WEBHOOK_URL || cmd.url);
if (url.port) {
port = parseInt(url.port, 10);
} else {
port = url.protocol === 'https:' ? 443 : 80;
if (cmd.poll) {
const poll = () => {
setTimeout(async () => {
prev = await run({ ...verified, sync: true }, prev);
if (useCache) {
await cache.setSyncState(prev);
}

poll();
}, parseInt(cmd.pollIntervall, 10));
};

poll();
} else {
let port = await getPort({ port: 1314 });

if (process.env.CSSG_WEBHOOK_URL || cmd.url) {
const url = new URL(process.env.CSSG_WEBHOOK_URL || cmd.url);
if (url.port) {
port = parseInt(url.port, 10);
} else {
port = url.protocol === 'https:' ? 443 : 80;
}
}
}

if (process.env.CSSG_WEBHOOK_PORT || cmd.port) {
port = parseInt(process.env.CSSG_WEBHOOK_PORT || cmd.port, 10);
}
if (process.env.CSSG_WEBHOOK_PORT || cmd.port) {
port = parseInt(process.env.CSSG_WEBHOOK_PORT || cmd.port, 10);
}

const app = getApp(async () => {
prev = await run({ ...verified, sync: true }, prev);
await cache.setSyncState(prev);
});
const app = getApp(async () => {
prev = await run({ ...verified, sync: true }, prev);
await cache.setSyncState(prev);
});

console.log();
console.log();

const server = app.listen(port, () => {
console.log(` Internal server listening on port :${chalk.cyan(port)}`);
});
const server = app.listen(port, () => {
console.log(` Internal server listening on port :${chalk.cyan(port)}`);
});

const stopServer = async () =>
new Promise((resolve, reject) => {
server.close((err) => {
if (err) {
reject(err);
} else {
resolve(true);
}
const stopServer = async () =>
new Promise((resolve, reject) => {
server.close((err) => {
if (err) {
reject(err);
} else {
resolve(true);
}
});
});
});

const url =
process.env.CSSG_WEBHOOK_URL || (cmd.url as string) || (await ngrok.connect(port));
console.log(` Listening for hooks on ${chalk.cyan(url)}`);
const webhook = await addWatchWebhook(verified as ContentfulConfig, url);
const url =
process.env.CSSG_WEBHOOK_URL || (cmd.url as string) || (await ngrok.connect(port));
console.log(` Listening for hooks on ${chalk.cyan(url)}`);
const webhook = await addWatchWebhook(verified as ContentfulConfig, url);

exitHook((cb) => {
Promise.allSettled([webhook.delete(), stopServer(), !useCache && cache.reset()])
.then(() => {
// Remove webhook & stop server on exit
exitHook(async (cb: () => void) => {
try {
const results = await Promise.allSettled([webhook.delete(), stopServer()]);
results.forEach((result) => {
if (result.status === 'rejected') {
console.error('\nError:', result?.reason?.message ?? result?.reason);
}
});
} finally {
cb();
})
.catch((err) => {
console.log('error:', err.message);
cb();
});
});
}
});
}
})
);

Expand Down
22 changes: 19 additions & 3 deletions packages/contentful-ssg/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ export const cleanupPrevData = (ctx: RuntimeContext, prev: RunResult) => {
});
};

const hasDeletions = (ctx: RuntimeContext) =>
(ctx?.data?.deletedAssets?.length ?? 0) + (ctx?.data?.deletedEntries?.length ?? 0) > 0;

const hasAdditions = (ctx: RuntimeContext) =>
(ctx?.data?.assets?.length ?? 0) + (ctx?.data?.entries?.length ?? 0) > 0;

const isEmpty = (ctx: RuntimeContext) => {
return !hasDeletions(ctx) && !hasAdditions(ctx);
};

/**
* Dump contentful objects to files
* @param {Object} config
Expand Down Expand Up @@ -136,16 +146,17 @@ export const run = async (
},
{
title: 'Localize data',
skip: (ctx) => isEmpty(ctx),
task: async (ctx) => localize(ctx),
},
{
title: 'Before Hook',
skip: (ctx) => !ctx.hooks.has('before'),
skip: (ctx) => !ctx.hooks.has('before') || isEmpty(ctx),
task: async (ctx) => ctx.hooks.before(),
},
{
title: 'Remove deleted files',
skip: (ctx) => (ctx.data.deletedEntries ?? []).length === 0,
skip: (ctx) => !hasDeletions(ctx),
task: async (ctx) => {
const { locales = [], deletedEntries = [] } = ctx.data;
const tasks = locales.map((locale) => ({
Expand Down Expand Up @@ -189,11 +200,16 @@ export const run = async (
},
{
title: 'Writing files',
skip: (ctx) => !hasAdditions(ctx),
task: async (ctx) => {
const { locales = [] } = ctx.data;

const tasks = locales.map((locale) => ({
title: `${locale.code}`,
skip: (ctx: RuntimeContext) =>
(ctx.localized?.get(locale.code)?.entryMap?.size ?? 0) +
(ctx.localized?.get(locale.code)?.assetMap?.size ?? 0) ===
0,
task: async () => {
const data = ctx.localized.get(locale.code);

Expand Down Expand Up @@ -283,7 +299,7 @@ export const run = async (
},
{
title: 'After Hook',
skip: (ctx) => !ctx.hooks.has('after'),
skip: (ctx) => !ctx.hooks.has('after') || isEmpty(ctx),
task: async (ctx) => ctx.hooks.after(),
},
{
Expand Down
1 change: 1 addition & 0 deletions packages/contentful-ssg/src/lib/cf-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export const hasSyncState = (config: Partial<ContentfulConfig>) => {

export const getSyncState = async (config: Partial<ContentfulConfig>): Promise<RunResult> => {
const file = getStateFile(config);

if (existsSync(file)) {
const data = await readFile(file);
return deserialize(data);
Expand Down

0 comments on commit 626b70f

Please sign in to comment.