cli: improve autocomplete of extra entry points in published packages

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-11-07 13:22:54 +01:00
parent 6ecc9e7211
commit d849865b6f
2 changed files with 22 additions and 45 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': minor
---
The package packing now populates `typesVersions` for additional entry points rather than using additional `package.json` files for type resolution. This improves auto completion of separate entry points when consuming published packages.
+17 -45
View File
@@ -49,11 +49,7 @@ export async function productionPack(options: ProductionPackOptions) {
}
// This mutates pkg to fill in index exports, so call it before applying publishConfig
const writeCompatibilityEntryPoints = await prepareExportsEntryPoints(
pkg,
packageDir,
options.featureDetectionProject,
);
await rewriteEntryPoints(pkg, packageDir, options.featureDetectionProject);
// TODO(Rugvip): Once exports are rolled out more broadly we should deprecate and remove this behavior
const publishConfig = pkg.publishConfig ?? {};
@@ -63,9 +59,6 @@ export async function productionPack(options: ProductionPackOptions) {
}
}
// For published packages we rely on compatibility entry points rather than this
delete pkg.typesVersions;
// We remove the dependencies from package.json of packages that are marked
// as bundled, so that yarn doesn't try to install them.
if (pkg.bundled) {
@@ -97,10 +90,6 @@ export async function productionPack(options: ProductionPackOptions) {
} else {
await fs.writeJson(pkgPath, pkg, { encoding: 'utf8', spaces: 2 });
}
if (writeCompatibilityEntryPoints) {
await writeCompatibilityEntryPoints(targetDir ?? packageDir);
}
}
// Reverts the changes made by productionPack when called without a targetDir.
@@ -138,7 +127,7 @@ const EXPORT_MAP = {
* well as returning a function that creates backwards compatibility
* entry points for importers that don't support exports.
*/
async function prepareExportsEntryPoints(
async function rewriteEntryPoints(
pkg: BackstagePackageJson,
packageDir: string,
featureDetectionProject?: Project,
@@ -150,12 +139,13 @@ async function prepareExportsEntryPoints(
const distFiles = await fs.readdir(distPath);
const outputExports = {} as Record<string, string | Record<string, string>>;
const compatibilityWriters = new Array<
(targetDir: string) => Promise<void>
>();
const entryPoints = readEntryPoints(pkg);
// Clear to ensure a clean slate before adding entries back in further down
if (pkg.typesVersions) {
pkg.typesVersions = { '*': {} };
}
for (const entryPoint of entryPoints) {
if (!SCRIPT_EXTS.includes(entryPoint.ext)) {
outputExports[entryPoint.mount] = entryPoint.path;
@@ -171,6 +161,16 @@ async function prepareExportsEntryPoints(
}
}
// Our current tooling relies on the typesVersions field rather than export.*.types
if (exp.types) {
if (!pkg.typesVersions) {
pkg.typesVersions = { '*': {} };
}
pkg.typesVersions['*'][entryPoint.name] = [
`dist/${entryPoint.name}.d.ts`,
];
}
exp.default = exp.require ?? exp.import;
// Find the default export type for the entry point, if feature detection is active
@@ -192,7 +192,6 @@ async function prepareExportsEntryPoints(
}
}
// This creates a directory with a lone package.json for backwards compatibility
if (entryPoint.mount === '.') {
if (exp.default) {
pkg.main = exp.default;
@@ -203,28 +202,6 @@ async function prepareExportsEntryPoints(
if (exp.types) {
pkg.types = exp.types;
}
} else {
// This is deferred until after we have created the target directory
compatibilityWriters.push(async targetDir => {
const entryPointDir = resolvePath(targetDir, entryPoint.name);
await fs.ensureDir(entryPointDir);
await fs.writeJson(
resolvePath(entryPointDir, PKG_PATH),
{
// Need a temporary name, as sharing the same name causes some typescript issues with caching of packages names
// And their defined `types` field.
name: `${pkg.name}__${entryPoint.name.toLocaleLowerCase('en-US')}`,
version: pkg.version,
...(exp.default ? { main: posixPath.join('..', exp.default) } : {}),
...(exp.import ? { module: posixPath.join('..', exp.import) } : {}),
...(exp.types ? { types: posixPath.join('..', exp.types) } : {}),
},
{ encoding: 'utf8', spaces: 2 },
);
});
if (Array.isArray(pkg.files) && !pkg.files.includes(entryPoint.name)) {
pkg.files.push(entryPoint.name);
}
}
if (Object.keys(exp).length > 0) {
@@ -238,10 +215,5 @@ async function prepareExportsEntryPoints(
pkg.exports['./package.json'] = './package.json';
}
if (compatibilityWriters.length > 0) {
return async (targetDir: string) => {
await Promise.all(compatibilityWriters.map(writer => writer(targetDir)));
};
}
return undefined;
}