Skip to content

Refactor: No more than 1 namespace declaration per file #35373

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

Merged
merged 3 commits into from
Nov 27, 2019
Merged
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
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"simple-indent": "error",
"debug-assert": "error",
"no-keywords": "error",
"one-namespace-per-file": "error",

// eslint-plugin-import
"import/no-extraneous-dependencies": ["error", { "optionalDependencies": false }],
Expand Down
6 changes: 3 additions & 3 deletions Gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -587,15 +587,15 @@ task("generate-spec").description = "Generates a Markdown version of the Languag
task("clean", series(parallel(cleanTasks), cleanBuilt));
task("clean").description = "Cleans build outputs";

const configureNightly = () => exec(process.execPath, ["scripts/configurePrerelease.js", "dev", "package.json", "src/compiler/core.ts"]);
const configureNightly = () => exec(process.execPath, ["scripts/configurePrerelease.js", "dev", "package.json", "src/compiler/corePublic.ts"]);
task("configure-nightly", series(buildScripts, configureNightly));
task("configure-nightly").description = "Runs scripts/configurePrerelease.ts to prepare a build for nightly publishing";

const configureInsiders = () => exec(process.execPath, ["scripts/configurePrerelease.js", "insiders", "package.json", "src/compiler/core.ts"]);
const configureInsiders = () => exec(process.execPath, ["scripts/configurePrerelease.js", "insiders", "package.json", "src/compiler/corePublic.ts"]);
task("configure-insiders", series(buildScripts, configureInsiders));
task("configure-insiders").description = "Runs scripts/configurePrerelease.ts to prepare a build for insiders publishing";

const configureExperimental = () => exec(process.execPath, ["scripts/configurePrerelease.js", "experimental", "package.json", "src/compiler/core.ts"]);
const configureExperimental = () => exec(process.execPath, ["scripts/configurePrerelease.js", "experimental", "package.json", "src/compiler/corePublic.ts"]);
task("configure-experimental", series(buildScripts, configureExperimental));
task("configure-experimental").description = "Runs scripts/configurePrerelease.ts to prepare a build for experimental publishing";

Expand Down
44 changes: 44 additions & 0 deletions scripts/eslint/rules/one-namespace-per-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/experimental-utils";
import { createRule } from "./utils";

export = createRule({
name: "one-namespace-per-file",
meta: {
docs: {
description: `Limits each file to having at most one top-level namespace declaration`,
category: "Possible Errors",
recommended: "error",
},
messages: {
excessNamespaceError: `All but one of these namespaces should be moved into seperate files.`,
},
schema: [],
type: "problem",
},
defaultOptions: [],

create(context) {
const isNamespaceDeclaration = (node: TSESTree.Node): node is TSESTree.TSModuleDeclaration => node.type === AST_NODE_TYPES.TSModuleDeclaration;

const checkSourceFile = (node: TSESTree.Program) => {
if (context.getFilename().endsWith(".d.ts")) {
return;
}
const members = node.body;
const namespaces = members.filter(isNamespaceDeclaration);
if (namespaces.length <= 1) {
return;
}

namespaces.forEach(n => {
context.report({
messageId: "excessNamespaceError", node: n
});
});
};

return {
Program: checkSourceFile,
};
},
});
163 changes: 1 addition & 162 deletions src/compiler/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1183,165 +1183,4 @@ namespace ts {
return Debug.assertDefined(state.program);
}
}
}

