Skip to content

Commit

Permalink
feat: support option item privateJsDocTag (#24)
Browse files Browse the repository at this point in the history
* feat: support option item privateJsDocTag

* publish 3.2.6-alpha.0

* fix && publish 3.2.6-alpha.1

* add TODO

* add test

* fix: internal namespace exported

* publish alpha.2

* add named export test

* fix: named export

* publish alpha.3
  • Loading branch information
PPpro committed Nov 17, 2022
1 parent 39e2e76 commit 02c8d94
Show file tree
Hide file tree
Showing 13 changed files with 219 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tfig",
"version": "3.2.5",
"version": "3.2.6-alpha.3",
"description": "Yet another tool to generate .d.ts bundle.",
"main": "build/gift.js",
"types": "build/gift.d.ts",
Expand Down
19 changes: 19 additions & 0 deletions source/distribute-exports.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import ts from 'typescript';
import { hasJsDocTag } from './ts-utils';

export function distributeExports(
moduleSymbols: ts.Symbol[],
typeChecker: ts.TypeChecker,
priorityList: string[] = [],
privateJsDocTag?: string,
) {
const parsedPriorityList = priorityList.map((id) => `"${id.replace(/\\/g, '/').replace(/\.(js|ts|d\.ts)$/, '')}"`);

Expand Down Expand Up @@ -66,6 +68,23 @@ export function distributeExports(
if (exportedSymbol.flags & ts.SymbolFlags.Alias) {
originalSymbol = typeChecker.getAliasedSymbol(exportedSymbol);
}
if (privateJsDocTag) {
// TODO: to add a Set to keep the internal originalSymbol, if it's referenced, we put it into the NE namespace.
if ((exportedSymbol.flags & ts.SymbolFlags.Alias) && (originalSymbol.flags & ts.SymbolFlags.Module)) {
// We need to detect tag on exported symbol with alias flag.
const parentNode = exportedSymbol.declarations[0]?.parent?.parent;
if (parentNode) {
const tags = ts.getJSDocTags(parentNode).map(tag => {return { name: tag.tagName.escapedText } as ts.JSDocTagInfo});
if (hasJsDocTag(tags, privateJsDocTag)) {
continue;
}
}
} else {
if (hasJsDocTag(originalSymbol.getJsDocTags(), privateJsDocTag)) {
continue;
}
}
}

if ((exportedSymbol.getFlags() & ts.SymbolFlags.Prototype) &&
(exportedSymbol.getFlags() & ts.SymbolFlags.Property)) {
Expand Down
8 changes: 7 additions & 1 deletion source/gift.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export interface IOptions {

priority?: string[];

/**
* The name of js doc tag, the interface which is marked with this tag, should be racast into the non export namespace called '__private'.
*/
privateJsDocTag?: string;

groups?: Array<{
test: RegExp;
path: string;
Expand Down Expand Up @@ -154,7 +159,7 @@ export function rollupTypes(options: IOptions) {

const rEntityMap = new SymbolEntityMap();

const exportDistribution = distributeExports(entryModules.map((eM) => eM.symbol), typeChecker, options.priority);
const exportDistribution = distributeExports(entryModules.map((eM) => eM.symbol), typeChecker, options.priority, options.privateJsDocTag);

const distributionMap = new Map<distributeExports.InternalModuleMeta, rConcepts.NamespaceTraits>();

Expand Down Expand Up @@ -195,6 +200,7 @@ export function rollupTypes(options: IOptions) {
nameResolver,
resolveEntity: (symbol) => rEntityMap.get(symbol),
registerNonExportedSymbol,
privateJsDocTag: options.privateJsDocTag,
});

const groupSources = new Map<number, GroupSource>();
Expand Down
14 changes: 14 additions & 0 deletions source/recast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export function recastTopLevelModule({
exportPrivates,
resolveEntity,
registerNonExportedSymbol,
privateJsDocTag,
}: {
program: ts.Program,
typeChecker: ts.TypeChecker,
Expand All @@ -22,6 +23,7 @@ export function recastTopLevelModule({
entity: rConcepts.Entity;
addStatements: (statements: ts.Statement[]) => void;
},
privateJsDocTag?: string,
}) {
const nodeFactor = ts.factory;

Expand Down Expand Up @@ -544,6 +546,12 @@ export function recastTopLevelModule({
if (!exportPrivates && isPrivateMember(element)) {
continue;
}
if (privateJsDocTag) {
const symbol = typeChecker.getSymbolAtLocation(element.name!);
if (symbol && tsUtils.hasJsDocTag(symbol.getJsDocTags(), privateJsDocTag)) {
continue;
}
}
// const name = typeof element.name === 'string' ? typeof element.name :
// (element.name ? element.name.getText() : '');
// console.log(` Dump member ${name}`);
Expand Down Expand Up @@ -626,6 +634,12 @@ export function recastTopLevelModule({
function recastTypeElements(typeElements: ts.NodeArray<ts.TypeElement>) {
const result: ts.TypeElement[] = [];
for (const typeElement of typeElements) {
if (privateJsDocTag) {
const symbol = typeChecker.getSymbolAtLocation(typeElement.name!);
if (symbol && tsUtils.hasJsDocTag(symbol.getJsDocTags(), privateJsDocTag)) {
continue;
}
}
const d = recastTypeElement(typeElement);
if (d) {
result.push(d);
Expand Down
9 changes: 9 additions & 0 deletions source/ts-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ export function printSymbol(symbol: ts.Symbol) {
` ${stringifySymbolFlags(symbol.flags)}`);
}

export function hasJsDocTag (tagInfos: ts.JSDocTagInfo[], tagName: string) {
for (let tagInfo of tagInfos) {
if (tagInfo.name === tagName) {
return true;
}
}
return false;
}

export function stringifySymbolFlags(flags: ts.SymbolFlags) {
const satisfies: string[] = [];
for (const key of Object.keys(ts.SymbolFlags)) {
Expand Down
41 changes: 41 additions & 0 deletions test/privateJsDocTag/__snapshots__/private-js-doc-tag.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Private JS doc tag 1`] = `
"declare module \\"testTag\\" {
export namespace publicNS {
export const publicVar: number;
}
export const publicNamedExport: number;
export class A {
publicMethod(): __private._index__InternalA;
get publicAccessor();
set publicAccessor(v: __private._index__InternalA);
publicField: number;
}
export const typeLiternal: {
publicMethod(): __private._index__InternalA;
publicProp: number;
};
export interface publicInterface {
publicMethod(): __private._index__internalInterface;
publicProp: number;
}
export const publicFromAll: number;
export namespace __private {
/**
* @internal
*/
export class _index__InternalA {
}
/**
* @internal
*/
export interface _index__internalInterface {
publicMethod(): _index__InternalA;
publicProp: number;
}
}
export {};
}
"
`;
16 changes: 16 additions & 0 deletions test/privateJsDocTag/private-js-doc-tag.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

import ps from 'path';
import { bundle } from '../../source/gift';

test('Private JS doc tag', async () => {
const inputPath = ps.join(__dirname, './test-case/index.d.ts');
const { groups } = bundle({
input: inputPath,
rootModule: inputPath,
name: 'testTag',
output: ps.join(__dirname, './outout.d.ts'),
privateJsDocTag: 'internal',
});
expect(groups.length).toBe(1);
expect(groups[0].code).toMatchSnapshot();
});
6 changes: 6 additions & 0 deletions test/privateJsDocTag/test-case/all.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

/**
* @internal
*/
export const internalFromAll: number;
export const publicFromAll: number;
86 changes: 86 additions & 0 deletions test/privateJsDocTag/test-case/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as publicNS from './public-namespace';
import * as internalNS from './internal-namespace';

export { publicNS };
/**
* @internal
*/
export { internalNS };

export * from './all';
export { internalNamedExport, publicNamedExport } from './named-export';

/**
* @internal
*/
export class InternalA {}

export class A {
/**
* @internal
*/
internalMethod (): InternalA;

publicMethod (): InternalA;

/**
* @internal
*/
get internalAccessor ();
set internalAccessor (v: InternalA);

get publicAccessor ();
set publicAccessor (v: InternalA);

/**
* @internal
*/
inaternalField: number;

publicField: number;
}

export declare const typeLiternal: {
/**
* @internal
*/
internalMethod (): InternalA;
publicMethod (): InternalA;

/**
* @internal
*/
inaternalProp: number;
publicProp: number;
}

/**
* @internal
*/
export interface internalInterface {
/**
* @internal
*/
internalMethod (): InternalA;
publicMethod (): InternalA;

/**
* @internal
*/
inaternalProp: number;
publicProp: number;
}

export interface publicInterface {
/**
* @internal
*/
internalMethod (): internalInterface;
publicMethod (): internalInterface;

/**
* @internal
*/
inaternalProp: number;
publicProp: number;
}
7 changes: 7 additions & 0 deletions test/privateJsDocTag/test-case/internal-namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export declare const publicVar: number;

/**
* @internal
*/
export declare const internalVar: number;

5 changes: 5 additions & 0 deletions test/privateJsDocTag/test-case/named-export.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const publicNamedExport: number;
/**
* @internal
*/
export const internalNamedExport: number;
7 changes: 7 additions & 0 deletions test/privateJsDocTag/test-case/public-namespace.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export declare const publicVar: number;

/**
* @internal
*/
export declare const internalVar: number;

0 comments on commit 02c8d94

Please sign in to comment.