Skip to content

[0.9.x] fix: pass in path aliases from project tsconfig to tsmorph Project #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
const rootMain = require('../../../.storybook/main');
import { StorybookConfig } from '@storybook/angular';

const WebpackAngularTypesPlugin =
require('../../../dist/packages/storybook-webpack-angular-types-plugin/index').WebpackAngularTypesPlugin;

module.exports = {
...rootMain,

core: { builder: 'webpack5' },

const config: StorybookConfig = {
framework: {
name: '@storybook/angular',
options: {},
},
core: {
builder: '@storybook/builder-webpack5',
},
stories: ['../src/app/**/*.mdx', '../src/app/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-essentials'],
addons: ['@storybook/addon-docs'],
webpackFinal: async (config, { configType }) => {
// add your own webpack tweaks if needed
config.plugins.push(
Expand All @@ -20,3 +24,5 @@ module.exports = {
return config;
},
};

export default config;
1 change: 1 addition & 0 deletions packages/angular-demo/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class AppComponent {
amount: 5.99,
unit: '€',
},
hiddenProperty: '',
};

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/angular-demo/src/app/parent.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@

import { Directive, EventEmitter, Input } from '@angular/core';
import { TestInterface } from './internal-types';
import { GreatGrandParentDirective } from '@scope/entrypoint';

interface X<T> {
x: T;
}

@Directive()
export abstract class GrandParentDirective<T, S, R> {
export abstract class GrandParentDirective<T, S, R> extends GreatGrandParentDirective {
@Input() nestedGenericTypeParameterInput?: EventEmitter<EventEmitter<S>>;
}

Expand Down
14 changes: 14 additions & 0 deletions packages/angular-demo/src/app/stories/interface.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ Interfaces need to be annotated with `@include-docs` for the type information to
>
> Add at least one **used** constant or function declaration to the file containing the interface.

To exclude certain properties, annotate them with the `@exclude-docs` annotation:

```typescript
/**
* @include-docs Product
*/
export interface Product {
/**
* @exclude-docs
*/
hiddenProperty: unknown;
}
```

<ArgTypes of="Product" />

## Aliases
Expand Down
5 changes: 5 additions & 0 deletions packages/angular-demo/src/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ export interface UndocumentedSecret {
*/
export interface Product extends Item {
price: Price;

/**
* @exclude-docs
*/
hiddenProperty?: unknown;
}

export interface Item {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Directive, input, output } from '@angular/core';

@Directive({})
export abstract class GreatGrandParentDirective {
/**
* An input from a Directive imported from a path alias
*/
libInput = input<string>();

/**
* An output from a Directive imported from a path alias
*/
libOutput = output<string>();

/**
* A property from a Directive imported from a path alias
*/
libProperty = 42;
}
1 change: 1 addition & 0 deletions packages/angular-demo/src/entrypoint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { GreatGrandParentDirective } from './great-grand-parent.directive';
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { getTsconfig } from 'get-tsconfig';
import { getTsconfig, TsConfigJsonResolved } from 'get-tsconfig';
import * as micromatch from 'micromatch';
import * as path from 'path';
import * as process from 'process';
import { ModuleKind, ModuleResolutionKind, Project, ScriptTarget } from 'ts-morph';
import { Project } from 'ts-morph';
import { Compiler, Module } from 'webpack';
import { DEFAULT_TS_CONFIG_PATH, PLUGIN_NAME } from '../constants';
import {
Expand All @@ -23,14 +23,24 @@ import {
} from './templating/arg-code-block-templates';
import { getPrototypeClassIdCodeBlock } from './templating/prototype-class-id-code-block-template';
import { generateTypeInformation } from './type-extraction/type-extraction';
import { parseModuleKind, parseModuleResolution, parseScriptTarget } from './ts-morph-helpers';

export class WebpackAngularTypesPlugin {
// A queue for modules, that should be processed by the plugin in the next seal-hook
private moduleQueue: Module[] = [];

private readonly tsconfigPaths: string[] = this.getTsconfigPaths();
private readonly tsconfigPath: string;
private readonly tsconfig: TsConfigJsonResolved;

private readonly includedPaths: string[];

constructor(private options: WebpackAngularTypesPluginOptions = {}) {
this.tsconfigPath = this.options.tsconfigPath ?? DEFAULT_TS_CONFIG_PATH;
this.tsconfig = getTsconfig(this.tsconfigPath)?.config ?? {};

this.includedPaths = this.getIncludedPathsFromTsconfig(this.tsconfigPath, this.tsconfig);
}

constructor(private options: WebpackAngularTypesPluginOptions = {}) {}
apply(compiler: Compiler) {
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
compilation.dependencyTemplates.set(CodeDocDependency, new CodeDocDependencyTemplate());
Expand All @@ -43,11 +53,13 @@ export class WebpackAngularTypesPlugin {

compilation.hooks.seal.tap(PLUGIN_NAME, () => {
const smallTsProject = new Project({
// TODO this should be taken from the specified storybook tsconfig in the future
compilerOptions: {
module: ModuleKind.ES2020,
target: ScriptTarget.ESNext,
moduleResolution: ModuleResolutionKind.NodeJs,
module: parseModuleKind(this.tsconfig.compilerOptions?.module),
target: parseScriptTarget(this.tsconfig.compilerOptions?.target),
moduleResolution: parseModuleResolution(
this.tsconfig.compilerOptions?.moduleResolution,
),
paths: this.tsconfig.compilerOptions?.paths ?? {},
},
});
const modulesToProcess = this.moduleQueue
Expand Down Expand Up @@ -129,10 +141,7 @@ export class WebpackAngularTypesPlugin {
const codeDocDependency = new CodeDocDependency(
alias,
uniqueId,
getNonClassArgCodeBlock(
alias,
interfaceInformation.entitiesByCategory,
),
getNonClassArgCodeBlock(alias, interfaceInformation.entitiesByCategory),
);
module.addDependency(codeDocDependency);
}
Expand Down Expand Up @@ -161,7 +170,7 @@ export class WebpackAngularTypesPlugin {
}

private isPathIncludedInTsConfig(pathToCheck: string): boolean {
const res = micromatch([pathToCheck], this.tsconfigPaths, {
const res = micromatch([pathToCheck], this.includedPaths, {
format: this.toUnixPath,
});
return res.length === 1;
Expand All @@ -179,13 +188,15 @@ export class WebpackAngularTypesPlugin {
};
}

private getTsconfigPaths(): string[] {
const tsconfigPath = this.options.tsconfigPath ?? DEFAULT_TS_CONFIG_PATH;
const tsconfigResult = getTsconfig(tsconfigPath);
//
private getIncludedPathsFromTsconfig(
tsconfigPath: string,
tsconfig: TsConfigJsonResolved,
): string[] {
const tsConfigRootDir = path.join(process.cwd(), tsconfigPath, '..');
const includedPaths = tsconfigResult?.config.include || [];
const excludedPaths = tsconfigResult?.config.exclude || [];

const includedPaths = tsconfig.include || [];
const excludedPaths = tsconfig.exclude || [];

return [
...this.transformToAbsolutePaths(tsConfigRootDir, includedPaths),
...this.transformToAbsolutePaths(tsConfigRootDir, excludedPaths).map(this.negateGlob),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// These are ts-morph helper functions that are no publicly exported but should
// bet rather used to get insights/help during development.

import { Type, TypeFormatFlags } from 'ts-morph';
import { ModuleKind, ModuleResolutionKind, ScriptTarget, Type, TypeFormatFlags } from 'ts-morph';

/**
* Prints a type with all available formatters. This helps to understand what
Expand Down Expand Up @@ -59,3 +59,89 @@ export function typeSummary(type: Type): object {
data.push({ check: 'Has alias symbol', result: !!type.getAliasSymbol() });
return data;
}

export function parseModuleKind(moduleString: string | undefined): ModuleKind {
switch (moduleString?.toLowerCase()) {
case 'commonjs':
return ModuleKind.CommonJS;
case 'amd':
return ModuleKind.AMD;
case 'umd':
return ModuleKind.UMD;
case 'system':
return ModuleKind.System;
case 'es6':
case 'es2015':
return ModuleKind.ES2015;
case 'es2020':
return ModuleKind.ES2020;
case 'es2022':
return ModuleKind.ES2022;
case 'esnext':
return ModuleKind.ESNext;
case 'node16':
return ModuleKind.Node16;
case 'nodenext':
return ModuleKind.NodeNext;
default:
console.warn(
`[WebpackAngularTypesPlugin]: Invalid or unknown "compilerOptions.module" retrieved from tsconfig: ${moduleString}. Defaulting to "es2020".`,
);
return ModuleKind.ES2020;
}
}
export function parseScriptTarget(targetString: string | undefined): ScriptTarget {
switch (targetString?.toLowerCase()) {
case 'es3':
return ScriptTarget.ES3;
case 'es5':
return ScriptTarget.ES5;
case 'es6':
case 'es2015':
return ScriptTarget.ES2015;
case 'es2016':
return ScriptTarget.ES2016;
case 'es2017':
return ScriptTarget.ES2017;
case 'es2018':
return ScriptTarget.ES2018;
case 'es2019':
return ScriptTarget.ES2019;
case 'es2020':
return ScriptTarget.ES2020;
case 'es2021':
return ScriptTarget.ES2021;
case 'es2022':
return ScriptTarget.ES2022;
case 'esnext':
return ScriptTarget.ESNext;
default:
console.warn(`
[WebpackAngularTypesPlugin]: Invalid or unknown "compilerOptions.target" retrieved from tsconfig: ${targetString}. Defaulting to "esnext".
`);
return ScriptTarget.ESNext;
}
}

export function parseModuleResolution(
moduleResolutionString: string | undefined,
): ModuleResolutionKind {
switch (moduleResolutionString?.toLowerCase()) {
case 'classic':
return ModuleResolutionKind.Classic;
case 'node':
case 'nodejs':
return ModuleResolutionKind.NodeJs;
case 'node16':
return ModuleResolutionKind.Node16;
case 'nodenext':
return ModuleResolutionKind.NodeNext;
case 'bundler':
return ModuleResolutionKind.Bundler;
default:
console.warn(
`[WebpackAngularTypesPlugin]: Invalid or unknown "compilerOptions.moduleResolution" retrieved from tsconfig: ${moduleResolutionString}. Defaulting to "nodejs".`,
);
return ModuleResolutionKind.NodeJs;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EntitiesByCategory, Entity, InterfaceInformation } from '../../types';
import { InterfaceDeclaration } from 'ts-morph';
import { groupBy } from '../utils';
import { collectBaseInterfaces, getJsDocsIncludeDocsAliases } from './ast-utils';
import { EXCLUDE_DOCS_JS_DOCS_PARAM, groupBy } from '../utils';
import { collectBaseInterfaces, getJsDocsIncludeDocsAliases, hasJsDocsTag } from './ast-utils';
import { mapSignatureToEntity } from './signature-mappers';
import { getterOrSetterInputExists, mergeEntities } from './utils';

Expand All @@ -17,6 +17,10 @@ function getInterfaceEntities(
const entities = new Map<string, Entity>();

for (const signature of [...properties, ...methods]) {
if (hasJsDocsTag(signature, EXCLUDE_DOCS_JS_DOCS_PARAM)) {
continue;
}

// do not include the property if it passes the exclusion test
if (propertiesToExclude?.test(signature.getName())) {
continue;
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
],
"storybook-webpack-angular-types-plugin/extract-arg-types": [
"packages/storybook-webpack-angular-types-plugin/extract-arg-types.ts"
]
],
"@scope/entrypoint": ["packages/angular-demo/src/entrypoint/index.ts"]
}
},
"exclude": ["node_modules", "tmp"]
Expand Down