Skip to content

Commit

Permalink
fix: vue preprocessor may replace wrong code (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tanimodori authored Oct 12, 2023
1 parent b3dc7ac commit 13bbbdd
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 22 deletions.
51 changes: 29 additions & 22 deletions src/preprocessors/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,40 @@ import { hasPlugin } from '../utils/get-experimental-parser-plugins';
import { preprocessor } from './preprocessor';

export function vuePreprocessor(code: string, options: PrettierOptions) {
let preprocessedCode = code;
try {
const { parse }: { parse: typeof Parse } = require('@vue/compiler-sfc');
const { descriptor } = parse(code);

if (descriptor.script) {
preprocessedCode = sortScript(
descriptor.script,
preprocessedCode,
options,
);
// 1. Filter valid blocks.
const blocks = [descriptor.script, descriptor.scriptSetup].filter(
(block): block is NonNullable<typeof descriptor.script> =>
Boolean(block?.content),
);
if (!blocks.length) {
return code;
}

if (descriptor.scriptSetup) {
preprocessedCode = sortScript(
descriptor.scriptSetup,
preprocessedCode,
options,
);
// 2. Sort blocks by start offset.
blocks.sort((a, b) => a.loc.start.offset - b.loc.start.offset);

// 3. Replace blocks.
// Using offsets to avoid string replace catching the wrong place and improve efficiency
// see https://github.com/IanVS/prettier-plugin-sort-imports/pull/90
let offset = 0;
let result = '';
for (const block of blocks) {
// https://github.com/vuejs/core/blob/b8fc18c0b23be9a77b05dc41ed452a87a0becf82/packages/compiler-core/src/ast.ts#L74-L80
// The node's range. The `start` is inclusive and `end` is exclusive.
// [start, end)
const { start, end } = block.loc;
const preprocessedBlockCode = sortScript(block, options);
result += code.slice(offset, start.offset) + preprocessedBlockCode;
offset = end.offset;
}

return preprocessedCode;
// 4. Append the rest.
result += code.slice(offset);
return result;
} catch (err) {
if ((err as NodeJS.ErrnoException).code === 'MODULE_NOT_FOUND') {
console.warn(
Expand All @@ -48,14 +60,12 @@ function isTS(lang?: string) {
*
* 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 param0 A script or setupScript block of the SFC
* @param options Prettier options
* @returns Original code with sorted imports in the script provided
* @returns Original code with sorted imports in the block provided
*/
function sortScript(
{ content, lang }: { content: string; lang?: string },
code: string,
options: PrettierOptions,
) {
const { importOrderParserPlugins = [] } = options;
Expand All @@ -82,8 +92,5 @@ function sortScript(
importOrderParserPlugins: newPlugins,
};

return code.replace(
content,
() => `\n${preprocessor(content, adjustedOptions)}\n`,
);
return `\n${preprocessor(content, adjustedOptions)}\n`;
}
103 changes: 103 additions & 0 deletions tests/Vue/__snapshots__/ppsi.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,108 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`duplicates-between-blocks.vue - vue-verify > duplicates-between-blocks.vue 1`] = `
<template>
<code>
import z from 'z';
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import { defineComponent } from 'vue'
function add(a,b) {
return a + b;
}
import z from 'z';
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import { defineComponent } from 'vue'
function add(a,b) {
return a + b;
}
</code>
</template>
<script>
import z from 'z';
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import { defineComponent } from 'vue'
function add(a,b) {
return a + b;
}
</script>
<script setup>
import z from 'z';
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import { defineComponent } from 'vue'
function add(a,b) {
return a + b;
}
</script>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<template>
<code>
import z from 'z'; import threeLevelRelativePath from
"../../../threeLevelRelativePath"; import sameLevelRelativePath from
"./sameLevelRelativePath"; import thirdParty from "third-party"; import
oneLevelRelativePath from "../oneLevelRelativePath"; import otherthing
from "@core/otherthing"; import { defineComponent } from 'vue' function
add(a,b) { return a + b; } import z from 'z'; import
threeLevelRelativePath from "../../../threeLevelRelativePath"; import
sameLevelRelativePath from "./sameLevelRelativePath"; import thirdParty
from "third-party"; import oneLevelRelativePath from
"../oneLevelRelativePath"; import otherthing from "@core/otherthing";
import { defineComponent } from 'vue' function add(a,b) { return a + b;
}
</code>
</template>
<script>
import thirdParty from "third-party";
import { defineComponent } from "vue";
import z from "z";
import otherthing from "@core/otherthing";
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import oneLevelRelativePath from "../oneLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
function add(a, b) {
return a + b;
}
</script>
<script setup>
import thirdParty from "third-party";
import { defineComponent } from "vue";
import z from "z";
import otherthing from "@core/otherthing";
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import oneLevelRelativePath from "../oneLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
function add(a, b) {
return a + b;
}
</script>
`;

exports[`jsx-in-ts.vue - vue-verify > jsx-in-ts.vue 1`] = `
<template>
<router-view />
Expand Down
51 changes: 51 additions & 0 deletions tests/Vue/duplicates-between-blocks.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template>
<code>
import z from 'z';
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import { defineComponent } from 'vue'
function add(a,b) {
return a + b;
}

import z from 'z';
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import { defineComponent } from 'vue'
function add(a,b) {
return a + b;
}
</code>
</template>

<script>
import z from 'z';
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import { defineComponent } from 'vue'
function add(a,b) {
return a + b;
}
</script>

<script setup>
import z from 'z';
import threeLevelRelativePath from "../../../threeLevelRelativePath";
import sameLevelRelativePath from "./sameLevelRelativePath";
import thirdParty from "third-party";
import oneLevelRelativePath from "../oneLevelRelativePath";
import otherthing from "@core/otherthing";
import { defineComponent } from 'vue'
function add(a,b) {
return a + b;
}
</script>

0 comments on commit 13bbbdd

Please sign in to comment.