Skip to content

Commit

Permalink
Use correct parser plugins for TS and Vue (#91)
Browse files Browse the repository at this point in the history
This fixes a few issues with parser plugins.

It fixes #78
by removing the `jsx` parser plugin when the file is named `.ts`
(typescript requires the use of `.tsx` extension for jsx).

It also applies similar logic for .vue files, ensuring that the correct
parser plugins are used, with similar logic to the vue compiler itself.
  • Loading branch information
IanVS committed May 17, 2023
1 parent dcb19a0 commit 2515ea1
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 19 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,14 +370,14 @@ Having some trouble or an issue? You can check [FAQ / Troubleshooting section](.

## Compatibility

| Framework | Supported | Note |
| ---------------------- | ------------- | ------------------------------------------------ |
| JS with ES Modules | ✅ Everything | - |
| NodeJS with ES Modules | ✅ Everything | - |
| React | ✅ Everything | - |
| Angular | ✅ Everything | Supported through `importOrderParserPlugins` API |
| Vue | ✅ Everything | Peer dependency `@vue/compiler-sfc` is required |
| Svelte | ⚠️ Not yet | Contributions are welcome |
| Framework | Supported | Note |
| ---------------------- | ------------- | ---------------------------------------------------------- |
| JS with ES Modules | ✅ Everything | - |
| NodeJS with ES Modules | ✅ Everything | - |
| React | ✅ Everything | - |
| Angular | ✅ Everything | Supported through `importOrderParserPlugins` API |
| Vue | ✅ Everything | SFCs only, peer dependency `@vue/compiler-sfc` is required |
| Svelte | ⚠️ Not yet | Contributions are welcome |

## Contribution

Expand Down
9 changes: 7 additions & 2 deletions src/preprocessors/preprocessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { getExperimentalParserPlugins } from '../utils/get-experimental-parser-p
import { getSortedNodes } from '../utils/get-sorted-nodes';

export function preprocessor(code: string, options: PrettierOptions): string {
const { importOrderParserPlugins, importOrder } = options;
const { importOrderParserPlugins, importOrder, filepath } = options;
let { importOrderTypeScriptVersion } = options;
const isTSSemverValid = semver.valid(importOrderTypeScriptVersion);

Expand All @@ -29,10 +29,15 @@ export function preprocessor(code: string, options: PrettierOptions): string {
: true;

const allOriginalImportNodes: ImportDeclaration[] = [];
let plugins = getExperimentalParserPlugins(importOrderParserPlugins);
// Do not inject jsx plugin for non-jsx ts files
if (filepath.endsWith('.ts')) {
plugins = plugins.filter((p) => p !== 'jsx');
}
const parserOptions: ParserOptions = {
sourceType: 'module',
attachComment: true,
plugins: getExperimentalParserPlugins(importOrderParserPlugins),
plugins,
};

// Disable importOrderCombineTypeAndValueImports if typescript is not set to a version that supports it
Expand Down
68 changes: 60 additions & 8 deletions src/preprocessors/vue.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { parse as Parse } from '@vue/compiler-sfc';

import { ImportOrderParserPlugin } from '../../types';
import { PrettierOptions } from '../types';
import { hasPlugin } from '../utils/get-experimental-parser-plugins';
import { preprocessor } from './preprocessor';

export function vuePreprocessor(code: string, options: PrettierOptions) {
Expand All @@ -10,18 +12,18 @@ export function vuePreprocessor(code: string, options: PrettierOptions) {
const { descriptor } = parse(code);

if (descriptor.script) {
const { content } = descriptor.script;
preprocessedCode = preprocessedCode.replace(
content,
`\n${preprocessor(content, options)}\n`,
preprocessedCode = sortScript(
descriptor.script,
preprocessedCode,
options,
);
}

if (descriptor.scriptSetup) {
const { content } = descriptor.scriptSetup;
preprocessedCode = preprocessedCode.replace(
content,
`\n${preprocessor(content, options)}\n`,
preprocessedCode = sortScript(
descriptor.scriptSetup,
preprocessedCode,
options,
);
}

Expand All @@ -35,3 +37,53 @@ export function vuePreprocessor(code: string, options: PrettierOptions) {
}
}
}

function isTS(lang?: string) {
return lang === 'ts' || lang === 'tsx';
}

/**
* Configures correct babel plugins, sorts imports in a script or setupScript,
* and replaces that script/setupScript within the original code
*
* Much of this was adapted from https://github.com/vuejs/vue/blob/49b6bd4264c25ea41408f066a1835f38bf6fe9f1/packages/compiler-sfc/src/compileScript.ts#L118-L134
*
* @param param0 a script or setupScript
* @param code Source code of the file
* @param options Prettier options
* @returns Original code with sorted imports in the script provided
*/
function sortScript(
{ content, lang }: { content: string; lang?: string },
code: string,
options: PrettierOptions,
) {
const { importOrderParserPlugins = [] } = options;
let pluginClone = [...importOrderParserPlugins];
const newPlugins: ImportOrderParserPlugin[] = [];

if (!isTS(lang) || lang === 'tsx') {
newPlugins.push('jsx');
} else {
// Remove jsx if typescript and not tsx
pluginClone = pluginClone.filter((p) => p !== 'jsx');
}

newPlugins.push(...pluginClone);

if (isTS(lang)) {
if (!hasPlugin(newPlugins, 'typescript')) {
newPlugins.push('typescript');
}
}

const adjustedOptions = {
...options,
importOrderParserPlugins: newPlugins,
};

return code.replace(
content,
`\n${preprocessor(content, adjustedOptions)}\n`,
);
}
32 changes: 32 additions & 0 deletions src/utils/get-experimental-parser-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,35 @@ export const getExperimentalParserPlugins = (
return plugin;
});
};

/**
* Checks whether a specified plugin is included in importOrderParserPlugins.
* More fancy than just a `.includes()` because importOrderParserPlugins can contain plugins with configuration
*
* @param importOrderParserPlugins array of experimental babel parser plugins
* @returns true if the plugin is in the list
*/
export const hasPlugin = (
importOrderParserPlugins: string[],
pluginName: string,
): boolean => {
for (const pluginNameOrJson of importOrderParserPlugins) {
const isParserPluginWithOptions = pluginNameOrJson.startsWith('[');
let plugin;

if (isParserPluginWithOptions) {
try {
plugin = JSON.parse(pluginNameOrJson)[0];
} catch (e) {
throw Error(
'Invalid JSON in importOrderParserPlugins: ' +
pluginNameOrJson,
);
}
} else {
plugin = pluginNameOrJson as ParserPlugin;
}
if (plugin === pluginName) return true;
}
return false;
};
52 changes: 52 additions & 0 deletions tests/Typescript/__snapshots__/ppsi.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,55 @@ export class AppComponent {
}
`;

exports[`jsx.tsx - typescript-verify > jsx.tsx 1`] = `
import z from 'z';
import { isEmpty } from "lodash-es";
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import abc from "@core/abc";
import twoLevelRelativePath from "../../twoLevelRelativePath";
import component from "@ui/hello";
import fourLevelRelativePath from "../../../../fourLevelRelativePath";
import something from "@server/something";
import xyz from "@ui/xyz";
export const Component = () => {
return <div>Component</div>
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import { isEmpty } from "lodash-es";
import thirdParty from "third-party";
import z from "z";
import abc from "@core/abc";
import otherthing from "@core/otherthing";
import something from "@server/something";
import component from "@ui/hello";
import xyz from "@ui/xyz";
import fourLevelRelativePath from "../../../../fourLevelRelativePath";
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import twoLevelRelativePath from "../../twoLevelRelativePath";
import oneLevelRelativePath from "../oneLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
export const Component = () => {
return <div>Component</div>;
};
`;

exports[`old-style-assertion.ts - typescript-verify > old-style-assertion.ts 1`] = `
import { b } from "b";
import { a } from "a";
<A>a();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import { a } from "a";
import { b } from "b";
<A>a();
`;
17 changes: 17 additions & 0 deletions tests/Typescript/jsx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import z from 'z';
import { isEmpty } from "lodash-es";
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import abc from "@core/abc";
import twoLevelRelativePath from "../../twoLevelRelativePath";
import component from "@ui/hello";
import fourLevelRelativePath from "../../../../fourLevelRelativePath";
import something from "@server/something";
import xyz from "@ui/xyz";

export const Component = () => {
return <div>Component</div>
}
4 changes: 4 additions & 0 deletions tests/Typescript/old-style-assertion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { b } from "b";
import { a } from "a";

<A>a();
2 changes: 1 addition & 1 deletion tests/Typescript/ppsi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import {run_spec} from '../../test-setup/run_spec';

run_spec(__dirname, ["typescript"], {
importOrder: ['^@core/(.*)$', '^@server/(.*)', '^@ui/(.*)$', '^[./]'],
importOrderParserPlugins : ['typescript', 'decorators-legacy', 'classProperties'],
importOrderParserPlugins : ['typescript', 'jsx', 'decorators-legacy', 'classProperties'],
});
35 changes: 35 additions & 0 deletions tests/Vue/__snapshots__/ppsi.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`jsx-in-ts.vue - vue-verify > jsx-in-ts.vue 1`] = `
<template>
<router-view />
</template>
<script lang="ts">
// This will crash if we try to use the \`jsx\` parser plugin
<A>a();
</script>
<style lang="less">
#app {
height: 100%;
background-color: inherit;
}
</style>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<template>
<router-view />
</template>
<script lang="ts">
// This will crash if we try to use the \`jsx\` parser plugin
<A>a();
</script>
<style lang="less">
#app {
height: 100%;
background-color: inherit;
}
</style>
`;
exports[`no-script.vue - vue-verify > no-script.vue 1`] = `
<template>
<router-view />
Expand Down
15 changes: 15 additions & 0 deletions tests/Vue/jsx-in-ts.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<router-view />
</template>

<script lang="ts">
// This will crash if we try to use the `jsx` parser plugin
<A>a();
</script>

<style lang="less">
#app {
height: 100%;
background-color: inherit;
}
</style>

0 comments on commit 2515ea1

Please sign in to comment.