From f4a4149880a8e40ba329737e78d91068eb706b03 Mon Sep 17 00:00:00 2001 From: Ryan Zimmerman Date: Tue, 25 Oct 2022 15:44:24 -0400 Subject: [PATCH] Add fs-extra/esm ESM named import module, with just fs-extra methods Fixes #746 --- README.md | 30 ++++++++++++- lib/esm.mjs | 68 ++++++++++++++++++++++++++++++ package.json | 10 +++-- test.mjs | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 lib/esm.mjs create mode 100644 test.mjs diff --git a/README.md b/README.md index 6ed8b6ae..6bd23d3f 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ Installation Usage ----- +### CommonJS + `fs-extra` is a drop in replacement for native `fs`. All methods in `fs` are attached to `fs-extra`. All `fs` methods return promises if the callback isn't passed. You don't ever need to include the original `fs` module again: @@ -55,6 +57,31 @@ const fs = require('fs') const fse = require('fs-extra') ``` +### ESM + +There is also an `fs-extra/esm` import, that supports both default and named exports. However, note that `fs` methods are not included in `fs-extra/esm`; you still need to import `fs` and/or `fs/promises` seperately: + +```js +import { readFileSync } from 'fs' +import { readFile } from 'fs/promises' +import { outputFile, outputFileSync } from 'fs-extra/esm' +``` + +Default exports are supported: + +```js +import fs from 'fs' +import fse from 'fs-extra/esm' +// fse.readFileSync is not a function; must use fs.readFileSync +``` + +but you probably want to just use regular `fs-extra` instead of `fs-extra/esm` for default exports: + +```js +import fs from 'fs-extra' +// both fs and fs-extra methods are defined +``` + Sync vs Async vs Async/Await ------------- Most methods are async by default. All async methods will return a promise if the callback isn't passed. @@ -197,7 +224,8 @@ fs-extra contains hundreds of tests. - `npm run lint`: runs the linter ([standard](http://standardjs.com/)) - `npm run unit`: runs the unit tests -- `npm test`: runs both the linter and the tests +- `npm run unit-esm`: runs tests for `fs-extra/esm` exports +- `npm test`: runs the linter and all tests ### Windows diff --git a/lib/esm.mjs b/lib/esm.mjs new file mode 100644 index 00000000..27b7a1f7 --- /dev/null +++ b/lib/esm.mjs @@ -0,0 +1,68 @@ +import _copy from './copy/index.js' +import _empty from './empty/index.js' +import _ensure from './ensure/index.js' +import _json from './json/index.js' +import _mkdirs from './mkdirs/index.js' +import _move from './move/index.js' +import _outputFile from './output-file/index.js' +import _pathExists from './path-exists/index.js' +import _remove from './remove/index.js' + +// NOTE: Only exports fs-extra's functions; fs functions must be imported from "node:fs" or "node:fs/promises" + +export const copy = _copy.copy +export const copySync = _copy.copySync +export const emptyDirSync = _empty.emptyDirSync +export const emptydirSync = _empty.emptydirSync +export const emptyDir = _empty.emptyDir +export const emptydir = _empty.emptydir +export const createFile = _ensure.createFile +export const createFileSync = _ensure.createFileSync +export const ensureFile = _ensure.ensureFile +export const ensureFileSync = _ensure.ensureFileSync +export const createLink = _ensure.createLink +export const createLinkSync = _ensure.createLinkSync +export const ensureLink = _ensure.ensureLink +export const ensureLinkSync = _ensure.ensureLinkSync +export const createSymlink = _ensure.createSymlink +export const createSymlinkSync = _ensure.createSymlinkSync +export const ensureSymlink = _ensure.ensureSymlink +export const ensureSymlinkSync = _ensure.ensureSymlinkSync +export const readJson = _json.readJson +export const readJSON = _json.readJSON +export const readJsonSync = _json.readJsonSync +export const readJSONSync = _json.readJSONSync +export const writeJson = _json.writeJson +export const writeJSON = _json.writeJSON +export const writeJsonSync = _json.writeJsonSync +export const writeJSONSync = _json.writeJSONSync +export const outputJson = _json.outputJson +export const outputJSON = _json.outputJSON +export const outputJsonSync = _json.outputJsonSync +export const outputJSONSync = _json.outputJSONSync +export const mkdirs = _mkdirs.mkdirs +export const mkdirsSync = _mkdirs.mkdirsSync +export const mkdirp = _mkdirs.mkdirp +export const mkdirpSync = _mkdirs.mkdirpSync +export const ensureDir = _mkdirs.ensureDir +export const ensureDirSync = _mkdirs.ensureDirSync +export const move = _move.move +export const moveSync = _move.moveSync +export const outputFile = _outputFile.outputFile +export const outputFileSync = _outputFile.outputFileSync +export const pathExists = _pathExists.pathExists +export const pathExistsSync = _pathExists.pathExistsSync +export const remove = _remove.remove +export const removeSync = _remove.removeSync + +export default { + ..._copy, + ..._empty, + ..._ensure, + ..._json, + ..._mkdirs, + ..._move, + ..._outputFile, + ..._pathExists, + ..._remove +} diff --git a/package.json b/package.json index ee3c6b51..40e0f372 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,10 @@ "read-dir-files": "^0.1.1", "standard": "^16.0.3" }, - "exports": "./lib/index.js", + "exports": { + ".": "./lib/index.js", + "./esm": "./lib/esm.mjs" + }, "files": [ "lib/", "!lib/**/__tests__/" @@ -59,8 +62,9 @@ "scripts": { "lint": "standard", "test-find": "find ./lib/**/__tests__ -name *.test.js | xargs mocha", - "test": "npm run lint && npm run unit", - "unit": "nyc node test.js" + "test": "npm run lint && npm run unit && npm run unit-esm", + "unit": "nyc node test.js", + "unit-esm": "node test.mjs" }, "sideEffects": false } diff --git a/test.mjs b/test.mjs new file mode 100644 index 00000000..333f9fde --- /dev/null +++ b/test.mjs @@ -0,0 +1,116 @@ +import assert from 'assert' +import fsLegacy from './lib/index.js' +// NOTE: eslint comments needed because we're importing the same file multiple times +import fsDefault from './lib/esm.mjs' // eslint-disable-line +import * as fsStar from './lib/esm.mjs' +import { + copy, + copySync, + emptyDirSync, + emptydirSync, + emptyDir, + emptydir, + createFile, + createFileSync, + ensureFile, + ensureFileSync, + createLink, + createLinkSync, + ensureLink, + ensureLinkSync, + createSymlink, + createSymlinkSync, + ensureSymlink, + ensureSymlinkSync, + readJson, + readJsonSync, + writeJson, + writeJsonSync, + outputJson, + outputJsonSync, + outputJSON, + outputJSONSync, + writeJSON, + writeJSONSync, + readJSON, + readJSONSync, + mkdirs, + mkdirsSync, + mkdirp, + mkdirpSync, + ensureDir, + ensureDirSync, + move, + moveSync, + outputFile, + outputFileSync, + pathExists, + pathExistsSync, + remove, + removeSync +} from './lib/esm.mjs' // eslint-disable-line +const fsNamed = [ + copy, + copySync, + emptyDirSync, + emptydirSync, + emptyDir, + emptydir, + createFile, + createFileSync, + ensureFile, + ensureFileSync, + createLink, + createLinkSync, + ensureLink, + ensureLinkSync, + createSymlink, + createSymlinkSync, + ensureSymlink, + ensureSymlinkSync, + readJson, + readJsonSync, + writeJson, + writeJsonSync, + outputJson, + outputJsonSync, + outputJSON, + outputJSONSync, + writeJSON, + writeJSONSync, + readJSON, + readJSONSync, + mkdirs, + mkdirsSync, + mkdirp, + mkdirpSync, + ensureDir, + ensureDirSync, + move, + moveSync, + outputFile, + outputFileSync, + pathExists, + pathExistsSync, + remove, + removeSync +] + +const keys = Object.keys(fsDefault) + +assert.deepStrictEqual(Object.values(fsDefault), fsNamed, 'named and default exports should match') +assert.deepStrictEqual( + Object.entries(fsStar) + .filter(([name]) => name !== 'default') // remove "default" property here + .sort(([nameA], [nameB]) => keys.indexOf(nameA) - keys.indexOf(nameB)) // sort for exact match + .map(([name, fn]) => fn), + Object.values(fsDefault), + 'star and default exports should match' +) + +// default exports a subset of the legacy implementation, but functions are the same +Object.entries(fsDefault).forEach(([name, fn]) => { + assert.strictEqual(fn, fsLegacy[name], `${name}() should match legacy implementation`) +}) + +console.warn('ESM tests pass!')