first commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
out
|
||||
dist
|
||||
node_modules
|
||||
.vscode-test/
|
||||
*.vsix
|
||||
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
},
|
||||
{
|
||||
"name": "Extension Tests",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/test/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"out": false // set this to true to hide the "out" folder with the compiled JS files
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off",
|
||||
"cSpell.ignoreWords": [
|
||||
"vsix",
|
||||
"grunwald",
|
||||
"renke"
|
||||
]
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
out/test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.yarnrc
|
||||
vsc-extension-quickstart.md
|
||||
**/tsconfig.json
|
||||
**/.eslintrc.json
|
||||
**/*.map
|
||||
**/*.ts
|
||||
@@ -0,0 +1,79 @@
|
||||
# vsc-sort-import README
|
||||
|
||||
Thanks to [Renke Grunwald](https://github.com/renke)'s [nice work](https://github.com/renke/import-sort), it really save me a lot of time so that I can easily make this.
|
||||
|
||||
## Features
|
||||
|
||||
No any more features, no configuration, just sort!
|
||||
|
||||
## Sorting Order
|
||||
|
||||
Firstly, NodeJS built-in module imports have most priority, and then, imports are grouped by the module hierarchy. Imports farther away from the current directory have higher priority.
|
||||
|
||||
Be attention, the absolute imports are always farther than relative imports.
|
||||
|
||||
Before:
|
||||
|
||||
```javascript
|
||||
import foo from "./foo";
|
||||
import _ from "lodash";
|
||||
import foobar from "../foobar";
|
||||
import fs from "fs";
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```javascript
|
||||
import fs from "fs"; // built-in module
|
||||
import _ from "lodash"; // absolute module
|
||||
import foobar from "../foobar"; // farther module
|
||||
import foo from "./foo";
|
||||
```
|
||||
|
||||
modules having same hierarchy compose a group. For each group, the order is:
|
||||
|
||||
1. imports which have only namespace member
|
||||
eg: `import * as fs from 'fs'`
|
||||
2. imports which have only default member
|
||||
eg: `import fs from 'fs'`
|
||||
3. imports which have both default and namespace member
|
||||
eg: `import foo, * as bar from 'foobar'`
|
||||
4. imports which have both default and named member
|
||||
eg: `import foo, { bar } from 'foobar'`
|
||||
5. imports which have only named member
|
||||
eg: `import { foo, bar } from 'foobar'`
|
||||
|
||||
If the above rule didn't determine the order, then use the first member's name (not alias) to compare. For namespace member, the alias is regarded as its name. The comparison rule is:
|
||||
|
||||
1. literal that starts with non-alphanumeric has higher priority
|
||||
2. then follow the alphabetical order(in unicode)
|
||||
|
||||
For imports having no member, sort them by module name with same rule.
|
||||
|
||||
## Extension Settings
|
||||
|
||||
None.
|
||||
|
||||
## Usage
|
||||
|
||||
This didn't be published to extension marketplace since it doesn't suitable for most people. So you have to install manually.
|
||||
|
||||
- download VSIX file from [Github release](https://github.com/mattuylee/vsc-sort-import/releases)
|
||||
- in vscode, `Ctrl+Shift+P` and input "Install from VSIX..."
|
||||
- select downloaded VSIX file, then install
|
||||
- enjoy
|
||||
|
||||
## Build
|
||||
|
||||
clone this repo:
|
||||
> `git clone https://github.com/mattuylee/vsc-sort-import`
|
||||
|
||||
change into repo directory and install dependencies:
|
||||
> `cd vsc-sort-import && npm install`
|
||||
|
||||
package VSIX file:
|
||||
> `npx vsce package`
|
||||
|
||||
## Debug
|
||||
|
||||
After install dependencies, just press `F5` to start debugging. If some error occurs, it will be printed onto the output panel.
|
||||
Generated
+1289
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "vsc-sort-import",
|
||||
"publisher": "mattuy",
|
||||
"displayName": "vsc-sort-import",
|
||||
"description": "A personal opinionated EcmaScript import sorting extension for VSCode.",
|
||||
"version": "0.0.1",
|
||||
"keywords": [
|
||||
"vscode",
|
||||
"javascript",
|
||||
"import",
|
||||
"sort"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mattuylee/vsc-sort-import.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/mattuylee/vsc-sort-import/issues"
|
||||
},
|
||||
"engines": {
|
||||
"vscode": "^1.0.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onCommand:vsc-sort-import.sort"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "vsc-sort-import.sort",
|
||||
"title": "vsc-sort-import: Sort Imports"
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"pretest": "npm run compile",
|
||||
"test": "node ./out/test/runTest.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^7.1.3",
|
||||
"@types/mocha": "^8.0.4",
|
||||
"@types/node": "^12.11.7",
|
||||
"@types/vscode": "^1.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"mocha": "^8.2.1",
|
||||
"typescript": "^4.1.3",
|
||||
"vscode-test": "^1.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"import-sort": "^6.0.0",
|
||||
"import-sort-parser": "^6.0.0",
|
||||
"import-sort-parser-typescript": "^6.0.0",
|
||||
"import-sort-style": "^6.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import * as vscode from "vscode";
|
||||
import sortImports from "import-sort";
|
||||
import sortStyle from "./sort-style";
|
||||
|
||||
// this method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
const channel = vscode.window.createOutputChannel("vsc-sort-import");
|
||||
const disposable = vscode.commands.registerCommand(
|
||||
"vsc-sort-import.sort",
|
||||
() => {
|
||||
try {
|
||||
const document = vscode.window.activeTextEditor?.document;
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
if (!/^(java|type)script(react)?$/.test(document.languageId)) {
|
||||
return;
|
||||
}
|
||||
const res = sortImports(
|
||||
document.getText(),
|
||||
"import-sort-parser-typescript",
|
||||
sortStyle
|
||||
);
|
||||
if (res.changes.length === 0) {
|
||||
// don't modify document if there is no change
|
||||
return;
|
||||
}
|
||||
vscode.window.activeTextEditor.edit((builder) => {
|
||||
const all = new vscode.Range(
|
||||
0,
|
||||
0,
|
||||
Number.MAX_SAFE_INTEGER,
|
||||
Number.MAX_SAFE_INTEGER
|
||||
);
|
||||
builder.replace(all, res.code);
|
||||
});
|
||||
} catch (e) {
|
||||
// if failed, print error and keep silent
|
||||
channel.appendLine(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
context.subscriptions.push(disposable);
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export function deactivate() {}
|
||||
@@ -0,0 +1,105 @@
|
||||
import {
|
||||
IComparatorFunction,
|
||||
ISorterFunction,
|
||||
IStyleAPI,
|
||||
IStyleItem,
|
||||
} from "import-sort-style";
|
||||
import { IImport } from "import-sort-parser";
|
||||
|
||||
export default function (styleApi: IStyleAPI): IStyleItem[] {
|
||||
const {
|
||||
and,
|
||||
always,
|
||||
dotSegmentCount,
|
||||
isAbsoluteModule,
|
||||
isNodeModule,
|
||||
isRelativeModule,
|
||||
name,
|
||||
startsWithAlphanumeric,
|
||||
unicode,
|
||||
} = styleApi;
|
||||
|
||||
// comparator function which place non-alphanumeric first
|
||||
const natural: IComparatorFunction = (a: string, b: string) => {
|
||||
if (a === b) {
|
||||
return 0;
|
||||
}
|
||||
const sa = startsWithAlphanumeric(a),
|
||||
sb = startsWithAlphanumeric(b);
|
||||
if (sa === sb) {
|
||||
return unicode(a, b);
|
||||
} else {
|
||||
return sa ? 1 : -1;
|
||||
}
|
||||
};
|
||||
// sort: use member primarily, if member is not avaliable, use module name
|
||||
const memberOrModule: (c: IComparatorFunction) => ISorterFunction = (
|
||||
comparator: IComparatorFunction
|
||||
) => {
|
||||
return (a: IImport, b: IImport): number => {
|
||||
const aHasDefaultMember = Boolean(a.defaultMember),
|
||||
aHasNamespaceMember = Boolean(a.namespaceMember),
|
||||
aHasNamedMember = Boolean(a.namedMembers[0]?.name),
|
||||
aHasOnlyDefaultMember =
|
||||
aHasDefaultMember && !aHasNamespaceMember && !aHasNamedMember,
|
||||
aHasOnlyNamespaceMember =
|
||||
aHasNamespaceMember && !aHasDefaultMember && !aHasNamedMember;
|
||||
const bHasDefaultMember = Boolean(b.defaultMember),
|
||||
bHasNamespaceMember = Boolean(b.namespaceMember),
|
||||
bHasNamedMember = Boolean(b.namedMembers[0]?.name),
|
||||
bHasOnlyDefaultMember =
|
||||
bHasDefaultMember && !bHasNamespaceMember && !bHasNamedMember,
|
||||
bHasOnlyNamespaceMember =
|
||||
bHasNamespaceMember && !bHasDefaultMember && !bHasNamedMember;
|
||||
if (aHasOnlyNamespaceMember !== bHasOnlyNamespaceMember) {
|
||||
return aHasOnlyNamespaceMember ? -1 : 1;
|
||||
} else if (aHasOnlyDefaultMember !== bHasOnlyDefaultMember) {
|
||||
return aHasOnlyDefaultMember ? -1 : 1;
|
||||
} else if (aHasNamespaceMember !== bHasNamespaceMember) {
|
||||
return aHasNamespaceMember ? -1 : 1;
|
||||
} else if (aHasDefaultMember !== bHasDefaultMember) {
|
||||
return aHasDefaultMember ? -1 : 1;
|
||||
} else if (aHasNamedMember !== bHasNamedMember) {
|
||||
return aHasNamedMember ? -1 : 1;
|
||||
}
|
||||
const first =
|
||||
a.defaultMember ||
|
||||
a.namespaceMember ||
|
||||
a.namedMembers[0]?.name ||
|
||||
a.moduleName;
|
||||
const second =
|
||||
b.defaultMember ||
|
||||
b.namespaceMember ||
|
||||
b.namedMembers[0]?.name ||
|
||||
b.moduleName;
|
||||
return comparator(first, second);
|
||||
};
|
||||
};
|
||||
|
||||
return [
|
||||
// built-in module, has member
|
||||
{
|
||||
match: isNodeModule,
|
||||
sort: memberOrModule(natural),
|
||||
sortNamedMembers: name(natural),
|
||||
},
|
||||
// absolute
|
||||
{
|
||||
match: isAbsoluteModule,
|
||||
sort: memberOrModule(natural),
|
||||
sortNamedMembers: name(natural),
|
||||
},
|
||||
// relative
|
||||
{
|
||||
match: isRelativeModule,
|
||||
sort: [dotSegmentCount, memberOrModule(natural)],
|
||||
sortNamedMembers: name(natural),
|
||||
},
|
||||
// fallback
|
||||
{
|
||||
match: always,
|
||||
sort: [dotSegmentCount, memberOrModule(natural)],
|
||||
sortNamedMembers: name(natural),
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as path from 'path';
|
||||
|
||||
import { runTests } from 'vscode-test';
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// The folder containing the Extension Manifest package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
|
||||
|
||||
// The path to test runner
|
||||
// Passed to --extensionTestsPath
|
||||
const extensionTestsPath = path.resolve(__dirname, './suite/index');
|
||||
|
||||
// Download VS Code, unzip it and run the integration test
|
||||
await runTests({ extensionDevelopmentPath, extensionTestsPath });
|
||||
} catch (err) {
|
||||
console.error('Failed to run tests');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,15 @@
|
||||
import * as assert from 'assert';
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from 'vscode';
|
||||
// import * as myExtension from '../../extension';
|
||||
|
||||
suite('Extension Test Suite', () => {
|
||||
vscode.window.showInformationMessage('Start all tests.');
|
||||
|
||||
test('Sample test', () => {
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(5));
|
||||
assert.strictEqual(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import * as path from 'path';
|
||||
import * as Mocha from 'mocha';
|
||||
import * as glob from 'glob';
|
||||
|
||||
export function run(): Promise<void> {
|
||||
// Create the mocha test
|
||||
const mocha = new Mocha({
|
||||
ui: 'tdd',
|
||||
color: true
|
||||
});
|
||||
|
||||
const testsRoot = path.resolve(__dirname, '..');
|
||||
|
||||
return new Promise((c, e) => {
|
||||
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
|
||||
if (err) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
// Add files to the test suite
|
||||
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
|
||||
|
||||
try {
|
||||
// Run the mocha test
|
||||
mocha.run(failures => {
|
||||
if (failures > 0) {
|
||||
e(new Error(`${failures} tests failed.`));
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
e(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"outDir": "out",
|
||||
"lib": ["es6"],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strict": false
|
||||
},
|
||||
"exclude": ["node_modules", ".vscode-test"]
|
||||
}
|
||||
Reference in New Issue
Block a user