Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: simonhaenisch/prettier-plugin-organize-imports
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.3.4
Choose a base ref
...
head repository: simonhaenisch/prettier-plugin-organize-imports
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.0.0
Choose a head ref
  • 10 commits
  • 8 files changed
  • 2 contributors

Commits on Sep 22, 2021

  1. docs: update changelog

    simonhaenisch committed Sep 22, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    be5f4fc View commit details
  2. Copy the full SHA
    f19e9d6 View commit details

Commits on Oct 15, 2021

  1. Update readme.md

    simonhaenisch authored Oct 15, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    d4adbd5 View commit details

Commits on Oct 18, 2021

  1. Copy the full SHA
    e68236a View commit details

Commits on Nov 30, 2021

  1. docs: update readme

    Closes #44.
    simonhaenisch authored Nov 30, 2021
    Copy the full SHA
    a087c65 View commit details

Commits on May 26, 2022

  1. docs: update readme to reflect lack of prettier yarn 2 support for au…

    …tomatic plugin discovery (#52)
    qbolt authored May 26, 2022
    Copy the full SHA
    5ba4618 View commit details

Commits on Jun 16, 2022

  1. fix!: switch from manual vue SFC parsing to @volar/vue-typescript (#56)

    BREAKING CHANGE: only if you're using this with Vue, you'll need to switch to a different package `@volar/vue-typescript` to support this.
    simonhaenisch committed Jun 16, 2022
    Copy the full SHA
    cafeee7 View commit details
  2. Copy the full SHA
    d772197 View commit details
  3. Copy the full SHA
    b2d533e View commit details
  4. 3.0.0

    simonhaenisch committed Jun 16, 2022
    Copy the full SHA
    38c6303 View commit details
Showing with 77 additions and 94 deletions.
  1. +1 −1 .github/workflows/test.yml
  2. +1 −31 index.js
  3. +0 −35 lib/get-vue-sfc-script.js
  4. +27 −3 lib/organize.js
  5. +4 −0 lib/service-host.js
  6. +7 −8 package.json
  7. +19 −14 readme.md
  8. +18 −2 test.js
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ jobs:
strategy:
matrix:
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
node: [10, 12, 14]
node: [12, 14, 16, 18]

runs-on: ${{ matrix.os }}

32 changes: 1 addition & 31 deletions index.js
Original file line number Diff line number Diff line change
@@ -18,37 +18,7 @@ const organizeImports = (code, options) => {
}

try {
const filePath = options.filepath || 'file.ts';

/**
* @todo remove this once Prettier has fixed the child-parser preprocessing bug
* @see https://github.com/prettier/prettier/issues/11206
*/
if (options.parentParser === 'vue') {
return code; // we already did the preprocessing for the `vue` parent parser
} else if (options.parser === 'vue') {
const { getVueSFCScript } = require('./lib/get-vue-sfc-script');

const script = getVueSFCScript(code, filePath);

if (!script) {
return code;
}

const organized = organize(script.content, filePath + '.ts');

return applyTextChanges(code, [
{
newText: organized,
span: {
start: script.start,
length: script.end - script.start,
},
},
]);
}

return organize(code, filePath);
return organize(code, options);
} catch (error) {
if (process.env.DEBUG) {
console.error(error);
35 changes: 0 additions & 35 deletions lib/get-vue-sfc-script.js

This file was deleted.

30 changes: 27 additions & 3 deletions lib/organize.js
Original file line number Diff line number Diff line change
@@ -7,10 +7,34 @@ const { ServiceHost } = require('./service-host');
* Organize the given code.
*
* @param {string} code
* @param {string} fileName
* @param {import('prettier').ParserOptions} options
*/
module.exports.organize = (code, fileName) => {
const languageService = ts.createLanguageService(new ServiceHost(fileName, code));
module.exports.organize = (code, options) => {
const { filepath = 'file.ts' } = options;

const fileName = options.parser === 'vue' ? filepath + '.ts' : filepath;

/**
* @type {ts.LanguageService}
*/
let languageService;

/**
* @todo remove this once Prettier has fixed the child-parser preprocessing bug
* @see https://github.com/prettier/prettier/issues/11206
*/
if (options.parentParser === 'vue') {
return code; // we already did the preprocessing for the `vue` parent parser
} else if (options.parser === 'vue') {
const tsServerLib = require('typescript/lib/tsserverlibrary');
const vueTs = require('@volar/vue-typescript');

const lsContext = vueTs.createLanguageServiceContext(tsServerLib, new ServiceHost(fileName, code));

languageService = lsContext.typescriptLanguageService;
} else {
languageService = ts.createLanguageService(new ServiceHost(fileName, code));
}

const fileChanges = languageService.organizeImports({ type: 'file', fileName }, {}, {})[0];

4 changes: 4 additions & 0 deletions lib/service-host.js
Original file line number Diff line number Diff line change
@@ -42,6 +42,10 @@ class ServiceHost {
getScriptSnapshot() {
return ts.ScriptSnapshot.fromString(this.content);
}

getVueCompilationSettings() {
return {};
}
}

module.exports = { ServiceHost };
15 changes: 7 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "prettier-plugin-organize-imports",
"version": "2.3.4",
"version": "3.0.0",
"description": "Make prettier organize your imports using the TypeScript language service API.",
"keywords": [
"prettier",
@@ -27,13 +27,11 @@
"typescript": ">=2.9"
},
"devDependencies": {
"@types/node": "16.3.2",
"@vue/compiler-sfc": "3.1.5",
"@vue/component-compiler-utils": "3.2.2",
"@types/node": "16.9.6",
"@volar/vue-typescript": "0.37.9",
"ava": "3.15.0",
"prettier": "2.3.2",
"typescript": "4.3.5",
"vue-template-compiler": "2.6.14"
"prettier": "2.4.1",
"typescript": "4.4.3"
},
"prettier": {
"printWidth": 120,
@@ -43,7 +41,8 @@
"overrides": [
{
"files": [
"package.json"
"package.json",
"*.md"
],
"options": {
"useTabs": false
33 changes: 19 additions & 14 deletions readme.md
Original file line number Diff line number Diff line change
@@ -8,11 +8,16 @@ A plugin that makes Prettier organize your imports (i. e. sorts, combines and re

**Features**

- 👌 Dependency-free (just peer-dependencies you probably already have).
- 💪 Supports `.js`, `.jsx`, `.ts`, `.tsx` and `.vue` files.
- 🚀 Zero config.
- 🤓 No more weird diffs or annoying merge conflicts in PRs caused by import statements.
- 🤯 If your editor supports auto-imports, you'll stop thinking about your imports so much that you won't even care about their order anymore.

**Caveat**

This plugin inherits, extends and overrides the built-in Prettier parsers for `babel`, `babel-ts`, `typescript` and `vue`, i. e., it's incompatible with other plugins that do the same... so only the last loaded plugin that exports one of those parsers will function.

## Installation

```sh
@@ -25,31 +30,35 @@ _`prettier` and `typescript` are peer dependencies, so make sure you have those

The plugin will be loaded by Prettier automatically. No configuration needed.

Note: Yarn 2 [does not yet support](https://github.com/prettier/prettier/issues/8474) automatic plugin discovery. [See the prettier documentation](https://prettier.io/docs/en/plugins.html) on plugins for alternatives to manually load them when using Yarn 2.

Files containing the substring `// organize-imports-ignore` or `// tslint:disable:ordered-imports` are skipped.

### Vue.js

**TL;DR:** Make sure that you have either `@vue/compiler-sfc` (for Vue 3.x) or both `@vue/component-compiler-utils` and `vue-template-compiler` (for Vue 2.x) installed.
**TL;DR:** Make sure that you have `@volar/vue-typescript` installed.

```
npm i --save-dev @vue/compiler-sfc
npm i --save-dev @volar/vue-typescript
```

or

```
npm i --save-dev @vue/component-compiler-utils vue-template-compiler
```

The `vue` parser of Prettier splits the SFC (single file component) into its blocks and then runs each block through their respective "child" parser, i.&nbsp;e. `typescript` for a `<script lang="ts">` block. This plugin would then preprocess the script content to organize the imports. However Prettier has a [bug](https://github.com/prettier/prettier/issues/11206) with the `preprocess` hook when called in a child parser, which causes broken code around comments and other things. Therefore some work was necessary to do the import organizing on the parent parser level already; this requires some manual parsing using the aforementioned packages. Hopefully Prettier will fix this bug soon so that this whole readme section and the extra code can be deleted and _it just works™️_ again 🤓
The `vue` parser of Prettier splits the SFC (single file component) into its blocks and then runs each block through their respective "child" parser, i.&nbsp;e. `typescript` for a `<script lang="ts">` block. This plugin would then preprocess the script content to organize the imports. However Prettier has a [bug](https://github.com/prettier/prettier/issues/11206) with the `preprocess` hook when called in a child parser, which causes broken code around comments and other things. Therefore some work was necessary to do the import organizing on the parent parser level already; this requires using a different language service that the above package provides. Hopefully Prettier will fix this bug soon so that this whole readme section and the extra code can be deleted and _it just works™️_ again 🤓

### Debug Logs

If something doesn't work, you can try to prefix your command with `DEBUG=true` which will enable this plugin to print some logs.

## Rationale/Disclaimer

This plugin acts outside of [Prettier's scope](https://prettier.io/docs/en/rationale#what-prettier-is-_not_-concerned-about) because _"Prettier only prints code. It does not transform it."_, and technically sorting is a code transformation because it changes the AST (this plugin even removes code, i. e. unused imports). In my opinion however, the import statements are not _really_ part of the code, they are merely directives that instruct the module system where to find the code (only true as long as your imports are side-effects free regarding the global scope, i. e. import order doesn't matter), comparable with `using` directives in C# or `#include` preprocessing directives in C. Therefore the practical benefits outweigh sticking with the philosophy in this case.

## Changelog

Version `2.3.2` fixes an issue with Vue 2 files.
Version `3.0.0` switches to a different package for Vue support, which fixes some more issues, e. g. support for setup scripts. No breaking changes otherwise.

Version `2.3.4` fixes an issue with Vue 2 files.

Version `2.3.3` fixes a bug where default imports were removed erroneously.

Version `2.3.1` adds debug logs and fixes Vue.js support.

@@ -59,10 +68,6 @@ Version `2.1.0` adds support for Vue.js (`.vue` files).

Version `2.0.0` adds support for the parsers `babel` (i. e. JavaScript) and `babel-ts` which are only available since Prettier v2 (and thus the peer dependency has received a major bump).

## Rationale/Disclaimer

This plugin acts outside of [Prettier's scope](https://prettier.io/docs/en/rationale#what-prettier-is-_not_-concerned-about) because _"Prettier only prints code. It does not transform it."_, and technically sorting is a code transformation because it changes the AST (this plugin even removes code, i. e. unused imports). In my opinion however, the import statements are not _really_ part of the code, they are merely directives that instruct the module system where to find the code (only true as long as your imports are side-effects free regarding the global scope, i. e. import order doesn't matter), comparable with `using` directives in C# or `#include` preprocessing directives in C. Therefore the practical benefits outweigh sticking with the philosophy in this case.

## License

[MIT](/license).
20 changes: 18 additions & 2 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const test = require('ava');
const test = require('ava').default;
const prettier = require('prettier');

/**
@@ -12,7 +12,7 @@ const prettify = (code, options) => prettier.format(code, { plugins: ['.'], file
*/
const getMacro = (parser) => {
/**
* @param {test.Assertions} t
* @param {import('ava').Assertions} t
* @param {string} input
* @param {string} expected
* @param {object} [options]
@@ -33,6 +33,9 @@ const getMacro = (parser) => {
return macro;
};

/**
* @type {import('ava').OneOrMoreMacros<[string, string] | [string, string, { options?: prettier.Options, transformer?: (res: string) => string }], unknown>}
*/
const macros = [getMacro('typescript'), getMacro('babel'), getMacro('babel-ts')];

test(
@@ -113,6 +116,19 @@ test('works with TypeScript code inside Vue files', (t) => {
t.is(formattedCode.split('\n')[1], `import { compile, defineComponent } from "vue";`);
});

test('works with Vue setup scripts', (t) => {
const code = `
<script setup lang="ts">
import {defineComponent,compile} from 'vue';
export default defineComponent({});
</script>
`;

const formattedCode = prettify(code, { filepath: 'file.vue' });

t.is(formattedCode.split('\n')[1], `import { defineComponent } from "vue";`);
});

test('preserves new lines and comments in Vue files', (t) => {
const code = `<script lang="ts">
import { defineComponent, ref } from "vue";