Summary
src/custom/UniversalFilter.tsx:3 imports date-fns at module scope, but date-fns is declared an optional peer dependency. UniversalFilter is reachable from the package barrel, so import { anything } from '@sistent/sistent' executes a top-level require('date-fns') and throws for any consumer that has not installed it.
This is the same root cause #1732 fixed for @mui/x-date-pickers. That PR resolved one instance and revealed this one behind it.
Reproduction (against published 0.21.39)
mkdir /tmp/repro && cd /tmp/repro && npm init -y
npm i @sistent/sistent@0.21.39 react react-dom @mui/material @mui/system @emotion/react @emotion/styled
node -e "require('@sistent/sistent')"
Error: Cannot find module 'date-fns'
Require stack:
- /tmp/repro/node_modules/@sistent/sistent/dist/index.js
Installing date-fns makes it resolve cleanly, confirming it is the only remaining instance.
Before #1732 the same reproduction failed on Cannot find module '@mui/x-date-pickers/AdapterDateFns', so this is not a regression - it is the next optional peer in line.
Impact
layer5labs/meshery-extensions (ui/package.json) depends on @sistent/sistent and has neither date-fns nor @mui/x-date-pickers. Any test or module there that imports anything from the barrel still fails on a clean install, with a message that names sistent rather than the missing peer - the diagnostic problem #1732 set out to remove.
meshery/meshery (ui/package.json) has both (date-fns@^4.4.0, @mui/x-date-pickers@^9.6.0) and is unaffected.
Root cause
The declaration and the usage disagree. date-fns is marked optional:
but UniversalFilter uses it unconditionally. An optional dependency has to be either used conditionally or declared honestly - right now it is neither.
Scope
Exactly one instance remains. @mui/x-date-pickers is fully resolved (only type-only imports, erased at runtime, plus dynamic import()):
| File |
Import |
Runtime require? |
src/custom/UniversalFilter.tsx:3 |
import { subDays, subMonths, subYears } from 'date-fns' |
yes - the bug |
src/base/DateTimePicker/DateTimePicker.tsx:1 |
import type ... |
no |
src/base/DateTimePicker/index.tsx:1 |
import type ... |
no |
src/base/DateTimePicker/DateTimePicker.tsx:21-23 |
dynamic import() |
no |
Options
A. Declare date-fns a real dependency. One line in package.json; makes the declaration match the unconditional usage. Adds a dependency for every consumer.
B. Drop date-fns here in favour of moment, which is already a hard dependency of sistent (^2.30.1), so it is always installed. The usage is three calls inside lazily-invoked getRange closures:
{ label: 'Last 7 days', getRange: () => ({ startDate: subDays(new Date(), 7), endDate: new Date() }) },
{ label: 'Last 3 months', getRange: () => ({ startDate: subMonths(new Date(), 3), endDate: new Date() }) },
{ label: 'Last 1 year', getRange: () => ({ startDate: subYears(new Date(), 1), endDate: new Date() }) },
moment().subtract(n, 'days'|'months'|'years').toDate() is equivalent for these, including the end-of-month clamping subMonths does. Zero new dependencies, and it removes an optional peer from the barrel's runtime graph entirely.
B is preferable on dependency hygiene. Flagging that UniversalFilter.tsx may have work in flight, so B is best folded into whoever is already touching it rather than landed in parallel.
Suggested guard
Whichever option is taken, the failure mode is invisible to this repo's CI because the repo always has its own devDependencies installed. A check that imports the built barrel with only the required peers present would catch the next instance:
node -e "require('./dist/index.js')" # in a dir without the optional peers
Summary
src/custom/UniversalFilter.tsx:3importsdate-fnsat module scope, butdate-fnsis declared an optional peer dependency.UniversalFilteris reachable from the package barrel, soimport { anything } from '@sistent/sistent'executes a top-levelrequire('date-fns')and throws for any consumer that has not installed it.This is the same root cause #1732 fixed for
@mui/x-date-pickers. That PR resolved one instance and revealed this one behind it.Reproduction (against published 0.21.39)
Installing
date-fnsmakes it resolve cleanly, confirming it is the only remaining instance.Before #1732 the same reproduction failed on
Cannot find module '@mui/x-date-pickers/AdapterDateFns', so this is not a regression - it is the next optional peer in line.Impact
layer5labs/meshery-extensions(ui/package.json) depends on@sistent/sistentand has neitherdate-fnsnor@mui/x-date-pickers. Any test or module there that imports anything from the barrel still fails on a clean install, with a message that names sistent rather than the missing peer - the diagnostic problem #1732 set out to remove.meshery/meshery(ui/package.json) has both (date-fns@^4.4.0,@mui/x-date-pickers@^9.6.0) and is unaffected.Root cause
The declaration and the usage disagree.
date-fnsis marked optional:but
UniversalFilteruses it unconditionally. An optional dependency has to be either used conditionally or declared honestly - right now it is neither.Scope
Exactly one instance remains.
@mui/x-date-pickersis fully resolved (only type-only imports, erased at runtime, plus dynamicimport()):src/custom/UniversalFilter.tsx:3import { subDays, subMonths, subYears } from 'date-fns'src/base/DateTimePicker/DateTimePicker.tsx:1import type ...src/base/DateTimePicker/index.tsx:1import type ...src/base/DateTimePicker/DateTimePicker.tsx:21-23import()Options
A. Declare
date-fnsa real dependency. One line inpackage.json; makes the declaration match the unconditional usage. Adds a dependency for every consumer.B. Drop
date-fnshere in favour ofmoment, which is already a harddependencyof sistent (^2.30.1), so it is always installed. The usage is three calls inside lazily-invokedgetRangeclosures:moment().subtract(n, 'days'|'months'|'years').toDate()is equivalent for these, including the end-of-month clampingsubMonthsdoes. Zero new dependencies, and it removes an optional peer from the barrel's runtime graph entirely.B is preferable on dependency hygiene. Flagging that
UniversalFilter.tsxmay have work in flight, so B is best folded into whoever is already touching it rather than landed in parallel.Suggested guard
Whichever option is taken, the failure mode is invisible to this repo's CI because the repo always has its own devDependencies installed. A check that imports the built barrel with only the required peers present would catch the next instance: