From d849865b6fafdf7d7c8eb5b96e7d5fd933105e16 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 7 Nov 2024 13:22:54 +0100 Subject: [PATCH] cli: improve autocomplete of extra entry points in published packages Signed-off-by: Patrik Oldsberg --- .changeset/seven-berries-listen.md | 5 ++ .../cli/src/lib/packager/productionPack.ts | 62 +++++-------------- 2 files changed, 22 insertions(+), 45 deletions(-) create mode 100644 .changeset/seven-berries-listen.md diff --git a/.changeset/seven-berries-listen.md b/.changeset/seven-berries-listen.md new file mode 100644 index 0000000000..2ab18d9e19 --- /dev/null +++ b/.changeset/seven-berries-listen.md @@ -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. diff --git a/packages/cli/src/lib/packager/productionPack.ts b/packages/cli/src/lib/packager/productionPack.ts index c15a7f8b9e..9736e97b8d 100644 --- a/packages/cli/src/lib/packager/productionPack.ts +++ b/packages/cli/src/lib/packager/productionPack.ts @@ -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>; - const compatibilityWriters = new Array< - (targetDir: string) => Promise - >(); - 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; }