Skip to content

Commit

Permalink
fix!: switch from manual vue SFC parsing to @volar/vue-typescript (#56)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
simonhaenisch committed Jun 16, 2022
1 parent 5ba4618 commit cafeee7
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 85 deletions.
32 changes: 1 addition & 31 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
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
Expand Up @@ -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];

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

getVueCompilationSettings() {
return {};
}
}

module.exports = { ServiceHost };
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@
},
"devDependencies": {
"@types/node": "16.9.6",
"@vue/compiler-sfc": "3.2.13",
"@vue/component-compiler-utils": "3.2.2",
"@volar/vue-typescript": "0.37.9",
"ava": "3.15.0",
"prettier": "2.4.1",
"typescript": "4.4.3",
"vue-template-compiler": "2.6.14"
"typescript": "4.4.3"
},
"prettier": {
"printWidth": 120,
Expand All @@ -43,7 +41,8 @@
"overrides": [
{
"files": [
"package.json"
"package.json",
"*.md"
],
"options": {
"useTabs": false
Expand Down
12 changes: 3 additions & 9 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,13 @@ Files containing the substring `// organize-imports-ignore` or `// tslint:disabl

### 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

Expand Down
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');

/**
Expand All @@ -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]
Expand All @@ -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(
Expand Down Expand Up @@ -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";
Expand Down

0 comments on commit cafeee7

Please sign in to comment.