From 500f9956b941a642c79ca3a473a57bc88cb9ee49 Mon Sep 17 00:00:00 2001 From: Alice Pote Date: Tue, 25 Jul 2023 10:51:15 -0500 Subject: [PATCH] fix(docs-json): use dts-bundle-generator to bundle types for docs-json 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 | 9 +- scripts/utils/bundle-dts.ts | 30 +- src/compiler/docs/json/index.ts | 9 +- test/docs-json/docs.d.ts | 570 ++++++++++++++++++-------------- 4 files changed, 362 insertions(+), 256 deletions(-) diff --git a/scripts/bundles/internal.ts b/scripts/bundles/internal.ts index 561754ee01bd..2e1ddbd738a4 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,12 @@ 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, { + noBanner: true, + 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 9e7f5236126c..a0ad617ce0eb 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 ouputOptions options for bundling the file + * @returns a string containing the bundled typedef + */ +export async function bundleDts(opts: BuildOptions, inputFile: string, ouputOptions?: 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 (ouputOptions) { + config.output = ouputOptions; + } - 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 5e34261b6646..e8005c34f96e 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 42a02815a423..dc9f2d8d6a45 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