tshy
Version: 
TypeScript HYbridizer - Hybrid (CommonJS/ESM) TypeScript node package builder
117 lines • 4.29 kB
JavaScript
// this is the thing that supports top-level package.json imports
// via symlinks, not the tshy.imports which are just config.
import { writeFileSync } from 'fs';
import { symlink } from 'fs/promises';
import { mkdirp } from 'mkdirp';
import { dirname, relative, resolve, sep } from 'path';
import { getAllConditionalValues, getUniqueConditionSets, resolveAllLocalImports, } from 'resolve-import';
import { rimraf } from 'rimraf';
import { fileURLToPath } from 'url';
import * as console from './console.js';
const dirsMade = new Set();
// write out the steps to the save file script
export const save = (f) => {
    const links = [...saveSet.entries()];
    if (!links.length)
        return false;
    const dirs = new Set(links.map(([dest]) => dirname(dest)));
    console.debug('save import linker', f);
    writeFileSync(f, `import { mkdirSync } from 'node:fs'
import { symlink } from 'node:fs/promises'
const dirs = ${JSON.stringify([...dirs])}
const links = [
${links.map(l => `  ${JSON.stringify(l)},\n`).join('')}]
const e = (er) => { if (er.code !== 'EEXIST') throw er }
for (const d of dirs) mkdirSync(d, { recursive: true })
Promise.all(links.map(([dest, src]) => symlink(src, dest).catch(e)))
`);
    return true;
};
let targets = undefined;
// Get the targets that will have to be linked, because they're not
// a target in ./src
const getTargets = async (imports) => {
    const conds = getAllConditionalValues(imports).filter(c => !c.startsWith('./src/'));
    if (!conds.some(c => c.includes('*'))) {
        // fast path
        return (targets = conds.filter(c => c.startsWith('./')));
    }
    const sets = getUniqueConditionSets(imports);
    const t = new Set();
    const pj = resolve('package.json');
    for (const conditions of sets) {
        const imps = await resolveAllLocalImports(pj, { conditions });
        for (const url of Object.values(imps)) {
            // node builtin
            if (typeof url === 'string')
                continue;
            const p = fileURLToPath(url);
            const rel = relative(process.cwd(), p);
            // if it's empty, a dep in node_modules, or a built module, skip
            if (!rel ||
                rel.startsWith('..' + sep) ||
                rel.startsWith('src' + sep) ||
                rel.startsWith('node_modules' + sep))
                continue;
            t.add('./' + rel.replace(/\\/g, '/'));
        }
    }
    return (targets = [...t]);
};
const saveSet = new Map();
// create symlinks for the package imports in the target dir
export const link = async (pkg, dir, save = false) => {
    const { imports } = pkg;
    if (!imports)
        return;
    if (!targets)
        targets = await getTargets(imports);
    if (!targets.length)
        return;
    console.debug(`link import targets in ${dir}`, targets);
    const rel = relative(resolve(dir), process.cwd());
    const lps = [];
    for (const t of targets) {
        const l = t.replace(/^\.\//, '');
        const df = dirname(l);
        const dfrel = df === '.' ? '' : (df
            .split('/')
            .map(() => '../')
            .join(''));
        const dest = dir + '/' + l;
        const src = rel + '/' + dfrel + l;
        if (save)
            saveSet.set(dest, src);
        lps.push(mkdirp(dirname(dest))
            .then(d => {
            // if we aren't saving, then this is a transient link
            // save the dirs created so that we can clean them up
            if (!save && d)
                dirsMade.add(d);
            return rimraf(dest);
        })
            .then(() => symlink(src, dest)));
    }
    await Promise.all(lps);
};
// remove symlinks created for package imports in the target dir
export const unlink = async (pkg, dir) => {
    const { imports } = pkg;
    if (!imports)
        return;
    // will always have targets by this point
    /* c8 ignore start */
    if (!targets)
        targets = await getTargets(imports);
    /* c8 ignore stop */
    console.debug(`unlink import targets in ${dir}`, targets);
    const lps = [];
    for (const t of targets) {
        const dest = resolve(dir, t);
        lps.push(rimraf(dest));
    }
    for (const d of dirsMade)
        lps.push(rimraf(d));
    await Promise.all(lps);
};
//# sourceMappingURL=unbuilt-imports.js.map