From 6ba3249cab485f09ab6e16d4eb63076d40a564a0 Mon Sep 17 00:00:00 2001 From: Alice Date: Thu, 27 Jul 2023 09:44:15 -0500 Subject: [PATCH] fix(docs-json): use dts-bundle-generator to bundle types for docs-json (#4619) This fixes an issue that crops up when building documentation using the `docs-json` output target. When #4212 was merged it included a change to `src/declarations/stencil-public-docs.ts` which involved adding imports from private Stencil type declarations. Previously this file had no imports and so was "standalone", however, after this change it now imported some types from `"./private"`. This created a problem when using the `docs-json` output target because when building a Stencil project with that output target enabled the compiled typedef corresponding to `stencil-public-docs.ts` is copied out of `node_modules` and into the users project. See here: https://github.com/ionic-team/stencil/blob/43cfc584c8fbcb77122cbf2a813b1a0303085dce/src/compiler/docs/json/index.ts#L16-L18 This commit fixes the issue by using `dts-bundle-generator` to inline the types imported by `stencil-public-docs.ts` so that the resulting `.d.ts` file has no external dependencies and is therefore portable. --- scripts/bundles/internal.ts | 13 +- scripts/utils/bundle-dts.ts | 30 +- src/compiler/docs/json/index.ts | 9 +- test/docs-json/docs.d.ts | 562 ++++++++++++++++++-------------- 4 files changed, 362 insertions(+), 252 deletions(-) diff --git a/scripts/bundles/internal.ts b/scripts/bundles/internal.ts index 561754ee01b..fbf7decc4a5 100644 --- a/scripts/bundles/internal.ts +++ b/scripts/bundles/internal.ts @@ -1,7 +1,7 @@ import fs from 'fs-extra'; import { join } from 'path'; -import { cleanDts } from '../utils/bundle-dts'; +import { bundleDts, cleanDts } from '../utils/bundle-dts'; import type { BuildOptions } from '../utils/options'; import { writePkgJson } from '../utils/write-pkg-json'; import { internalAppData } from './internal-app-data'; @@ -70,7 +70,16 @@ async function copyStencilInternalDts(opts: BuildOptions, outputInternalDir: str // @stencil/core/internal/stencil-public-docs.d.ts const docsDtsSrcPath = join(declarationsInputDir, 'stencil-public-docs.d.ts'); const docsDtsDestPath = join(outputInternalDir, 'stencil-public-docs.d.ts'); - const docsDts = cleanDts(await fs.readFile(docsDtsSrcPath, 'utf8')); + // We bundle with `dts-bundle-generator` here to ensure that when the `docs-json` + // OT writes a `docs.d.ts` file based on this file it is fully portable. + const docsDts = await bundleDts(opts, docsDtsSrcPath, { + // we want to suppress the `dts-bundle-generator` banner here because we do + // our own later on + noBanner: true, + // we also don't want the types which are inlined into our bundled file to + // be re-exported, which will change the 'surface' of the module + exportReferencedTypes: false, + }); await fs.writeFile(docsDtsDestPath, docsDts); // @stencil/core/internal/stencil-public-runtime.d.ts diff --git a/scripts/utils/bundle-dts.ts b/scripts/utils/bundle-dts.ts index 9e7f5236126..9bd7af5b6c8 100644 --- a/scripts/utils/bundle-dts.ts +++ b/scripts/utils/bundle-dts.ts @@ -1,9 +1,21 @@ -import { generateDtsBundle } from 'dts-bundle-generator/dist/bundle-generator.js'; +import { EntryPointConfig, generateDtsBundle, OutputOptions } from 'dts-bundle-generator/dist/bundle-generator.js'; import fs from 'fs-extra'; import { BuildOptions } from './options'; -export async function bundleDts(opts: BuildOptions, inputFile: string) { +/** + * A thin wrapper for `dts-bundle-generator` which uses our build options to + * set a few things up + * + * **Note**: this file caches its output to disk, and will return any + * previously cached file if not in a prod environment! + * + * @param opts an object holding information about the current build of Stencil + * @param inputFile the path to the file which should be bundled + * @param outputOptions options for bundling the file + * @returns a string containing the bundled typedef + */ +export async function bundleDts(opts: BuildOptions, inputFile: string, outputOptions?: OutputOptions): Promise { const cachedDtsOutput = inputFile + '-bundled.d.ts'; if (!opts.isProd) { @@ -12,15 +24,15 @@ export async function bundleDts(opts: BuildOptions, inputFile: string) { } catch (e) {} } - const entries = [ - { - filePath: inputFile, - }, - ]; + const config: EntryPointConfig = { + filePath: inputFile, + }; - let outputCode = generateDtsBundle(entries).join('\n'); + if (outputOptions) { + config.output = outputOptions; + } - outputCode = cleanDts(outputCode); + const outputCode = cleanDts(generateDtsBundle([config]).join('\n')); await fs.writeFile(cachedDtsOutput, outputCode); diff --git a/src/compiler/docs/json/index.ts b/src/compiler/docs/json/index.ts index 5e34261b664..e8005c34f96 100644 --- a/src/compiler/docs/json/index.ts +++ b/src/compiler/docs/json/index.ts @@ -14,7 +14,14 @@ export const generateJsonDocs = async ( return; } const docsDtsPath = join(config.sys.getCompilerExecutingPath(), '..', '..', 'internal', 'stencil-public-docs.d.ts'); - const docsDts = await compilerCtx.fs.readFile(docsDtsPath); + let docsDts = await compilerCtx.fs.readFile(docsDtsPath); + // this file was written by dts-bundle-generator, which uses tabs for + // indentation. Instead, let's replace those with spaces! + docsDts = docsDts + .split('\n') + .map((line) => line.replace(/\t/g, ' ')) + .join('\n'); + const typesContent = ` /** * This is an autogenerated file created by the Stencil compiler. diff --git a/test/docs-json/docs.d.ts b/test/docs-json/docs.d.ts index 42a02815a42..3f36802faee 100644 --- a/test/docs-json/docs.d.ts +++ b/test/docs-json/docs.d.ts @@ -3,7 +3,87 @@ * This is an autogenerated file created by the Stencil compiler. * DO NOT MODIFY IT MANUALLY */ -import { ComponentCompilerEventComplexType, ComponentCompilerMethodComplexType, ComponentCompilerPropertyComplexType, ComponentCompilerReferencedType } from './stencil-private'; +interface ComponentCompilerPropertyComplexType { + /** + * The string of the original type annotation in the Stencil source code + */ + original: string; + /** + * A 'resolved' type, where e.g. imported types have been resolved and inlined + * + * For instance, an annotation like `(foo: Foo) => string;` will be + * converted to `(foo: { foo: string }) => string;`. + */ + resolved: string; + /** + * A record of the types which were referenced in the assorted type + * annotation in the original source file. + */ + references: ComponentCompilerTypeReferences; +} +type ComponentCompilerTypeReferences = Record; +interface ComponentCompilerTypeReference { + /** + * A type may be defined: + * - locally (in the same file as the component that uses it) + * - globally + * - by importing it into a file (and is defined elsewhere) + */ + location: "local" | "global" | "import"; + /** + * The path to the type reference, if applicable (global types should not need a path associated with them) + */ + path?: string; + /** + * An ID for this type which is unique within a Stencil project. + */ + id: string; +} +interface ComponentCompilerReferencedType { + /** + * The path to the module where the type is declared. + */ + path: string; + /** + * The string of the original type annotation in the Stencil source code + */ + declaration: string; + /** + * An extracted docstring + */ + docstring: string; +} +interface ComponentCompilerEventComplexType { + original: string; + resolved: string; + references: ComponentCompilerTypeReferences; +} +interface ComponentCompilerMethodComplexType { + signature: string; + parameters: CompilerJsDoc[]; + references: ComponentCompilerTypeReferences; + return: string; +} +interface CompilerJsDoc { + /** + * The text associated with the JSDoc + */ + text: string; + /** + * Tags included in the JSDoc + */ + tags: CompilerJsDocTagInfo[]; +} +interface CompilerJsDocTagInfo { + /** + * The name of the tag - e.g. `@deprecated` + */ + name: string; + /** + * Additional text that is associated with the tag - e.g. `@deprecated use v2 of this API` + */ + text?: string; +} /** * The Type Library holds information about the types which are used in a * Stencil project. During compilation, Stencil gathers information about the @@ -20,158 +100,158 @@ export type JsonDocsTypeLibrary = Record