Skip to content

Commit

Permalink
feat: support stripping internal types
Browse files Browse the repository at this point in the history
... using `@internal` JSDoc tags, if the `stripInternal` tsconfig compiler option is enabled. This means types annotated with `@internal` will be stripped both from .js/ts files going through the TS program aswell as d.ts files going through our logic

part of sveltejs/svelte#12292
  • Loading branch information
dummdidumm committed Aug 21, 2024
1 parent 2ce1fcc commit 8486126
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# dts-buddy changelog

## 0.5.2

- Support stripping internal types using `@internal` JSDoc tags ([#87](https://github.com/Rich-Harris/dts-buddy/pull/87))

## 0.5.1

- Use more TypeScript-friendly way of preventing unintended exports ([#85](https://github.com/Rich-Harris/dts-buddy/pull/85))
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dts-buddy",
"version": "0.5.1",
"version": "0.5.2",
"description": "A tool for creating .d.ts bundles",
"repository": {
"type": "git",
Expand Down
24 changes: 21 additions & 3 deletions src/create-module-declaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,29 @@ import path from 'node:path';
import ts from 'typescript';
import * as tsu from 'ts-api-utils';
import MagicString from 'magic-string';
import { clean_jsdoc, get_dts, is_declaration, is_reference, resolve_dts, walk } from './utils.js';
import {
clean_jsdoc,
get_dts,
is_declaration,
is_internal,
is_reference,
resolve_dts,
walk
} from './utils.js';

/**
* @param {string} id
* @param {string} entry
* @param {Record<string, string>} created
* @param {(file: string, specifier: string) => string | null} resolve
* @param {{ stripInternal?: boolean }} options
* @returns {{
* content: string;
* mappings: Map<string, Mapping>;
* ambient: ModuleReference[];
* }}
*/
export function create_module_declaration(id, entry, created, resolve) {
export function create_module_declaration(id, entry, created, resolve, options) {
let content = '';

/** @type {Map<string, Mapping>} */
Expand Down Expand Up @@ -57,7 +66,7 @@ export function create_module_declaration(id, entry, created, resolve) {
const included = new Set([entry]);

for (const file of included) {
const module = get_dts(file, created, resolve);
const module = get_dts(file, created, resolve, options);

for (const name of module.globals) {
globals.add(name);
Expand Down Expand Up @@ -247,6 +256,10 @@ export function create_module_declaration(id, entry, created, resolve) {
}

if (is_declaration(node)) {
if (is_internal(node) && options.stripInternal) {
result.remove(node.pos, node.end);
}

const identifier = ts.isVariableStatement(node)
? ts.getNameOfDeclaration(node.declarationList.declarations[0])
: ts.getNameOfDeclaration(node);
Expand Down Expand Up @@ -348,6 +361,11 @@ export function create_module_declaration(id, entry, created, resolve) {
}

walk(node, (node) => {
if (ts.isPropertySignature(node) && is_internal(node) && options.stripInternal) {
result.remove(node.pos, node.end);
return false;
}

if (is_reference(node)) {
const name = node.getText(module.ast);

Expand Down
3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ export async function createBundle(options) {
id,
modules[id],
created,
resolve
resolve,
compilerOptions
);

types += content;
Expand Down
32 changes: 28 additions & 4 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ export function get_jsdoc(node) {
return jsDoc;
}

/** @param {ts.Node} node */
export function is_internal(node) {
const jsdoc = get_jsdoc(node);

if (jsdoc) {
for (const jsDoc of jsdoc) {
if (jsDoc.tags?.some((tag) => tag.tagName.escapedText === 'internal')) {
return true;
}
}
}

return false;
}

/** @param {ts.Node} node */
export function get_jsdoc_imports(node) {
/** @type {import('typescript').TypeNode[]} */
Expand Down Expand Up @@ -180,8 +195,9 @@ export function write(file, contents) {
* @param {string} file
* @param {Record<string, string>} created
* @param {(file: string, specifier: string) => string | null} resolve
* @param {{ stripInternal?: boolean }} options
*/
export function get_dts(file, created, resolve) {
export function get_dts(file, created, resolve, options) {
const dts = created[file] ?? fs.readFileSync(file, 'utf8');
const ast = ts.createSourceFile(file, dts, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
const locator = getLocator(dts, { offsetLine: 1 });
Expand Down Expand Up @@ -325,6 +341,8 @@ export function get_dts(file, created, resolve) {
}

if (is_declaration(node)) {
if (is_internal(node) && options.stripInternal) return;

const identifier = ts.isVariableStatement(node)
? ts.getNameOfDeclaration(node.declarationList.declarations[0])
: ts.getNameOfDeclaration(node);
Expand Down Expand Up @@ -383,6 +401,10 @@ export function get_dts(file, created, resolve) {
current = previous;
} else {
walk(node, (node) => {
if (ts.isPropertySignature(node) && is_internal(node) && options.stripInternal) {
return false;
}

// `import('./foo').Foo` -> `Foo`
if (
ts.isImportTypeNode(node) &&
Expand Down Expand Up @@ -466,11 +488,13 @@ export function resolve_dts(from, to) {

/**
* @param {ts.Node} node
* @param {(node: ts.Node) => void} callback
* @param {(node: ts.Node) => void | false} callback
*/
export function walk(node, callback) {
callback(node);
ts.forEachChild(node, (child) => walk(child, callback));
const go_on = callback(node);
if (go_on !== false) {
ts.forEachChild(node, (child) => walk(child, callback));
}
}

/**
Expand Down
6 changes: 6 additions & 0 deletions test/samples/strip-internal/input/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { Foo } from './others';

/** @internal TS itself will take care of stripping this */
export interface TSdddd {
foo: boolean;
}
1 change: 1 addition & 0 deletions test/samples/strip-internal/input/internal.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Internal = { foo: boolean };
7 changes: 7 additions & 0 deletions test/samples/strip-internal/input/others.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Internal } from './internal';

export interface Foo {
bar: string;
/** @internal */
baz: Internal;
}
3 changes: 3 additions & 0 deletions test/samples/strip-internal/options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"stripInternal": true
}
9 changes: 9 additions & 0 deletions test/samples/strip-internal/output/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module 'strip-internal' {
export interface Foo {
bar: string;
}

export {};
}

//# sourceMappingURL=index.d.ts.map
14 changes: 14 additions & 0 deletions test/samples/strip-internal/output/index.d.ts.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"version": 3,
"file": "index.d.ts",
"names": [
"Foo"
],
"sources": [
"../input/others.d.ts"
],
"sourcesContent": [
null
],
"mappings": ";kBAEiBA,GAAGA"
}
5 changes: 4 additions & 1 deletion test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ for (const sample of fs.readdirSync('test/samples')) {

const compilerOptions = {
/** @type {Record<string, string[]>} */
paths: {}
paths: {},
...(fs.existsSync(`${dir}/options.json`)
? JSON.parse(fs.readFileSync(`${dir}/options.json`, 'utf-8'))
: {})
};

for (const file of glob('**', { cwd: `${dir}/input`, filesOnly: true })) {
Expand Down

0 comments on commit 8486126

Please sign in to comment.