Skip to content

Commit

Permalink
feat: several improvements and minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilmysliwiec committed Jun 6, 2023
1 parent f958183 commit c4b3559
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 29 deletions.
1 change: 1 addition & 0 deletions actions/build.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export class BuildAction extends AbstractAction {
appName,
'builder',
options,
'tsc',
);

await this.workspaceUtils.deleteOutDirIfEnabled(
Expand Down
4 changes: 2 additions & 2 deletions commands/build.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class BuildCommand extends AbstractCommand {
'--webpack',
'Use webpack for compilation (deprecated option, use --build instead).',
)
.option('--type-check', 'Enable type checking (when SWC is used).', false)
.option('--type-check', 'Enable type checking (when SWC is used).')
.option('--webpackPath [path]', 'Path to webpack configuration.')
.option('--tsc', 'Use tsc for compilation.')
.description('Build Nest application.')
Expand Down Expand Up @@ -53,7 +53,7 @@ export class BuildCommand extends AbstractCommand {
}
options.push({
name: 'builder',
value: command.builder ?? (isWebpackEnabled ? 'webpack' : 'tsc'),
value: command.builder,
});

if (command.typeCheck && command.builder !== 'swc') {
Expand Down
4 changes: 2 additions & 2 deletions commands/start.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class StartCommand extends AbstractCommand {
'Use webpack for compilation (deprecated option, use --build instead).',
)
.option('--webpackPath [path]', 'Path to webpack configuration.')
.option('--type-check', 'Enable type checking (when SWC is used).', false)
.option('--type-check', 'Enable type checking (when SWC is used).')
.option('--tsc', 'Use tsc for compilation.')
.option(
'--sourceRoot [sourceRoot]',
Expand Down Expand Up @@ -91,7 +91,7 @@ export class StartCommand extends AbstractCommand {
}
options.push({
name: 'builder',
value: command.builder ?? (isWebpackEnabled ? 'webpack' : 'tsc'),
value: command.builder,
});

if (command.typeCheck && command.builder !== 'swc') {
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler/interfaces/readonly-visitor.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ export type DeepPluginMeta =
export interface ReadonlyVisitor {
key: string;
visit(program: ts.Program, sf: ts.SourceFile): void;
collect(): Array<[ts.CallExpression, DeepPluginMeta]>;
collect(): Record<string, Array<[ts.CallExpression, DeepPluginMeta]>>;
}
85 changes: 79 additions & 6 deletions lib/compiler/plugins/plugin-metadata-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,74 @@ import {
DeepPluginMeta,
ReadonlyVisitor,
} from '../interfaces/readonly-visitor.interface';
import { FOUND_NO_ISSUES_GENERATING_METADATA } from '../swc/constants';
import { TypeCheckerHost } from '../swc/type-checker-host';
import { TypeScriptBinaryLoader } from '../typescript-loader';
import { PluginMetadataPrinter } from './plugin-metadata-printer';

export interface PluginMetadataGenerateOptions {
/**
* The visitors to use to generate the metadata.
*/
visitors: ReadonlyVisitor[];
/**
* The output directory to write the metadata to.
*/
outputDir: string;
/**
* Whether to watch the project for changes.
*/
watch?: boolean;
/**
* The path to the tsconfig file.
* Relative to the current working directory (process.cwd()).
*/
tsconfigPath?: string;
/**
* The filename to write the metadata to.
*/
filename?: string;
/**
* A reference to an existing ts.Program instance.
*/
tsProgramRef?: ts.Program;
/**
* Whether to print diagnostics to the console.
* @default true
*/
printDiagnostics?: boolean;
}

/**
* Generates plugins metdata by traversing the AST of the project.
* @example
* ```ts
* const generator = new PluginMetadataGenerator();
* generator.generate({
* visitors: [
* new ReadonlyVisitor({ introspectComments: true, pathToSource: __dirname }),
* ],
* outputDir: __dirname,
* watch: true,
* tsconfigPath: 'tsconfig.build.json',
* });
* ```
*/
export class PluginMetadataGenerator {
private readonly pluginMetadataPrinter = new PluginMetadataPrinter();
private readonly typeCheckerHost = new TypeCheckerHost();
private readonly typescriptLoader = new TypeScriptBinaryLoader();

generate(options: PluginMetadataGenerateOptions) {
const { tsconfigPath, visitors, tsProgramRef, outputDir, watch, filename } =
options;
const {
tsconfigPath,
visitors,
tsProgramRef,
outputDir,
watch,
filename,
printDiagnostics = true,
} = options;

if (visitors.length === 0) {
return;
Expand All @@ -36,11 +85,35 @@ export class PluginMetadataGenerator {
);
}

const onTypeCheckOrProgramInit = (program: ts.Program) => {
this.traverseAndPrintMetadata(program, visitors, outputDir, filename);

if (printDiagnostics) {
const tsBinary = this.typescriptLoader.load();
const diagnostics = tsBinary.getPreEmitDiagnostics(program);
if (diagnostics.length > 0) {
const formatDiagnosticsHost: ts.FormatDiagnosticsHost = {
getCanonicalFileName: (path) => path,
getCurrentDirectory: tsBinary.sys.getCurrentDirectory,
getNewLine: () => tsBinary.sys.newLine,
};

console.log();
console.log(
tsBinary.formatDiagnosticsWithColorAndContext(
diagnostics,
formatDiagnosticsHost,
),
);
} else {
console.log(FOUND_NO_ISSUES_GENERATING_METADATA);
}
}
};
this.typeCheckerHost.run(tsconfigPath, {
watch,
onSuccess: (program) => {
this.traverseAndPrintMetadata(program, visitors, outputDir, filename);
},
onTypeCheck: onTypeCheckOrProgramInit,
onProgramInit: onTypeCheckOrProgramInit,
});
}

Expand All @@ -58,7 +131,7 @@ export class PluginMetadataGenerator {

const collectedMetadata: Record<
string,
Array<[ts.CallExpression, DeepPluginMeta]>
Record<string, Array<[ts.CallExpression, DeepPluginMeta]>>
> = {};
visitors.forEach((visitor) => {
collectedMetadata[visitor.key] = visitor.collect();
Expand Down
8 changes: 7 additions & 1 deletion lib/compiler/plugins/plugin-metadata-printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ export interface PluginMetadataPrintOptions {
filename?: string;
}

/**
* Prints the metadata to a file.
*/
export class PluginMetadataPrinter {
print(
metadata: Record<string, Array<[ts.CallExpression, DeepPluginMeta]>>,
metadata: Record<
string,
Record<string, Array<[ts.CallExpression, DeepPluginMeta]>>
>,
options: PluginMetadataPrintOptions,
) {
const objectLiteralExpr = ts.factory.createObjectLiteralExpression(
Expand Down
29 changes: 16 additions & 13 deletions lib/compiler/swc/forked-type-checker.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as ts from 'typescript';
import { Configuration } from '../../configuration';
import { ERROR_PREFIX } from '../../ui';
import { BaseCompiler } from '../base-compiler';
Expand Down Expand Up @@ -34,21 +35,23 @@ class ForkedTypeChecker extends BaseCompiler {
);

try {
const onTypeCheckOrProgramInit = (program: ts.Program) => {
if (readonlyVisitors.length > 0) {
console.log(FOUND_NO_ISSUES_GENERATING_METADATA);

this.pluginMetadataGenerator.generate({
outputDir,
visitors: readonlyVisitors,
tsProgramRef: program,
});
} else {
console.log(FOUND_NO_ISSUES_METADATA_GENERATION_SKIPPED);
}
};
this.typeCheckerHost.run(tsConfigPath, {
watch: extras.watch,
onSuccess: (program) => {
if (readonlyVisitors.length > 0) {
console.log(FOUND_NO_ISSUES_GENERATING_METADATA);

this.pluginMetadataGenerator.generate({
outputDir,
visitors: readonlyVisitors,
tsProgramRef: program,
});
} else {
console.log(FOUND_NO_ISSUES_METADATA_GENERATION_SKIPPED);
}
},
onTypeCheck: onTypeCheckOrProgramInit,
onProgramInit: onTypeCheckOrProgramInit,
});
} catch (err) {
console.log(ERROR_PREFIX, err.message);
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler/swc/swc-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class SwcCompiler extends BaseCompiler {
try {
this.typeCheckerHost.run(tsConfigPath, {
watch: extras.watch,
onSuccess: (program) => {
onTypeCheck: (program) => {
if (!fulfilled) {
fulfilled = true;
resolve();
Expand Down
10 changes: 7 additions & 3 deletions lib/compiler/swc/type-checker-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {

export interface TypeCheckerHostRunOptions {
watch?: boolean;
onSuccess?: (program: ts.Program) => void;
onTypeCheck?: (program: ts.Program) => void;
onProgramInit?: (program: ts.Program) => void;
}

export class TypeCheckerHost {
Expand Down Expand Up @@ -70,7 +71,7 @@ export class TypeCheckerHost {
return;
}
const tsProgram = builderProgram.getProgram().getProgram();
options.onSuccess?.(tsProgram);
options.onTypeCheck?.(tsProgram);
};

const host = this.createWatchCompilerHost(
Expand All @@ -80,6 +81,9 @@ export class TypeCheckerHost {
reportWatchStatusCallback,
);
builderProgram = tsBinary.createWatchProgram(host);
process.nextTick(() => {
options.onProgramInit?.(builderProgram!.getProgram().getProgram());
});
}

private runOnce(
Expand Down Expand Up @@ -123,7 +127,7 @@ export class TypeCheckerHost {
);
process.exit(1);
}
options.onSuccess?.(programRef);
options.onTypeCheck?.(programRef);
}

private createWatchCompilerHost(
Expand Down

0 comments on commit c4b3559

Please sign in to comment.