Skip to content

Commit

Permalink
feat(core): generate example module support vars define providers, de…
Browse files Browse the repository at this point in the history
…clarations and imports, and remove default export from module (#400)
  • Loading branch information
why520crazy committed Oct 30, 2022
1 parent 7d0cee7 commit 55f2371
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*.md
*.json
*.hbs
fixtures/**/**
**/fixtures/**/**
2 changes: 1 addition & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
extends: ['@commitlint/config-angular'],
rules: {
'header-max-length': [2, 'always', 120],
'header-max-length': [2, 'always', 140],
'scope-enum': [2, 'always', ['cli', 'core', 'template', 'alib', 'ngdoc', 'toolkit', 'deps', 'examples']]
}
};
42 changes: 34 additions & 8 deletions packages/core/src/ast-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HostTree } from '@angular-devkit/schematics';
import { NgSourceFile, ts, getNodeText } from '@docgeni/ngdoc';
import { applyToUpdateRecorder, Change, InsertChange } from '@schematics/angular/utility/change';
import { toolkit } from '@docgeni/toolkit';
import { applyToUpdateRecorder, Change, InsertChange, RemoveChange } from '@schematics/angular/utility/change';
import { NgModuleMetadata } from './types/module';

export function generateComponentsModule(
Expand All @@ -10,7 +11,12 @@ export function generateComponentsModule(
): string {
const changes = insertImports(sourceFile, importStructures);
const sourceText = sourceFile.origin.getFullText();
changes.push(new InsertChange(sourceFile.origin.fileName, sourceText.length, ngModuleText));
const defaultExportNode = sourceFile.getDefaultExportNode();
if (defaultExportNode) {
changes.push(new RemoveChange(sourceFile.origin.fileName, defaultExportNode.pos, defaultExportNode.getFullText()));
}
changes.push(new InsertChange(sourceFile.origin.fileName, defaultExportNode ? defaultExportNode.pos : sourceText.length, ngModuleText));

return applyChanges(sourceFile.origin.fileName, sourceText, changes);
}

Expand Down Expand Up @@ -69,16 +75,36 @@ export function combineNgModuleMetaData(metadata: NgModuleMetadata, appendMetada

metadata = { ...defaultModuleMetadata, ...metadata };
appendMetadata = { ...defaultModuleMetadata, ...appendMetadata };

return {
declarations: Array.from(new Set([...metadata.declarations, ...appendMetadata?.declarations])),
entryComponents: Array.from(new Set([...metadata.entryComponents, ...appendMetadata?.entryComponents])),
providers: Array.from(new Set([...metadata.providers, ...appendMetadata?.providers])),
imports: Array.from(new Set([...metadata.imports, ...appendMetadata?.imports])),
exports: Array.from(new Set([...metadata.exports, ...appendMetadata.exports]))
declarations: combineArray(metadata.declarations, appendMetadata?.declarations),
entryComponents: combineArray(metadata.entryComponents, appendMetadata?.entryComponents),
providers: combineArray(metadata.providers, appendMetadata.providers),
imports: combineArray(metadata.imports, appendMetadata?.imports),
exports: combineArray(metadata.exports, appendMetadata.exports)
};
}

function combineArray(origin: string | string[], append?: string[]) {
const result: string[] = [];
if (toolkit.utils.isArray(origin)) {
origin.forEach(item => {
result.push(item);
});
} else {
result.push(`...${origin}`);
}
if (append) {
if (toolkit.utils.isArray(append)) {
append.forEach(item => {
if (!result.includes(item)) {
result.push(item);
}
});
}
}
return result;
}

export function applyChanges(filePath: string, originContent: string, changes: Change[]) {
const hostTree = new HostTree();
hostTree.create(filePath, originContent);
Expand Down
57 changes: 54 additions & 3 deletions packages/core/src/builders/examples-module.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createNgSourceFile } from '@docgeni/ngdoc';
import { generateComponentExamplesModule } from './examples-module';
import * as utils from '../ast-utils';
import { compatibleNormalize } from '../markdown';

describe('#examples-module', () => {
const sourceText = `
Expand All @@ -13,16 +14,16 @@ export default {
providers: [ AppService ]
};
`;
const ngSourceFile = createNgSourceFile('module.ts', sourceText);

it('should generate module success ', async () => {
it('should generate module success with mock', async () => {
const ngSourceFile = createNgSourceFile('module.ts', sourceText);
const components = [{ name: 'AlibComponent', moduleSpecifier: './basic.component' }];
const getNgModuleMetadataFromDefaultExportSpy = spyOn(utils, 'getNgModuleMetadataFromDefaultExport');
const combineNgModuleMetaDataSpy = spyOn(utils, 'combineNgModuleMetaData');
const generateComponentsModuleSpy = spyOn(utils, 'generateComponentsModule');

const metaData = {
declarations: ['AlibComponent', 'AppComponent'],
declarations: ['AppComponent', 'AlibComponent'],
entryComponents: ['AlibComponent'],
providers: ['AppService'],
imports: ['CommonModule'],
Expand Down Expand Up @@ -74,4 +75,54 @@ export class MyButtonExamplesModule {}

expect(output).toEqual(componentModuleText);
});

it('should generate module success ', async () => {
const ngSourceFile = createNgSourceFile('module.ts', sourceText);
const components = [{ name: 'AlibComponent', moduleSpecifier: './basic.component' }];
const metaData = {
declarations: ['AppComponent', 'AlibComponent'],
entryComponents: ['AlibComponent'],
providers: ['AppService'],
imports: ['CommonModule'],
exports: ['AlibComponent']
};

const moduleMetadataArgs = Object.keys(metaData)
.map(key => {
return `${key}: [ ${metaData[key].join(', ')} ]`;
})
.join(',\n ');
const moduleText = `
@NgModule({
${moduleMetadataArgs}
})
export class MyButtonExamplesModule {}
`;

const output = await generateComponentExamplesModule(ngSourceFile, 'MyButtonExamplesModule', components);
expect(output).toContain(moduleText);
expect(output).toContain(`import { AlibComponent } from './basic.component';`);
expect(output).toContain(`import { NgModule } from '@angular/core';`);
expect(output).not.toContain(`export default`);
});

const sourceTextWithVarsProviders = `
import { CommonModule } from '@angular/common';
import { AppComponent } from './app.component';
const myProviders = [ AppService ];
export default {
imports: [ CommonModule ],
declarations: [ AppComponent ],
providers: myProviders
};
`;
it('should generate module with vars myProviders ', async () => {
const ngSourceFile = createNgSourceFile('module.ts', sourceTextWithVarsProviders);
const components = [{ name: 'AlibComponent', moduleSpecifier: './basic.component' }];

const output = await generateComponentExamplesModule(ngSourceFile, 'MyButtonExamplesModule', components);
expect(output).toContain(`const myProviders = [ AppService ];`);
expect(output).toContain(`providers: [ ...myProviders ]`);
});
});
6 changes: 5 additions & 1 deletion packages/core/src/builders/examples-module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NgSourceFile } from '@docgeni/ngdoc';
import { toolkit } from '@docgeni/toolkit';
import { generateComponentsModule, getNgModuleMetadataFromDefaultExport, combineNgModuleMetaData } from '../ast-utils';
import { NgModuleMetadata } from '../types/module';

Expand Down Expand Up @@ -28,7 +29,10 @@ export async function generateComponentExamplesModule(
function generateNgModuleText(ngModuleName: string, moduleMetadata: NgModuleMetadata) {
const moduleMetadataArgs = Object.keys(moduleMetadata)
.map(key => {
return `${key}: [ ${moduleMetadata[key].join(', ')} ]`;
return (
`${key}:` +
(toolkit.utils.isArray(moduleMetadata[key]) ? ` [ ${moduleMetadata[key].join(', ')} ]` : ` ${moduleMetadata[key]}`)
);
})
.join(',\n ');
return `
Expand Down
12 changes: 7 additions & 5 deletions packages/core/src/builders/library-component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ describe('#library-component', () => {
expect(component.getDocItem('zh-cn')).toBeFalsy();
expect(component.getDocItem('en-us')).toBeFalsy();

const apiDocsDefinitions: Record<string, ApiDeclaration[]> = {
const apiDocsDefinitions: Record<string, ApiDeclaration[]> = ({
'zh-cn': [
{
name: 'Button',
Expand All @@ -253,7 +253,7 @@ describe('#library-component', () => {
properties: []
}
]
};
} as unknown) as Record<string, ApiDeclaration[]>;
const componentSpectator = new LibraryComponentSpectator(component, apiDocsDefinitions);

await component.build();
Expand Down Expand Up @@ -361,14 +361,15 @@ describe('#library-component', () => {
});

describe('api-docs', () => {
const apiDocsDefinitions: Record<string, ApiDeclaration[]> = {
const apiDocsDefinitions: Record<string, ApiDeclaration[]> = ({
'zh-cn': [
{
name: 'Button',
type: 'component',
description: 'This is button zh-cn desc',
properties: [
{
kind: 'Input',
name: 'thyType',
type: 'string',
default: 'primary'
Expand All @@ -390,7 +391,7 @@ describe('#library-component', () => {
]
}
]
};
} as unknown) as Record<string, ApiDeclaration[]>;

beforeEach(async () => {
fixture = await loadFixture('library-component-button');
Expand Down Expand Up @@ -441,6 +442,7 @@ describe('#library-component', () => {
type: 'component',
properties: [
{
kind: 'Input',
name: 'thyType',
type: 'string',
default: 'primary',
Expand Down Expand Up @@ -521,7 +523,7 @@ describe('#library-component', () => {

await expectFiles(context.host, {
[`${absDestSiteContentComponentsPath}/button/index.ts`]: fixture.output['index.ts'],
[`${absDestSiteContentComponentsPath}/button/module.ts`]: fixture.output['module.ts'],
[`${absDestSiteContentComponentsPath}/button/module.ts`]: fixture.output['module.ts.txt'],
[`${absDestSiteContentComponentsPath}/button/basic/basic.component.ts`]: fixture.output['basic/basic.component.ts'],
[`${absDestSiteContentComponentsPath}/button/basic/basic.component.html`]: fixture.output['basic/basic.component.html']
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class ComponentBuilder {
}
const componentText = await this.docgeniHost.readFile(this.entryComponentFullPath);
const componentFile = createNgSourceFile(this.entryComponentFullPath, componentText);
const exportDefault = componentFile.getDefaultExports() as { selector: string; component: string };
const exportDefault = componentFile.getDefaultExports<{ selector: string; component: string }>();

if (exportDefault) {
this.componentData = { selector: exportDefault.selector, name: exportDefault.component };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as systemPath from 'path';
import * as builtInModule from './built-in-module';
import { ComponentBuilder } from './component-builder';

const COMPONENTS_ROOT_PATH = `${DEFAULT_TEST_ROOT_PATH}/.docgeni/components`;
const COMPONENTS_ROOT_PATH: string = `${DEFAULT_TEST_ROOT_PATH}/.docgeni/components`;

let linuxOnlyIt: typeof it = it;
if (process.platform.startsWith('win') || process.platform.startsWith('darwin')) {
Expand Down Expand Up @@ -56,7 +56,7 @@ describe('#components-builder', () => {

const components = (componentsBuilder as any).components as Map<string, ComponentBuilder>;

expect(components.get(`${COMPONENTS_ROOT_PATH}/hello`).componentData).toEqual({
expect(components.get(`${COMPONENTS_ROOT_PATH}/hello`)!.componentData).toEqual({
selector: 'hello',
name: 'HelloComponent'
});
Expand All @@ -75,7 +75,7 @@ describe('#components-builder', () => {

const components = (componentsBuilder as any).components as Map<string, ComponentBuilder>;

expect(components.get(`${COMPONENTS_ROOT_PATH}/color`).componentData).toEqual({
expect(components.get(`${COMPONENTS_ROOT_PATH}/color`)!.componentData).toEqual({
selector: 'my-color',
name: 'ColorComponent'
});
Expand All @@ -94,7 +94,7 @@ describe('#components-builder', () => {

const components = (componentsBuilder as any).components as Map<string, ComponentBuilder>;

expect(components.get(`${COMPONENTS_ROOT_PATH}/hello`).componentData).toEqual({
expect(components.get(`${COMPONENTS_ROOT_PATH}/hello`)!.componentData).toEqual({
selector: 'hello',
name: 'HelloComponent'
});
Expand Down Expand Up @@ -123,7 +123,7 @@ describe('#components-builder', () => {
await componentsBuilder.build();
await componentsBuilder.emit();
(context as any).watch = true;
subscription = componentsBuilder.watch();
subscription = componentsBuilder.watch() as Subscription;
});

afterEach(() => {
Expand All @@ -141,6 +141,7 @@ describe('#components-builder', () => {
expect(await context.host.readFile(resolve(componentsDistPath, 'hello/hello.component.ts'))).toEqual(newHelloComponentSource);

const latestEntryContent = await context.host.readFile(resolve(componentsDistPath, 'index.ts'));

expect(latestEntryContent).toContain('hello-new');
expect(latestEntryContent).toContain('MyHelloComponent');
expect(latestEntryContent).not.toContain(`'hello'`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@ import { AlibButtonModule } from '@docgeni/alib/button';
import { AlibButtonOtherExampleComponent } from './basic/other.component';
import { AlibButtonBasicExampleComponent } from './basic/basic.component';
import { NgModule } from '@angular/core';

export default {
imports: [CommonModule, AlibButtonModule],
providers: [],
declarations: [AlibButtonOtherExampleComponent]
};

@NgModule({
declarations: [ AlibButtonOtherExampleComponent, AlibButtonBasicExampleComponent ],
entryComponents: [ AlibButtonBasicExampleComponent ],
Expand Down
13 changes: 12 additions & 1 deletion packages/ngdoc/src/ng-source-file.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ describe('#ng-source-file', () => {
expect(defaultExports).toEqual({ providers: [], imports: ['CommonModule'] });
});

it('should get default exports with variables providers', () => {
const sourceText = `
import { CommonModule } from "@angular/common"
const myProviders = [ ClassA ];
export default { providers: myProviders, imports: [CommonModule] } {}
`;
const ngSourceFile = createNgSourceFile('test.ts', sourceText);
const defaultExports = ngSourceFile.getDefaultExports();
expect(defaultExports).toEqual({ providers: 'myProviders', imports: ['CommonModule'] });
});

it('should get undefined default exports', () => {
const sourceText = `
export const book = { providers: [], imports: [] }
Expand Down Expand Up @@ -164,7 +175,7 @@ export default {
}
} else {
if (insertImportDeclarations.get(importItem.moduleSpecifier)) {
insertImportDeclarations.get(importItem.moduleSpecifier).push(importItem.name);
insertImportDeclarations.get(importItem.moduleSpecifier)!.push(importItem.name);
} else {
insertImportDeclarations.set(importItem.moduleSpecifier, [importItem.name]);
}
Expand Down
14 changes: 12 additions & 2 deletions packages/ngdoc/src/ng-source-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ export class NgSourceFile {
return ngModule;
}

public getDefaultExports(): ArgumentInfo {
let exports: ArgumentInfo;
public getDefaultExports<TResult extends ArgumentInfo>(): TResult {
let exports: TResult;
ts.forEachChild(this.sourceFile, node => {
if (ts.isExportAssignment(node) && ts.isObjectLiteralExpression(node.expression)) {
exports = getObjectLiteralExpressionProperties(node.expression);
Expand All @@ -91,6 +91,16 @@ export class NgSourceFile {
return exports;
}

public getDefaultExportNode(): ts.Node {
let result: ts.Node;
ts.forEachChild(this.sourceFile, node => {
if (ts.isExportAssignment(node) && ts.isObjectLiteralExpression(node.expression)) {
result = node;
}
});
return result;
}

public getImportDeclarations(): ts.ImportDeclaration[] {
return findNodes(this.sourceFile, ts.SyntaxKind.ImportDeclaration) as ts.ImportDeclaration[];
}
Expand Down

0 comments on commit 55f2371

Please sign in to comment.