diff --git a/.gitignore b/.gitignore index bfa34c1..c992673 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ coverage locales/ package-lock.json yarn.lock +examples/**/dist Thumbs.db tmp/ diff --git a/examples/typescript-jobserver/index.ts b/examples/typescript-jobserver/index.ts new file mode 100644 index 0000000..759f3f0 --- /dev/null +++ b/examples/typescript-jobserver/index.ts @@ -0,0 +1,29 @@ +import process from 'node:process'; +import * as path from 'node:path'; +import Bree from 'bree'; + +const bree = new Bree({ + /** + * Always set the root option when doing any type of + * compiling with bree. This just makes it clearer where + * bree should resolve the jobs folder from. By default it + * resolves to the jobs folder relative to where the program + * is executed. + */ + root: path.join(__dirname, 'jobs'), + /** + * We only need the default extension to be "ts" + * when we are running the app with ts-node - otherwise + * the compiled-to-js code still needs to use JS + */ + defaultExtension: process.env.TS_NODE ? 'ts' : 'js', + acceptedExtensions: ['.ts', '.js'], + jobs: [ + { cron: '* * * * *', name: 'job' }, + { cron: '* * * * *', name: 'defaults' } + ] +}); + +(async () => { + await bree.start(); +})(); diff --git a/examples/typescript-jobserver/jobs/defaults.ts b/examples/typescript-jobserver/jobs/defaults.ts new file mode 100644 index 0000000..17f7599 --- /dev/null +++ b/examples/typescript-jobserver/jobs/defaults.ts @@ -0,0 +1,9 @@ +import { parentPort } from 'node:worker_threads'; +import process from 'node:process'; + +console.log('Hello TypeScript Defaults!'); + +// signal to parent that the job is done +if (parentPort) parentPort.postMessage('done'); +// eslint-disable-next-line unicorn/no-process-exit +else process.exit(0); diff --git a/examples/typescript-jobserver/jobs/job.ts b/examples/typescript-jobserver/jobs/job.ts new file mode 100644 index 0000000..981171c --- /dev/null +++ b/examples/typescript-jobserver/jobs/job.ts @@ -0,0 +1,9 @@ +import { parentPort } from 'node:worker_threads'; +import process from 'node:process'; + +console.log('Hello TypeScript!'); + +// signal to parent that the job is done +if (parentPort) parentPort.postMessage('done'); +// eslint-disable-next-line unicorn/no-process-exit +else process.exit(0); diff --git a/examples/typescript-jobserver/package.json b/examples/typescript-jobserver/package.json new file mode 100644 index 0000000..f8242d9 --- /dev/null +++ b/examples/typescript-jobserver/package.json @@ -0,0 +1,22 @@ +{ + "name": "bree-js-example-jobserver", + "version": "1.0.0", + "description": "basic bree typescript jobserver example", + "main": "index.ts", + "author": "Mike Valstar (https://valstar.dev/)", + "license": "MIT", + "devDependencies": { + "@tsconfig/node20": "^20.1.2", + "@types/node": "^20.8.0", + "ts-node": "^10.9.1", + "tslib": "^2.6.2", + "typescript": "^5.2.2" + }, + "scripts": { + "dev": "TS_NODE=true NODE_OPTIONS=\"-r ts-node/register\" node index.ts", + "start": "rm -rf dist && tsc && node dist/index.js" + }, + "dependencies": { + "bree": "latest" + } +} diff --git a/examples/typescript-jobserver/tsconfig.json b/examples/typescript-jobserver/tsconfig.json new file mode 100644 index 0000000..8801e72 --- /dev/null +++ b/examples/typescript-jobserver/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "include": ["**/*.ts", "jobs/index.js"], + "compilerOptions": { + "outDir": "dist", + "moduleResolution": "NodeNext", + "esModuleInterop": true + } +} diff --git a/src/job-builder.js b/src/job-builder.js index 0ebd181..54965ae 100644 --- a/src/job-builder.js +++ b/src/job-builder.js @@ -3,7 +3,7 @@ const isSANB = require('is-string-and-not-blank'); const isValidPath = require('is-valid-path'); const later = require('@breejs/later'); const { boolean } = require('boolean'); -const { isSchedule, parseValue } = require('./job-utils'); +const { isSchedule, parseValue, getJobPath } = require('./job-utils'); later.date.localTime(); @@ -12,9 +12,7 @@ const buildJob = (job, config) => { if (isSANB(job)) { const path = join( config.root, - config.acceptedExtensions.some((ext) => job.endsWith(ext)) - ? job - : `${job}.${config.defaultExtension}` + getJobPath(job, config.acceptedExtensions, config.defaultExtension) ); const jobObject = { @@ -61,9 +59,11 @@ const buildJob = (job, config) => { ? job.path : join( config.root, - config.acceptedExtensions.some((ext) => job.name.endsWith(ext)) - ? job.name - : `${job.name}.${config.defaultExtension}` + getJobPath( + job.name, + config.acceptedExtensions, + config.defaultExtension + ) ); if (isValidPath(path)) { diff --git a/src/job-utils.js b/src/job-utils.js index 7d28c13..2d6037b 100644 --- a/src/job-utils.js +++ b/src/job-utils.js @@ -95,9 +95,34 @@ const getJobNames = (jobs, excludeIndex) => { return names; }; +/** + * Processes job name to generate a partial path for the job + * Allows for resiliancy when the path extensions are either + * provided or not on both default and accepted extensions + * + * @param {string} name + * @param {number} acceptedExtensions + * @param {string} defaultExtension + * @returns {string} job path + */ +const getJobPath = (name, acceptedExtensions, defaultExtension) => { + const extFindArray = acceptedExtensions.map((ext) => { + return ext.startsWith('.') ? ext : `.${ext}`; + }); + + const hasExt = extFindArray.find((ext) => name.endsWith(ext)); + + if (hasExt) return name; + + return defaultExtension.startsWith('.') + ? `${name}${defaultExtension}` + : `${name}.${defaultExtension}`; +}; + module.exports = { getHumanToMs, getJobNames, + getJobPath, getName, isSchedule, parseValue diff --git a/src/job-validator.js b/src/job-validator.js index 71a8f7f..35b1ffa 100644 --- a/src/job-validator.js +++ b/src/job-validator.js @@ -5,7 +5,7 @@ const cron = require('cron-validate'); const isSANB = require('is-string-and-not-blank'); const isValidPath = require('is-valid-path'); -const { getName, isSchedule, parseValue } = require('./job-utils'); +const { getName, isSchedule, parseValue, getJobPath } = require('./job-utils'); const validateReservedJobName = (name) => { // Don't allow a job to have the `index` file name @@ -37,9 +37,7 @@ const validateStringJob = async (job, i, config) => { const path = join( config.root, - config.acceptedExtensions.some((ext) => job.endsWith(ext)) - ? job - : `${job}.${config.defaultExtension}` + getJobPath(job, config.acceptedExtensions, config.defaultExtension) ); const stats = await fs.promises.stat(path); @@ -86,9 +84,11 @@ const validateJobPath = async (job, prefix, config) => { ? job.path : join( config.root, - config.acceptedExtensions.some((ext) => job.name.endsWith(ext)) - ? job.name - : `${job.name}.${config.defaultExtension}` + getJobPath( + job.name, + config.acceptedExtensions, + config.defaultExtension + ) ); if (isValidPath(path)) { try {