tshy
Version:
TypeScript HYbridizer - Hybrid (CommonJS/ESM) TypeScript node package builder
166 lines • 6.7 kB
JavaScript
import { relative, resolve } from 'node:path/posix';
import config from './config.js';
import dialects from './dialects.js';
import fail from './fail.js';
import pkg from './package.js';
import polyfills from './polyfills.js';
import { resolveExport } from './resolve-export.js';
const { esmDialects = [], commonjsDialects = [] } = config;
const liveDev = config.liveDev &&
process.env.npm_command !== 'publish' &&
process.env.npm_command !== 'pack';
const getTargetForDialectCondition = (s, dialect, condition, type, polyfills = new Map()) => {
if (s === undefined)
return undefined;
if (typeof s === 'string') {
// the excluded filename pattern
const xts = type === 'commonjs' ? '.mts' : '.cts';
if (s.endsWith(xts))
return undefined;
const pf = dialect === 'commonjs' ? 'cjs' : dialect;
const rel = relative(resolve('./src'), resolve(polyfills.get(pf)?.map.get(s) ?? s));
const target = liveDev ? rel : rel.replace(/\.([mc]?)tsx?$/, '.$1js');
const dt = dialect === 'commonjs' || dialect === 'esm' ?
dialect
: `${type}/${dialect}`;
return (!s || !s.startsWith('./src/') ? s
: dialects.includes(type) ? `./dist/${dt}/${target}`
: undefined);
}
return resolveExport(s, [condition]);
};
export const getImpTarget = (s, polyfills = new Map()) => getTargetForDialectCondition(s, 'esm', 'import', 'esm', polyfills);
export const getReqTarget = (s, polyfills = new Map()) => getTargetForDialectCondition(s, 'commonjs', 'require', 'commonjs', polyfills);
const getExports = (c, pkgType) => {
// by this time it always exports, will get the default if missing
/* c8 ignore start */
if (!c.exports) {
fail('no exports on tshy config (is there code in ./src?)');
return process.exit(1);
}
/* c8 ignore stop */
const e = {};
for (const [sub, s] of Object.entries(c.exports)) {
// external export, not built by us
if (s !== null && (typeof s !== 'string' || !s.startsWith('./src/'))) {
// already been validated, just accept as-is
e[sub] = s;
continue;
}
/* c8 ignore next - already guarded */
if (s === null)
continue;
const impTarget = getImpTarget(s, polyfills);
const reqTarget = getReqTarget(s, polyfills);
// should be impossible
/* c8 ignore start */
if (!impTarget && !reqTarget)
continue;
/* c8 ignore stop */
const exp = (e[sub] = {});
if (impTarget) {
for (const d of esmDialects) {
const source = s && (polyfills.get('esm-' + d)?.rmap.get(s) ?? s);
const target = getTargetForDialectCondition(s, d, d, 'esm', polyfills);
if (target) {
const imp = (exp.import ??= {});
const src = pkgType === 'module' || source.endsWith('.mts') ?
getSourceDialects(source, c)
: undefined;
const types = liveDev ? undefined : ({ types: target.replace(/\.js$/, '.d.ts') });
imp[d] = { ...src, ...types, default: target };
}
}
}
if (reqTarget) {
for (const d of commonjsDialects) {
const source = s && (polyfills.get('commonjs-' + d)?.rmap.get(s) ?? s);
const target = getTargetForDialectCondition(s, d, d, 'commonjs', polyfills);
if (target) {
const req = (exp.require ??= {});
const src = pkgType === 'commonjs' || source.endsWith('.cts') ?
getSourceDialects(source, c)
: undefined;
const types = liveDev ? undefined : ({ types: target.replace(/\.js$/, '.d.ts') });
req[d] = { ...src, ...types, default: target };
}
}
}
// put the default import/require after all the other special ones.
if (impTarget) {
const source = s && (polyfills.get('esm')?.rmap.get(s) ?? s);
const src = pkgType === 'module' || source.endsWith('.mts') ?
getSourceDialects(source, c)
: undefined;
const types = liveDev ? undefined : ({ types: impTarget.replace(/\.(m?)js$/, '.d.$1ts') });
exp.import = {
...exp.import,
...src,
...types,
default: impTarget,
};
}
if (reqTarget) {
const source = s && (polyfills.get('cjs')?.rmap.get(s) ?? s);
const src = pkgType === 'commonjs' || source.endsWith('.cts') ?
getSourceDialects(source, c)
: undefined;
const types = liveDev ? undefined : ({ types: reqTarget.replace(/\.(c?)js$/, '.d.$1ts') });
exp.require = {
...exp.require,
...src,
...types,
default: reqTarget,
};
}
}
return e;
};
const getSourceDialects = (source, c) => {
const { sourceDialects } = c;
if (!sourceDialects)
return undefined;
return Object.fromEntries(sourceDialects.map(s => [s, source]));
};
export const setMain = (c, pkg) => {
pkg.type = pkg.type === 'commonjs' ? 'commonjs' : 'module';
const mod = resolveExport(pkg.exports['.'], ['require', 'default']);
const main = c?.main ?? !!mod;
if (main) {
if (!mod) {
fail(`could not resolve exports['.'] for tshy.main 'require'`);
return process.exit(1);
}
const types = resolveExport(pkg.exports['.'], ['require', 'types']);
pkg.main = mod;
if (types && types !== mod)
pkg.types = types;
else
delete pkg.types;
}
else {
if (c && c.main !== false)
delete c.main;
delete pkg.main;
delete pkg.types;
}
// Set the package module to exports["."]
const importMod = resolveExport(pkg.exports['.'], ['import', 'default']);
const module = c?.module ?? !!importMod;
if (module) {
if (!importMod) {
fail(`could not resolve exports['.'] for tshy.module 'import'`);
return process.exit(1);
}
pkg.module = importMod;
}
else {
if (c && c.module !== false)
delete c.module;
delete pkg.module;
}
};
pkg.exports = getExports(config, pkg.type);
setMain(config, pkg);
export default pkg.exports;
//# sourceMappingURL=exports.js.map