@plugjs/plug
Version:
PlugJS Build System ===================
110 lines • 5.57 kB
JavaScript
import { EOL } from 'node:os';
import { sep } from 'node:path';
import { assert } from "../asserts.js";
import { Files } from "../files.js";
import { readFile, writeFile } from "../fs.js";
import { $p } from "../logging.js";
import { assertRelativeChildPath, getAbsoluteParent } from "../paths.js";
import { install } from "../pipe.js";
install('exports', class Exports {
_packageJson;
_outputPackageJson;
_cjsExtension;
_esmExtension;
constructor(...args) {
const options = args[0] || {};
const { packageJson = 'package.json', outputPackageJson = packageJson, cjsExtension, esmExtension, } = options;
this._packageJson = packageJson;
this._outputPackageJson = outputPackageJson;
this._cjsExtension = cjsExtension;
this._esmExtension = esmExtension;
}
async pipe(files, context) {
// read up our package.json, we need it to figure out the default `type`
const incomingFile = context.resolve(this._packageJson);
const incomingData = await readFile(incomingFile, 'utf8');
const packageData = JSON.parse(incomingData);
// exports must be relative to the _output_ package.json
const outgoingFile = context.resolve(this._outputPackageJson);
const outgoingDirectory = getAbsoluteParent(outgoingFile);
// type here determines the extension of commonjs or ecmascript modules
const type = packageData.type === 'module' ? 'module' :
packageData.type === 'commonjs' ? 'commonjs' :
packageData.type == null ? 'commonjs' :
undefined;
assert(type, `Unknown module type "${packageData.type}" in ${$p(incomingFile)}`);
context.log.debug(`Package file ${$p(incomingFile)} declares module type "${type}"`);
const cjsExtension = this._cjsExtension || (type === 'commonjs' ? '.js' : '.cjs');
const esmExtension = this._esmExtension || (type === 'module' ? '.js' : '.mjs');
// reject when commonjs and ecmascript modules have the same extension
assert(cjsExtension !== esmExtension, `CommonJS and EcmaScript modules both resolve to same extension "${cjsExtension}"`);
const exports = {};
function addExport(name, type, file) {
if (!exports[name])
exports[name] = {};
// if (! exports[name]![type]) exports[name]![type] = {}
exports[name][type] = file;
}
// all extensions to match in the incoming files
const exts = ['.d.ts', '.d.cts', '.d.mts', cjsExtension, esmExtension];
// look up all the files we were piped in
for (const [name, absolute] of files.pathMappings()) {
const relative = assertRelativeChildPath(outgoingDirectory, absolute);
for (const ext of exts) {
if (!relative.endsWith(ext))
continue;
const base = `.${sep}${name.slice(0, -ext.length)}`;
const exp = base.endsWith(`${sep}index`) ? base.slice(0, -6) : base;
switch (ext) {
case cjsExtension:
addExport(exp, type === 'commonjs' ? 'default' : 'require', `.${sep}${relative}`);
break;
case esmExtension:
addExport(exp, type === 'module' ? 'default' : 'import', `.${sep}${relative}`);
break;
case '.d.cts':
addExport(exp, 'types', `.${sep}${relative}`);
break;
case '.d.mts':
addExport(exp, 'types', `.${sep}${relative}`);
break;
case '.d.ts':
addExport(exp, 'types', `.${sep}${relative}`);
break;
}
}
}
// if we have a "." export, inject the "main", "module" and "types" fields
if ('.' in exports) {
const rootExport = exports['.'];
packageData['module'] = undefined; // wipe existing...
packageData['main'] =
type === 'commonjs' ? rootExport?.require || rootExport?.default :
type === 'module' ? rootExport?.import || rootExport?.default :
undefined;
packageData['types'] = rootExport?.types;
}
// correctly order the exports record (e.g. types comes before default)
packageData['exports'] = Object.keys(exports).sort().reduce((obj, name) => {
const current = exports[name];
if (!current)
return obj;
// json serialization will scrub all undefined... here we export the types
// only if the "default" export is available, or we scrub the whole thing!
obj[name] = {
types: current.types,
require: current.require,
import: current.import,
default: current.default,
};
return obj;
}, {});
// convert back our package data into a json and write it
const outgoingData = JSON.stringify(packageData, null, 2);
context.log.info(`Writing new ${$p(outgoingFile)}`, outgoingData);
await writeFile(outgoingFile, outgoingData + EOL, 'utf8');
// return a `Files` instance with our `package.json` in there
return Files.builder(getAbsoluteParent(outgoingFile)).add(outgoingFile).build();
}
});
//# sourceMappingURL=exports.js.map