Skip to content

Commit

Permalink
Merge (#20)
Browse files Browse the repository at this point in the history
* Referencing Symbol

* Release 3.2.2; Fix an edge case

* Release 3.2.3; Fix some error
  • Loading branch information
shrinktofit committed Jan 6, 2022
1 parent 6e0c33e commit 8d34e9e
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 48 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.1",
"version": "3.2.3",
"description": "Yet another tool to generate .d.ts bundle.",
"main": "build/gift.js",
"types": "build/gift.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion source/distribute-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function distributeExports(
typeChecker: ts.TypeChecker,
priorityList: string[] = [],
) {
const parsedPriorityList = priorityList.map((id) => `"${id}"`);
const parsedPriorityList = priorityList.map((id) => `"${id.replace(/\\/g, '/').replace(/\.(js|ts|d\.ts)$/, '')}"`);

const exportMap: Map<ts.Symbol, SymbolInfo> = new Map();

Expand Down
103 changes: 83 additions & 20 deletions source/gift.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import * as fs from 'fs-extra';
import * as path from 'path';
import ts from 'typescript';
import ts, { NodeFlags } from 'typescript';
import ps from 'path';
import * as rConcepts from './r-concepts';
import { NameResolver } from './name-resolver';
Expand Down Expand Up @@ -89,6 +89,7 @@ class SymbolEntityMap {

export function rollupTypes(options: IOptions) {
const inputs = Array.isArray(options.input) ? options.input : [options.input];
const rootDir = options.rootDir ?? ps.dirname(inputs[0]);
const entries = getEntries();
const program = createProgram();
const typeChecker = program.getTypeChecker();
Expand Down Expand Up @@ -116,7 +117,7 @@ export function rollupTypes(options: IOptions) {

function createTscOptions(): ts.CompilerOptions {
return {
rootDir: options.rootDir ?? ps.dirname(inputs[0]),
rootDir,
};
}

Expand All @@ -133,11 +134,19 @@ export function rollupTypes(options: IOptions) {

const entryModules = Object.entries(entries).map(([entryModuleName, entryModuleId]) => {
const name = `"${entryModuleId}"`;
const moduleSymbol = ambientModules.find(m => m.getName() === name);
let moduleSymbol = ambientModules.find(m => m.getName() === name);
if (!moduleSymbol) {
const sourceFile = program.getSourceFile(entryModuleId);
if (sourceFile) {
moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
}
}
if (!moduleSymbol) {
throw new Error(`Entry ${entryModuleName}: ${entryModuleId} is not found.`);
}
const referencingSymbols = getReferencingSymbolsInModule(moduleSymbol);
return {
referencingSymbols,
name: entryModuleName,
symbol: moduleSymbol,
};
Expand Down Expand Up @@ -220,6 +229,32 @@ export function rollupTypes(options: IOptions) {
}
return Array.from(groupSources.values());

function getReferencingSymbolsInModule(symbol: ts.Symbol) {
const referencingSymbols = new Set<ts.Symbol>();

const declarations = symbol.getDeclarations();
if (!declarations || declarations.length === 0) {
return referencingSymbols;
}

for (const declaration of declarations) {
const scopeSymbols = typeChecker.getSymbolsInScope(declaration, -1);
for (const scopeSymbol of scopeSymbols) {
const declarations = scopeSymbol.getDeclarations();
if (!declarations || declarations.length === 0 || declarations.some((declaration) => {
const sourceFile = declaration.getSourceFile();
return program.isSourceFileDefaultLibrary(sourceFile) ||
program.isSourceFileFromExternalLibrary(sourceFile);
})) {
continue;
}
referencingSymbols.add(scopeSymbol);
}
}

return referencingSymbols;
}

function createREntities(
moduleExportDistribution: distributeExports.InternalModuleMeta,
parent: rConcepts.NamespaceTraits,
Expand Down Expand Up @@ -283,14 +318,13 @@ export function rollupTypes(options: IOptions) {
return;
}

const enclosingModuleSymbol = getEnclosingModuleSymbol(symbol);
if (!enclosingModuleSymbol) {
const enclosingModuleName = getEnclosingModuleName(symbol);
if (!enclosingModuleName) {
return null;
}

const moduleName = enclosingModuleSymbol.getName();
for (const { sourceModule, targetModule } of nonExportedSymbolDistribution) {
if (!sourceModule.test(moduleName)) {
if (!sourceModule.test(enclosingModuleName)) {
continue;
}
const externalModule = rExternalModules.find(({ name }) => name === targetModule);
Expand All @@ -303,19 +337,22 @@ export function rollupTypes(options: IOptions) {
return null;
}

function getEnclosingModuleSymbol(symbol: ts.Symbol): ts.Symbol | null {
function getEnclosingModuleName(symbol: ts.Symbol): string | null {
const declarations = symbol.getDeclarations();
if (!declarations || declarations.length === 0) {
return null;
}
const declaration0 = declarations[0];
let currentNode: ts.Node = declaration0;
const transformModuleName = (fileName: string) => {
return fileName.replace(/[\\]/g, '/');
};
while (true) {
if (ts.isSourceFile(currentNode)) {
return typeChecker.getSymbolAtLocation(currentNode) ?? null;
return transformModuleName(currentNode.fileName);
}
if (ts.isModuleDeclaration(currentNode) && !(currentNode.flags & ts.NodeFlags.Namespace)) {
return typeChecker.getSymbolAtLocation(currentNode.name) ?? null;
return transformModuleName(typeChecker.getSymbolAtLocation(currentNode.name)?.getName() ?? '');
}
currentNode = currentNode.parent;
}
Expand All @@ -328,29 +365,55 @@ export function rollupTypes(options: IOptions) {
}

const namespaces: string[] = [];

let current: ts.Node = declaration0;
while (true) {

if (!ts.isSourceFile(declaration0)) {
// If the input isn't source file,
// we directly extract its name in symbol,
// otherwise we handle it further.
namespaces.push(generateIdFromString(symbol.getName()));
current = current.parent;
}

while (current) {
if (ts.isSourceFile(current)) {
namespaces.unshift(generateIdFromSourceFileName(current.fileName));
break;
} else if (ts.isModuleDeclaration(current) && ts.isSourceFile(current.parent)) {
namespaces.push(createIdFromModuleName(current.name));
} else if (ts.isModuleDeclaration(current)) {
namespaces.unshift(generateIdFromModuleDeclarationName(current.name));
if (ts.isSourceFile(current.parent) &&
!(current.flags & NodeFlags.Namespace) &&
ts.isStringLiteral(current.name)) {
// is `[declare] module "" {}` under source file
break;
}
}
current = current.parent;
}

if (!(ts.isModuleDeclaration(declaration0) && ts.isSourceFile(declaration0.parent))) {
namespaces.push(symbol.getName());
}
return namespaces.join('_');

const name = namespaces.join('ლ');
return name;
}

function createIdFromModuleName(name: ts.ModuleName) {
function generateIdFromModuleDeclarationName(name: ts.ModuleName) {
if (ts.isIdentifier(name)) {
return name.text;
} else {
return name.text.replace(/[\/\\-]/g, '_');
return generateIdFromString(name.text);
}
}

function generateIdFromSourceFileName(fileName: string) {
const relativeFromRoot = ps.relative(rootDir, fileName);
const extensionStriped = relativeFromRoot.replace(/\.(js|ts|d\.ts)$/, '');
return generateIdFromString(extensionStriped);
}

function generateIdFromString(text: string) {
// To handle keywords and illegal first letters, we prefix it with a legal letter.
return 'ლ' + text.replace(/[\/\\-]/g, 'ლ').replace(/['":\.@]/g, '');
}
}

function emit(groupSource: GroupSource): GroupResult {
Expand Down
29 changes: 24 additions & 5 deletions source/recast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,17 @@ export function recastTopLevelModule({
return null;
}

function recastSourceFileDeclarationAsNamespaceDeclaration(sourceFile: ts.SourceFile, newName: string) {
const newBody = nodeFactor.createModuleBlock(recastStatements(sourceFile.statements));
return nodeFactor.createModuleDeclaration(
undefined, // decorators
undefined,
nodeFactor.createIdentifier(newName),
newBody,
ts.NodeFlags.Namespace, // TODO: flags. Maybe `ts.NodeFlags.Namespace`?
);
}

function recastModuleDeclarationAsNamespaceDeclaration(moduleDeclaration: ts.ModuleDeclaration, newName: string) {
const body = moduleDeclaration.body;
let newBody: ts.ModuleDeclaration['body'];
Expand Down Expand Up @@ -682,8 +693,13 @@ export function recastTopLevelModule({
}
const result: ts.Modifier[] = [];
for (const modifier of modifiers) {
if (modifier.kind !== ts.SyntaxKind.DefaultKeyword) {
result.push(modifier);
switch (modifier.kind) {
case ts.SyntaxKind.DefaultKeyword:
case ts.SyntaxKind.DeclareKeyword:
break;
default:
result.push(modifier);
break;
}
}
return result;
Expand Down Expand Up @@ -1132,11 +1148,14 @@ export function recastTopLevelModule({

const statements: ts.Statement[] = [];
for (const declaration of declarations) {
if (ts.isModuleDeclaration(declaration)) {
if (ts.isSourceFile(declaration) || ts.isModuleDeclaration(declaration)) {
// `namespace xx {}`, `import * as`, but not exported
const namespaceTraits = entity.addNamespaceTraits();
nameResolver.enter(namespaceTraits);
statements.push(
recastModuleDeclarationAsNamespaceDeclaration(declaration, entity.name));
const newNamespaceDeclaration = ts.isSourceFile(declaration)
? recastSourceFileDeclarationAsNamespaceDeclaration(declaration, entity.name)
: recastModuleDeclarationAsNamespaceDeclaration(declaration, entity.name);
statements.push(newNamespaceDeclaration);
nameResolver.leave();
} else {
pushIfNonNull(
Expand Down
2 changes: 1 addition & 1 deletion source/ts-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function splitLeftmost(entityName: ts.EntityName) {
if (ts.isIdentifier(leftmost)) {
break;
}
rights.push(leftmost.right);
rights.unshift(leftmost.right);
leftmost = leftmost.left;
}
return {
Expand Down
10 changes: 5 additions & 5 deletions test/export-priority/export-priority.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ describe('Export priority', () => {
test('Specified priority', () => {
const { entryModules, typeChecker } = genProg(
[ ps.join(__dirname, 'specified-priority', 'input.d.ts') ],
[ 'a', 'b' ],
[ 'aa', 'bb' ],
);
const [aSymbol, bSymbol] = entryModules;

{
const [aDistribute, bDistribute] = distributeExports(entryModules, typeChecker, [
'a',
'b',
'aa',
'bb',
]);
expect(aDistribute.mainExports.length).toBe(1);
expect(aDistribute.mainExports[0].exportSymbol.getName()).toBe('C');
Expand All @@ -39,8 +39,8 @@ describe('Export priority', () => {

{
const [aDistribute, bDistribute] = distributeExports(entryModules, typeChecker, [
'b',
'a',
'bb',
'aa',
]);
expect(bDistribute.mainExports.length).toBe(1);
expect(bDistribute.mainExports[0].exportSymbol.getName()).toBe('C');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

exports[`Non exported symbol distribution: Export to "firstly encountered" module 1`] = `
"declare module \\"baz\\" {
export function foo(): __private.internal_NonExported;
export function foo(): __private.internalლლNonExported;
export namespace __private {
export class internal_NonExported {
export class ლinternalლლNonExported {
}
}
export {};
}
declare module \\"foo\\" {
export function foo(): ___private.internal_NonExported;
export function foo(): ___private.internalლლNonExported;
import { __private as ___private } from \\"baz\\";
export {};
}
declare module \\"bar\\" {
export function bar(): ___private.internal_NonExported;
export function bar(): ___private.internalლლNonExported;
import { __private as ___private } from \\"baz\\";
export {};
}
Expand All @@ -24,19 +24,19 @@ declare module \\"bar\\" {

exports[`Non exported symbol distribution: Export to bar 1`] = `
"declare module \\"baz\\" {
export function foo(): ___private.internal_NonExported;
export function foo(): ___private.internalლლNonExported;
import { __private as ___private } from \\"bar\\";
export {};
}
declare module \\"foo\\" {
export function foo(): ___private.internal_NonExported;
export function foo(): ___private.internalლლNonExported;
import { __private as ___private } from \\"bar\\";
export {};
}
declare module \\"bar\\" {
export function bar(): __private.internal_NonExported;
export function bar(): __private.internalლლNonExported;
export namespace __private {
export class internal_NonExported {
export class ლinternalლლNonExported {
}
}
export {};
Expand All @@ -46,20 +46,20 @@ declare module \\"bar\\" {

exports[`Non exported symbol distribution: Export to foo 1`] = `
"declare module \\"baz\\" {
export function foo(): ___private.internal_NonExported;
export function foo(): ___private.internalლლNonExported;
import { __private as ___private } from \\"foo\\";
export {};
}
declare module \\"foo\\" {
export function foo(): __private.internal_NonExported;
export function foo(): __private.internalლლNonExported;
export namespace __private {
export class internal_NonExported {
export class ლinternalლლNonExported {
}
}
export {};
}
declare module \\"bar\\" {
export function bar(): ___private.internal_NonExported;
export function bar(): ___private.internalლლNonExported;
import { __private as ___private } from \\"foo\\";
export {};
}
Expand Down
4 changes: 2 additions & 2 deletions test/types/__snapshots__/types.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ exports[`From triple slash directive 1`] = `
export class Component {
}
export class Node {
getComponent<T extends Component>(constructor: __private.Constructor<T>): T | null;
getComponent<T extends Component>(constructor: __private.typesglobalsლლConstructor<T>): T | null;
}
export namespace __private {
export type Constructor<T = unknown> = new (...args: any[]) => T;
export type ლtypesლglobalsლლConstructor<T = unknown> = new (...args: any[]) => T;
}
export {};
}
Expand Down

0 comments on commit 8d34e9e

Please sign in to comment.