Created
November 12, 2019 14:34
-
-
Save jquense/0c6723e1c6e86bd46e1ee18ff27ac4c2 to your computer and use it in GitHub Desktop.
Configure a yarn workspaces to use project references and paths
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable no-param-reassign */ | |
const { promises: fs, readFileSync } = require('fs'); | |
const path = require('path'); | |
const prettier = require('prettier'); | |
const { parse, stringify } = require('comment-json'); | |
const getWorkspaces = require('get-workspaces').default; | |
const findWorkspacesRoot = require('find-workspaces-root').default; | |
const safeRequire = m => { | |
try { | |
return parse(readFileSync(m, 'utf-8')); | |
} catch { | |
return null; | |
} | |
}; | |
const addReference = (tsconfig, ref) => { | |
const normalPath = path.normalize(ref); | |
const refs = tsconfig.references || []; | |
const existing = refs.findIndex(r => path.normalize(r.path) !== normalPath); | |
refs.splice(existing, 1, { path: normalPath }); | |
tsconfig.references = refs; | |
}; | |
async function run() { | |
const wks = await getWorkspaces({ tools: 'yarn' }); | |
if (!wks) return; | |
const wsRoot = await findWorkspacesRoot(process.cwd()); | |
const workspaces = wks | |
.map(ws => ({ ...ws, tsconfig: safeRequire(`${ws.dir}/tsconfig.json`) })) | |
.filter(ws => ws.tsconfig); | |
if (!workspaces.length) return; | |
const rootTsconfig = safeRequire(`${wsRoot}/tsconfig.json`) || { | |
files: [], | |
references: [], | |
}; | |
const workspaceByName = new Map(workspaces.map(ws => [ws.name, ws])); | |
const getLocalDeps = ({ | |
dependencies = {}, | |
devDependencies = {}, | |
peerDependencies = {}, | |
}) => { | |
return new Set( | |
[ | |
...Object.keys(dependencies), | |
...Object.keys(devDependencies), | |
...Object.keys(peerDependencies), | |
] | |
.filter(k => workspaceByName.has(k)) | |
.map(k => workspaceByName.get(k)), | |
); | |
}; | |
await Promise.all( | |
workspaces.map(({ name, dir, config, tsconfig }) => { | |
addReference(rootTsconfig, path.relative(wsRoot, dir)); | |
const deps = getLocalDeps(config); | |
if (!deps.size) return null; | |
for (const dep of deps) { | |
const publishDir = | |
dep.config.publishConfig && dep.config.publishConfig.directory; | |
addReference(tsconfig, path.relative(dir, dep.dir)); | |
// When the dependency publishes a differenct directory than it's root | |
// we also need to configure a `paths` for any cherry-picked imports | |
if (publishDir) { | |
tsconfig.compilerOptions = tsconfig.compilerOptions || {}; | |
const basePath = path.resolve( | |
dir, | |
tsconfig.compilerOptions.baseUrl || '.', | |
); | |
const relPath = path.relative(basePath, dep.dir); | |
tsconfig.compilerOptions.paths = | |
tsconfig.compilerOptions.paths || {}; | |
tsconfig.compilerOptions.paths[`${dep.name}/*`] = [ | |
`${relPath}/${publishDir}/*`, | |
]; | |
} | |
} | |
console.log(`${name}: updating tsconfig.json`); | |
const filepath = `${dir}/tsconfig.json`; | |
return fs.writeFile( | |
filepath, | |
prettier.format(stringify(tsconfig, null, 2), { filepath }), | |
); | |
}), | |
); | |
const filepath = `${wsRoot}/tsconfig.json`; | |
await fs.writeFile( | |
filepath, | |
prettier.format(stringify(rootTsconfig, null, 2), { filepath }), | |
); | |
} | |
run(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment