From 80e97bfb1afa03f204fe5705f073f5e8275ad7ee Mon Sep 17 00:00:00 2001 From: "sasjs-dev[bot]" Date: Thu, 15 Oct 2026 21:54:08 +0000 Subject: [PATCH] feat(barrel)!: stop re-exporting ./fs, the one module that imports crypto Importing anything from the barrel put the crypto chain into a browser bundle. './fs' is the only module here that imports `crypto`, and a bundler resolves that to crypto-browserify, which drags in browserify-sign, parse-asn1, asn1.js and vm-browserify: roughly 555 KB of AES/RSA/ECDSA polyfills plus a vm shim's dynamic code execution. That combination - dynamic code execution and a full crypto suite in one bundle - is the dual-use shape npm's publish scanner is trained on, and it is dead weight for a consumer that wanted, say, a type guard. sasjs/utils#275. The barrel no longer re-exports './fs'. The helpers are unchanged and still ship at '@sasjs/utils/fs': getHash, compareHashes, extractHashArray, generateCompileProgram, generateProgramToGetRemoteHash, generateProgramToSyncHashDiff, findResourcesNotPresentLocally Migration: `import { getHash } from '@sasjs/utils'` becomes `import { getHash } from '@sasjs/utils/fs'`. Nothing in this estate imports these names from the barrel - the CLI, server, vscode-extension and lint all import the barrel but none of them use these helpers, and vscode-extension's sync-directories command already imports them from the subpath. BREAKING CHANGE: the fs helpers are no longer exported from the package root. Import them from '@sasjs/utils/fs'. Measured A webpack browser build (target web, with the Node polyfill plugin - the shape the issue describes) importing one runtime value from the barrel: before 1971 kB | 473 modules | 103 packages | 21 crypto-suite packages after 822 kB | 292 modules | 65 packages | 0 crypto-suite packages The remaining weight is the other node-only modules the barrel carries by design - prompts, fs-extra, @fast-csv/format, chalk, consola - so the barrel is still a Node entry point and not a browser one. That contract is stated in the README now, along with the browser-safe subpaths. Making the barrel itself browser-safe would mean splitting the package's exports, which this change does not attempt. Verified - npm test: 41 suites / 450 tests pass, including a new src/index.spec.ts that asserts the barrel does not carry the fs helpers and that they stay reachable at the subpath. Re-adding the export fails it, naming getHash. - npm run build and npm run lint pass. - The probe above was run against the built package, and the compiled barrel carries no require of './fs'. --- README.md | 23 +++++++++++++++++++++++ src/index.spec.ts | 34 ++++++++++++++++++++++++++++++++++ src/index.ts | 7 ++++++- 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/index.spec.ts diff --git a/README.md b/README.md index a696026..e45ad55 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,29 @@ A library of utilities and types for SASjs written in TypeScript. +## Entry points + +This is a Node library, and the barrel - `import { x } from '@sasjs/utils'` - is its Node +entry point. Importing from it pulls in the modules that only Node can run: `fs-extra` and +`@fast-csv/format` through `./file`, `prompts` through `./input`, `chalk` and `consola` +through `./logger` and `./formatter`. + +Code that is bundled for a browser imports the subpath it needs instead: + +``` +import { ServerType } from '@sasjs/utils/types' +import { prefixMessage } from '@sasjs/utils/error' +import { isSpecialMissing } from '@sasjs/utils/input/validators' +import { bytesToSize } from '@sasjs/utils/utils' +``` + +The hash and sync helpers - `getHash`, `compareHashes`, `extractHashArray`, +`generateCompileProgram`, `generateProgramToGetRemoteHash`, `generateProgramToSyncHashDiff` +and `findResourcesNotPresentLocally` - live in `@sasjs/utils/fs`. They hash files and +folders on disk, so they are Node-only, and the barrel does not re-export them: a browser +bundle that imports the barrel for anything else no longer reaches `crypto`, which used to +arrive with the whole AES/RSA/ECDSA polyfill suite behind it. + ## Contributors ✨ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): diff --git a/src/index.spec.ts b/src/index.spec.ts new file mode 100644 index 0000000..c63ec94 --- /dev/null +++ b/src/index.spec.ts @@ -0,0 +1,34 @@ +import * as barrel from './index' + +/** + * The helpers './fs' provides. They hash files and folders on disk, so they are + * Node-only, and the barrel must not carry them: re-exporting './fs' put the + * crypto chain - crypto-browserify, browserify-sign, parse-asn1, asn1.js, + * vm-browserify - into every browser bundle that imported anything from the + * barrel. See sasjs/utils#275. + */ +const NODE_ONLY_FS_HELPERS = [ + 'getHash', + 'compareHashes', + 'extractHashArray', + 'generateCompileProgram', + 'generateProgramToGetRemoteHash', + 'generateProgramToSyncHashDiff', + 'findResourcesNotPresentLocally' +] + +describe('the barrel', () => { + it('does not re-export the node-only fs helpers', () => { + for (const name of NODE_ONLY_FS_HELPERS) { + expect(barrel).not.toHaveProperty(name) + } + }) + + it('leaves them reachable at the fs subpath', () => { + const fs = require('./fs') + + for (const name of NODE_ONLY_FS_HELPERS) { + expect(typeof fs[name]).toBe('function') + } + }) +}) diff --git a/src/index.ts b/src/index.ts index c3c20d1..3e5a55e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,4 +8,9 @@ export * from './time' export * from './types' export * from './utils' export * from './compileTree' -export * from './fs' +// './fs' is deliberately absent: it is the only module here that imports +// `crypto`, and re-exporting it put that - plus browserify-sign, parse-asn1, +// asn1.js and vm-browserify, roughly 555 KB of AES/RSA/ECDSA polyfills and a +// vm shim's dynamic code execution - into every browser bundle that imported +// anything from this barrel. The helpers live at '@sasjs/utils/fs' instead. +// See sasjs/utils#275.