Skip to content
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
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,24 @@ Then add this plugin in `tsconfig.json`.
### paths (required)

Specify directory in relative path to the project's root (`tsconfig.json`'s dir). All `.ts` or `.js` files in the directories can be Namespace Imported with auto-completion.

example:

```json
"options": {
"paths": ["src/logics"]
}
```

### ignoreNamedExport

If true, named export from files in `paths` won't be shown in auto-completion.

example:

```json
"options": {
"paths": ["src/logics"],
"ignoreNamedExport": true
}
```
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ function init() {
return original;
}

original.entries = [...original.entries, ...namespaceImportPlugin.getCompletionEntries(info)];
const originalEntries = namespaceImportPlugin.filterNamedImportEntries(original.entries, info);
const namespaceImportEntries = namespaceImportPlugin.getCompletionEntries(info);
original.entries = [...originalEntries, ...namespaceImportEntries];
return original;
};

Expand Down
53 changes: 37 additions & 16 deletions src/lib/import.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
import ts, { CodeFixAction, InferencePriority, ScriptElementKind } from 'typescript/lib/tsserverlibrary';
import ts, { CodeFixAction, ScriptElementKind } from 'typescript/lib/tsserverlibrary';
import * as path from 'path';

type PluginOptions = {
export type PluginOptions = {
paths: readonly string[];
ignoreNamedExport?: boolean;
};

export function getCompletionEntries(info: ts.server.PluginCreateInfo): ts.CompletionEntry[] {
const filePaths = getPathsToImport(info.config.options, info.project);
const modulePaths = getModulePathsToImport(info.config.options, info.project);

return filePaths.map((filePath) => {
const name = getFileNameWithoutExt(filePath);
return modulePaths.map((modulePath) => {
const name = getFileNameWithoutExt(modulePath);
return {
name: name,
kind: ts.ScriptElementKind.alias,
source: filePath,
source: modulePath,
sortText: name,
hasAction: true,
isImportStatementCompletion: true,
data: {
exportName: name,
modulePath: filePath,
modulePath: modulePath,
},
};
});
}

export function filterNamedImportEntries(
entries: ts.CompletionEntry[],
info: ts.server.PluginCreateInfo,
): ts.CompletionEntry[] {
const options: PluginOptions = info.config.options;
if (!options.ignoreNamedExport) {
return entries;
}

const currentDir = info.project.getCurrentDirectory();
const dirPaths = options.paths.map((dirPath) => path.resolve(currentDir, dirPath));
return entries.filter((entry) => {
return !dirPaths.some((dirPath) => entry.data?.exportName && entry.data.fileName?.startsWith(dirPath));
});
}

export function getCompletionEntryDetails(
name: string,
selfPath: string,
Expand Down Expand Up @@ -52,16 +69,16 @@ export function getCodeFixActionByName(
return null;
}

const filePaths = getPathsToImport(info.config.options, info.project);
const modulePath = filePaths.find((filePath) => getFileNameWithoutExt(filePath) === name);
const modulePaths = getModulePathsToImport(info.config.options, info.project);
const modulePath = modulePaths.find((filePath) => getFileNameWithoutExt(filePath) === name);
if (modulePath) {
return getCodeFixActionFromPath(name, selfPath, modulePath, info.project);
} else {
return null;
}
}

function getPathsToImport(options: PluginOptions, project: ts.server.Project): string[] {
function getModulePathsToImport(options: PluginOptions, project: ts.server.Project): string[] {
const currentDir = project.getCurrentDirectory();

return options.paths.flatMap((dirPath) => {
Expand All @@ -79,23 +96,27 @@ function getFilePathWithoutExt(filePath: string): string {
return filePath.slice(0, filePath.length - ext.length);
}

function transformModulePath(selfPath: string, filePath: string, project: ts.server.Project) {
function getModuleSpceifier(selfPath: string, modulePath: string, project: ts.server.Project) {
const compilerOptions = project.getCompilerOptions();

let specifier: string;
if (compilerOptions.baseUrl) {
return path.relative(compilerOptions.baseUrl, filePath);
specifier = path.relative(compilerOptions.baseUrl, modulePath);
} else {
return './' + path.relative(path.dirname(selfPath), filePath);
specifier = './' + path.relative(path.dirname(selfPath), modulePath);
}

return getFilePathWithoutExt(specifier);
}

export function getCodeFixActionFromPath(
function getCodeFixActionFromPath(
name: string,
selfPath: string,
modulePath: string,
project: ts.server.Project,
): CodeFixAction {
const importPath = transformModulePath(selfPath, modulePath, project);
const text = `import * as ${name} from "${getFilePathWithoutExt(importPath)}";\n`;
const moduleSpecifier = getModuleSpceifier(selfPath, modulePath, project);
const text = `import * as ${name} from "${moduleSpecifier}";\n`;
return {
fixName: 'namespace-import',
description: text,
Expand Down