namespace ts {
export type AffectedFileResult<T> = { result: T; affected: SourceFile | Program; } | undefined;

export interface BuilderProgramHost {
/**
* return true if file names are treated with case sensitivity
*/
useCaseSensitiveFileNames(): boolean;
/**
* If provided this would be used this hash instead of actual file shape text for detecting changes
*/
createHash?: (data: string) => string;
/**
* When emit or emitNextAffectedFile are called without writeFile,
* this callback if present would be used to write files
*/
writeFile?: WriteFileCallback;
}

/**
* Builder to manage the program state changes
*/
export interface BuilderProgram {
/*@internal*/
getState(): ReusableBuilderProgramState;
/*@internal*/
backupState(): void;
/*@internal*/
restoreState(): void;
/**
* Returns current program
*/
getProgram(): Program;
/**
* Returns current program that could be undefined if the program was released
*/
/*@internal*/
getProgramOrUndefined(): Program | undefined;
/**
* Releases reference to the program, making all the other operations that need program to fail.
*/
/*@internal*/
releaseProgram(): void;
/**
* Get compiler options of the program
*/
getCompilerOptions(): CompilerOptions;
/**
* Get the source file in the program with file name
*/
getSourceFile(fileName: string): SourceFile | undefined;
/**
* Get a list of files in the program
*/
getSourceFiles(): readonly SourceFile[];
/**
* Get the diagnostics for compiler options
*/
getOptionsDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Get the diagnostics that dont belong to any file
*/
getGlobalDiagnostics(cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Get the diagnostics from config file parsing
*/
getConfigFileParsingDiagnostics(): readonly Diagnostic[];
/**
* Get the syntax diagnostics, for all source files if source file is not supplied
*/
getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Get the declaration diagnostics, for all source files if source file is not supplied
*/
getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[];
/**
* Get all the dependencies of the file
*/
getAllDependencies(sourceFile: SourceFile): readonly string[];

/**
* Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program
* The semantic diagnostics are cached and managed here
* Note that it is assumed that when asked about semantic diagnostics through this API,
* the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics
* In case of SemanticDiagnosticsBuilderProgram if the source file is not provided,
* it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics
*/
getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly Diagnostic[];
/**
* Emits the JavaScript and declaration files.
* When targetSource file is specified, emits the files corresponding to that source file,
* otherwise for the whole program.
* In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified,
* it is assumed that that file is handled from affected file list. If targetSourceFile is not specified,
* it will only emit all the affected files instead of whole program
*
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult;
/**
* Get the current directory of the program
*/
getCurrentDirectory(): string;
}

/**
* The builder that caches the semantic diagnostics for the program and handles the changed files and affected files
*/
export interface SemanticDiagnosticsBuilderProgram extends BuilderProgram {
/**
* Gets the semantic diagnostics from the program for the next affected file and caches it
* Returns undefined if the iteration is complete
*/
getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult<readonly Diagnostic[]>;
}

/**
* The builder that can handle the changes in program and iterate through changed file to emit the files
* The semantic diagnostics are cached per file and managed by clearing for the changed/affected files
*/
export interface EmitAndSemanticDiagnosticsBuilderProgram extends SemanticDiagnosticsBuilderProgram {
/**
* Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete
* The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host
* in that order would be used to write the files
*/
emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult<EmitResult>;
}

/**
* Create the builder to manage semantic diagnostics and cache them
*/
export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): SemanticDiagnosticsBuilderProgram;
export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | SemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
}

/**
* Create the builder that can handle the changes in program and iterate through changed files
* to emit the those files and manage semantic diagnostics cache as well
*/
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): EmitAndSemanticDiagnosticsBuilderProgram;
export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | EmitAndSemanticDiagnosticsBuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]) {
return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences));
}

/**
* Creates a builder thats just abstraction over program and can be used with watch
*/
export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[]): BuilderProgram;
export function createAbstractBuilder(rootNames: readonly string[] | undefined, options: CompilerOptions | undefined, host?: CompilerHost, oldProgram?: BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram;
export function createAbstractBuilder(newProgramOrRootNames: Program | readonly string[] | undefined, hostOrOptions: BuilderProgramHost | CompilerOptions | undefined, oldProgramOrHost?: CompilerHost | BuilderProgram, configFileParsingDiagnosticsOrOldProgram?: readonly Diagnostic[] | BuilderProgram, configFileParsingDiagnostics?: readonly Diagnostic[], projectReferences?: readonly ProjectReference[]): BuilderProgram {
const { newProgram, configFileParsingDiagnostics: newConfigFileParsingDiagnostics } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, configFileParsingDiagnosticsOrOldProgram, configFileParsingDiagnostics, projectReferences);
return createRedirectedBuilderProgram({ program: newProgram, compilerOptions: newProgram.getCompilerOptions() }, newConfigFileParsingDiagnostics);
}
}
}
Loading