Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: harrisiirak/cron-parser
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v5.0.0
Choose a base ref
...
head repository: harrisiirak/cron-parser
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v5.0.1
Choose a head ref
  • 6 commits
  • 13 files changed
  • 1 contributor

Commits on Feb 16, 2025

  1. Remove .npmignore as package.json files override is preferred

    harrisiirak committed Feb 16, 2025
    Copy the full SHA
    7ca5c94 View commit details
  2. Remove unused jest-runner-tsd dev dependency

    harrisiirak committed Feb 16, 2025
    Copy the full SHA
    9b1e0f3 View commit details
  3. Use dynamic imports in CronFileParser to avoid breaking the browser e…

    …nvironment (fixes #362)
    harrisiirak committed Feb 16, 2025
    Copy the full SHA
    683f585 View commit details
  4. Copy the full SHA
    9ddf705 View commit details
  5. Remove assert usage for better compability. Add and update test cases…

    … to code that was missing proper test cases
    harrisiirak committed Feb 16, 2025
    Copy the full SHA
    5e10db0 View commit details
  6. Bump version

    harrisiirak committed Feb 16, 2025
    Copy the full SHA
    557dbf9 View commit details
36 changes: 0 additions & 36 deletions .npmignore

This file was deleted.

4 changes: 2 additions & 2 deletions benchmarks/runner.ts
Original file line number Diff line number Diff line change
@@ -22,8 +22,8 @@ export async function initializeBenchmark(version?: string) {
const { version: resolvedVersion, packagePath } = await VersionManager.getPackageVersion(version);
console.log(`Using cron-parser version ${resolvedVersion} for comparison`);

const parser = require(packagePath);
oldParseExpression = parser.parseExpression as ParseExpressionFn;
const module = require(packagePath);
oldParseExpression = (module.default?.parse as ParseExpressionFn) ?? (module.parseExpression as ParseExpressionFn);
}

interface BenchmarkStats {
63 changes: 2 additions & 61 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -50,7 +50,6 @@
"husky": "^9.1.7",
"jest": "^29.7.0",
"jest-coverage-badges": "^1.0.0",
"jest-runner-tsd": "^6.0.0",
"lint-staged": "^15.4.3",
"prettier": "^3.4.2",
"rimraf": "^6.0.1",
9 changes: 4 additions & 5 deletions src/CronExpression.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import assert from 'assert';
import { DateTime } from 'luxon';

import { CronDate, DateMathOp, TimeUnit } from './CronDate';
@@ -117,7 +116,9 @@ export class CronExpression {

// The first character represents the weekday
const weekday = parseInt(expression.toString().charAt(0), 10) % 7;
assert(!Number.isNaN(weekday), `Invalid last weekday of the month expression: ${expression}`);
if (Number.isNaN(weekday)) {
throw new Error(`Invalid last weekday of the month expression: ${expression}`);
}

// Check if the current date matches the last specified weekday of the month
return currentDate.getDay() === weekday && currentDate.isLastWeekdayOfMonth();
@@ -239,9 +240,7 @@ export class CronExpression {
*/
includesDate(date: Date | CronDate): boolean {
const { second, minute, hour, dayOfMonth, month, dayOfWeek } = this.#fields;
const dtStr = date.toISOString();
assert(dtStr != null, 'Invalid date');
const dt = DateTime.fromISO(dtStr, { zone: this.#tz });
const dt = DateTime.fromISO(date.toISOString()!, { zone: this.#tz });
return (
dayOfMonth.values.includes(<DayOfMonthRange>dt.day) &&
dayOfWeek.values.includes(<DayOfWeekRange>dt.weekday) &&
93 changes: 50 additions & 43 deletions src/CronExpressionParser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import assert from 'assert';

import { CronFieldCollection } from './CronFieldCollection';
import { CronDate } from './CronDate';
import { CronExpression, CronExpressionOptions } from './CronExpression';
@@ -15,7 +13,7 @@ import {
DayOfWeekRange,
HourRange,
MonthRange,
ParseRageResponse,
ParseRangeResponse,
SixtyRange,
} from './fields';

@@ -99,10 +97,9 @@ export class CronExpressionParser {

expression = PredefinedExpressions[expression as keyof typeof PredefinedExpressions] || expression;
const rawFields = CronExpressionParser.#getRawFields(expression, strict);
assert(
rawFields.dayOfMonth === '*' || rawFields.dayOfWeek === '*' || !strict,
'Cannot use both dayOfMonth and dayOfWeek together in strict mode!',
);
if (!(rawFields.dayOfMonth === '*' || rawFields.dayOfWeek === '*' || !strict)) {
throw new Error('Cannot use both dayOfMonth and dayOfWeek together in strict mode!');
}

const second = CronExpressionParser.#parseField(
CronUnit.Second,
@@ -151,11 +148,17 @@ export class CronExpressionParser {
* @returns {RawCronFields} The raw fields.
*/
static #getRawFields(expression: string, strict: boolean): RawCronFields {
assert(!strict || expression.length, 'Invalid cron expression');
if (!(!strict || expression.length)) {
throw new Error('Invalid cron expression');
}
expression = expression || '0 * * * * *';
const atoms = expression.trim().split(/\s+/);
assert(!strict || atoms.length === 6, 'Invalid cron expression, expected 6 fields');
assert(atoms.length <= 6, 'Invalid cron expression, too many fields');
if (!(!strict || atoms.length === 6)) {
throw new Error('Invalid cron expression, expected 6 fields');
}
if (atoms.length > 6) {
throw new Error('Invalid cron expression, too many fields');
}
const defaults = ['*', '*', '*', '*', '*', '0'];
if (atoms.length < defaults.length) {
atoms.unshift(...defaults.slice(atoms.length));
@@ -178,13 +181,17 @@ export class CronExpressionParser {
value = value.replace(/[a-z]{3}/gi, (match) => {
match = match.toLowerCase();
const replacer = Months[match as keyof typeof Months] || DayOfWeek[match as keyof typeof DayOfWeek];
assert(replacer != null, `Validation error, cannot resolve alias "${match}"`);
if (!replacer) {
throw new Error(`Validation error, cannot resolve alias "${match}"`);
}
return replacer.toString();
});
}

// Check for valid characters
assert(constraints.validChars.test(value), `Invalid characters, got value: ${value}`);
if (!constraints.validChars.test(value)) {
throw new Error(`Invalid characters, got value: ${value}`);
}

// Replace '*' and '?'
value = value.replace(/[*?]/g, constraints.min + '-' + constraints.max);
@@ -200,35 +207,30 @@ export class CronExpressionParser {
*/
static #parseSequence(field: CronUnit, val: string, constraints: CronConstraints): (number | string)[] {
const stack: (number | string)[] = [];

function handleResult(result: number | string | (number | string)[], constraints: CronConstraints) {
if (Array.isArray(result)) {
result.forEach((value) => {
if (!CronExpressionParser.#isValidConstraintChar(constraints, value)) {
const v = parseInt(value.toString(), 10);
const isValid = v >= constraints.min && v <= constraints.max;
assert(
isValid,
`Constraint error, got value ${value} expected range ${constraints.min}-${constraints.max}`,
);
stack.push(value);
}
});
stack.push(...result);
} else {
if (CronExpressionParser.#isValidConstraintChar(constraints, result)) {
stack.push(result);
} else {
const v = parseInt(result.toString(), 10);
const isValid = v >= constraints.min && v <= constraints.max;
assert(isValid, `Constraint error, got value ${result} expected range ${constraints.min}-${constraints.max}`);
if (!isValid) {
throw new Error(
`Constraint error, got value ${result} expected range ${constraints.min}-${constraints.max}`,
);
}
stack.push(field === CronUnit.DayOfWeek ? v % 7 : result);
}
}
}

const atoms = val.split(',');
atoms.forEach((atom) => {
assert(atom.length > 0, 'Invalid list value format');
if (!(atom.length > 0)) {
throw new Error('Invalid list value format');
}
handleResult(CronExpressionParser.#parseRepeat(field, atom, constraints), constraints);
});
return stack;
@@ -242,9 +244,11 @@ export class CronExpressionParser {
* @private
* @returns {(number | string)[]} The parsed repeat.
*/
static #parseRepeat(field: CronUnit, val: string, constraints: CronConstraints): ParseRageResponse {
static #parseRepeat(field: CronUnit, val: string, constraints: CronConstraints): ParseRangeResponse {
const atoms = val.split('/');
assert(atoms.length <= 2, `Invalid repeat: ${val}`);
if (atoms.length > 2) {
throw new Error(`Invalid repeat: ${val}`);
}
if (atoms.length === 2) {
if (!isNaN(parseInt(atoms[0], 10))) {
atoms[0] = `${atoms[0]}-${constraints.max}`;
@@ -266,8 +270,12 @@ export class CronExpressionParser {
*/
static #validateRange(min: number, max: number, constraints: CronConstraints): void {
const isValid = !isNaN(min) && !isNaN(max) && min >= constraints.min && max <= constraints.max;
assert(isValid, `Constraint error, got range ${min}-${max} expected range ${constraints.min}-${constraints.max}`);
assert(min <= max, `Invalid range: ${min}-${max}, min(${min}) > max(${max})`);
if (!isValid) {
throw new Error(`Constraint error, got range ${min}-${max} expected range ${constraints.min}-${constraints.max}`);
}
if (min > max) {
throw new Error(`Invalid range: ${min}-${max}, min(${min}) > max(${max})`);
}
}

/**
@@ -278,10 +286,9 @@ export class CronExpressionParser {
* @throws {Error} Throws an error if the repeat interval is invalid.
*/
static #validateRepeatInterval(repeatInterval: number): void {
assert(
!isNaN(repeatInterval) && repeatInterval > 0,
`Constraint error, cannot repeat at every ${repeatInterval} time.`,
);
if (!(!isNaN(repeatInterval) && repeatInterval > 0)) {
throw new Error(`Constraint error, cannot repeat at every ${repeatInterval} time.`);
}
}

/**
@@ -320,7 +327,7 @@ export class CronExpressionParser {
val: string,
repeatInterval: number,
constraints: CronConstraints,
): ParseRageResponse {
): ParseRangeResponse {
const atoms: string[] = val.split('-');
if (atoms.length <= 1) {
return isNaN(+val) ? val : +val;
@@ -346,14 +353,14 @@ export class CronExpressionParser {
}
const nthValue = +atoms[atoms.length - 1];
const matches = val.match(/([,-/])/);
assert(
matches === null,
`Constraint error, invalid dayOfWeek \`#\` and \`${matches?.[0]}\` special characters are incompatible`,
);
assert(
atoms.length <= 2 && !isNaN(nthValue) && nthValue >= 1 && nthValue <= 5,
'Constraint error, invalid dayOfWeek occurrence number (#)',
);
if (matches !== null) {
throw new Error(
`Constraint error, invalid dayOfWeek \`#\` and \`${matches?.[0]}\` special characters are incompatible`,
);
}
if (!(atoms.length <= 2 && !isNaN(nthValue) && nthValue >= 1 && nthValue <= 5)) {
throw new Error('Constraint error, invalid dayOfWeek occurrence number (#)');
}
return { dayOfWeek: atoms[0], nthDayOfWeek: nthValue };
}

Loading