Hone logo
Hone
Problems

Vue Transform Plugin Implementation

Vue's build process, particularly with Vite, offers powerful ways to extend its functionality through plugins. This challenge focuses on a common and crucial use case: implementing custom transform plugins. You will create a plugin that modifies the content of Vue SFCs (Single File Components) based on specific rules, demonstrating a deep understanding of Vue's plugin API and build tooling.

Problem Description

You are tasked with creating a custom Vite plugin for Vue 3 that intercepts and transforms the content of .vue files. This plugin should be able to apply different transformations based on metadata associated with the component. Specifically, it needs to:

  1. Identify Vue SFCs: The plugin should only process files with the .vue extension.
  2. Read Component Metadata: Parse the <script> or <script setup> block to extract a custom attribute, let's call it data-transform-rule.
  3. Apply Transformations: Based on the value of data-transform-rule, apply different transformations to the component's template:
    • If data-transform-rule="uppercase": Convert all text content within the <template> block to uppercase.
    • If data-transform-rule="add-prefix": Prepend a specific string (e.g., "[TRANSFORMED] ") to all text content within the <template> block.
  4. Handle No Rule: If the data-transform-rule attribute is not present or has an unrecognized value, the component should remain unchanged.
  5. Return Transformed Code: The plugin should return the modified JavaScript code that Vite will then process.

This challenge is useful for understanding how to dynamically alter component code during the build process, enabling features like internationalization, theming, or custom component logic injection.

Examples

Example 1:

Input .vue file content:

<template>
  <div>Hello World</div>
</template>

<script setup lang="ts">
const message = "This is a test.";
</script>

<script lang="ts" data-transform-rule="uppercase">
export default {
  name: "GreetingComponent"
};
</script>

Output transformed JavaScript code (after Vite processing):

import { defineComponent as _defineComponent } from 'vue'

const __script = {
  name: "GreetingComponent"
}

// ... other Vite internal boilerplate ...

const __file = "/path/to/your/project/GreetingComponent.vue"

const __css = "" // Assuming no style block for simplicity

const __template = {
  render: function render() {
    // Text content converted to uppercase
    return (_openBlock(), _createElementBlock("div", null, "HELLO WORLD", 1 /* TEXT */))
  },
  staticRenderFns: [],
  __file: __file
}

// ... Vite internal component registration ...

// This is the simplified representation of what the plugin outputs before Vite's full processing
// The actual output will be a valid Vue SFC export that Vite understands.
// The key is that the render function's template content is modified.

export default /*#__PURE__*/ _defineComponent({
  name: 'GreetingComponent',
  render: __template.render,
  // ... other properties
})

Explanation: The data-transform-rule="uppercase" attribute in the <script> block triggered the transformation. The text "Hello World" inside the <template> has been converted to "HELLO WORLD".

Example 2:

Input .vue file content:

<template>
  <p>Some informative text.</p>
</template>

<script lang="ts" data-transform-rule="add-prefix">
export default {
  data() {
    return {
      count: 0
    };
  }
};
</script>

Output transformed JavaScript code:

import { defineComponent as _defineComponent } from 'vue'

const __script = {
  data() {
    return {
      count: 0
    };
  }
}

// ... other Vite internal boilerplate ...

const __file = "/path/to/your/project/ExampleComponent.vue"

const __css = "" // Assuming no style block for simplicity

const __template = {
  render: function render() {
    // Text content has a prefix added
    return (_openBlock(), _createElementBlock("p", null, "[TRANSFORMED] Some informative text.", 1 /* TEXT */))
  },
  staticRenderFns: [],
  __file: __file
}

// ... Vite internal component registration ...

export default /*#__PURE__*/ _defineComponent({
  name: 'ExampleComponent',
  render: __script.render,
  // ... other properties
})

Explanation: The data-transform-rule="add-prefix" attribute in the <script> block caused the string "[TRANSFORMED] " to be prepended to the text "Some informative text." within the template.

Example 3: (No transformation)

Input .vue file content:

<template>
  <button @click="handleClick">Click Me</button>
</template>

<script setup>
const handleClick = () => {
  alert('Clicked!');
};
</script>

Output transformed JavaScript code:

import { defineComponent as _defineComponent } from 'vue'

// ... Vite internal boilerplate ...

const __file = "/path/to/your/project/NoTransformComponent.vue"

const __css = ""

const __template = {
  render: function render() {
    // Content remains unchanged
    return (_openBlock(), _createElementBlock("button", { onClick: handleClick }, "Click Me", 1 /* TEXT */))
  },
  staticRenderFns: [],
  __file: __file
}

// ... Vite internal component registration ...

export default /*#__PURE__*/ _defineComponent({
  name: 'NoTransformComponent',
  render: __script.render,
  // ... other properties
})

Explanation: Since no data-transform-rule attribute was found, the component's template content remains exactly as it was.

Constraints

  • The plugin must be implemented as a Vite plugin.
  • The plugin should be written in TypeScript.
  • The plugin should only target .vue files.
  • The parsing of the <script> or <script setup> block should be robust enough to handle variations in attribute order and whitespace.
  • The transformations should only affect text nodes within the template; HTML tags and attributes should be preserved.
  • Consider the performance implications; the parsing and transformation should be efficient.

Notes

  • You will need to use a parser to extract information from the Vue SFC. Libraries like @vue/compiler-sfc are excellent for this.
  • Remember that Vite plugins operate in a pipeline. Your plugin will likely need to hook into the transform hook.
  • The output of the transform hook should be a string representing the modified JavaScript code.
  • Pay attention to how Vue's compiler generates render functions and how to target the text content within those functions. You might need to inspect the AST (Abstract Syntax Tree) generated by @vue/compiler-sfc.
  • The exact structure of the output JavaScript will depend on Vite's internal processing. Focus on ensuring the template content within the generated render function is correctly transformed.
Loading editor...
typescript