[go: up one dir, main page]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nuxt,schema): add directives folder #29203

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

userquin
Copy link
Member
@userquin userquin commented Sep 27, 2024

🔗 Linked issue

resolves #28954

📚 Description

This PR includes:

  • simplified version of directives folder described here: allow auto import vue directives #28954 (comment)
  • update logic to handle changes in latest unimport (v3.13.0)
  • use imports module to auto import directives and generating the corresponding dts entries (add the directives to the Vue module augmentation (Volar will show the directives in the template) )
  • add a new plugin to replace all resolveDirectives in the imports module: check Context below
  • use the same types in Vue preset used in unimport: missing DirectiveBinding (missing here and unimport), ExtractDefaultPropTypes and WritableComputedRef (MaybeRef and MaybeRefOrGetter missing in unimport)
  • draft version to include directives folder in docs: added docs/2.guide/2.directory-structure/1.directives.md/, not yet tested on my local with nuxt.com repository ✔️ tested
  • WIP: add some test

Important

Context
Since we only need the directives and the dts, the imports module will do it for us (free, no changes, only the ones to update the new options in unimport). We can reuse the unimport configuration to generate the imports and the dts to replace resolveDirectives without having to move that logic to the components loader as suggested by @danielroe in the issue: this new plugin will filter imports and presets imports with meta.vueDirective set to true.

Warning

✔️ This PR in draft since we need this PR merged and released: unjs/unimport#380 (unimport updated to 3.13.1)

FUTURE: we need to:

Nuxt Devtools showing the directives

imagen

Testing

If you want to test this PR you will need to:

  • create directives folder in the playground
  • add a new directive with object notation using named export (Vue will fail when enabling ssr when getting getSSRProps )
  • add the directive somewhere in the app.vue
  • patch playground/node_modules/nuxt/node_modules/unimport/dist/shared/unimport.0cf85d87.mjs manually: chekc screenshot below unimport updated to 3.13.1 with the fix
unimport patch

imagen

Summary by CodeRabbit

  • New Features

    • Introduced documentation for the directives/ directory, detailing usage and requirements for Vue directives.
    • Added a DirectivesPlugin to manage Vue directives within a Nuxt application.
    • Enhanced the vueTypesPreset with additional types for improved type safety.
  • Bug Fixes

    • Updated default directories for auto imports to include ./directives.
  • Documentation

    • Added examples and guidelines for implementing Vue directives in the new documentation file.

Copy link
stackblitz bot commented Sep 27, 2024

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@userquin userquin marked this pull request as ready for review September 28, 2024 09:51
}
}
imports?.forEach(visit)
presets?.forEach((preset) => {
Copy link
Member Author
@userquin userquin Sep 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review this, I guess not necessary (getting some errors starting nuxt dev server, re-check this without ?)

@userquin
Copy link
Member Author

We also need to change the import to use #imports, I need to figure out how to do that.

@userquin
Copy link
Member Author

We need a new release on unimport to collect info in directives context and update imports context: unjs/unimport#381

Copy link
Member
@danielroe danielroe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry it took so long for the review! 🙏

I think what we should do is not register a directives/ folder but instead add a kit utility that registers custom directives for auto-importing.

If there is enough demand later on we might reconsider the folder but for now I think the main benefit is for modules to register directives (like vuetify).

For this I think we can either pass an additional option to the imports module, if required - maybe an array of directives? (Or if it's possible to configure unimport without this, we can go simpler.)

Do you have time to implement this?

Copy link
coderabbitai bot commented Nov 3, 2024

Walkthrough

The changes introduce a new documentation file for the directives/ directory in a Nuxt application, detailing the usage and requirements for Vue directives. Additionally, a DirectivesPlugin is implemented to handle the auto-importing of these directives, enhancing the management of Vue directives within the Nuxt framework. The configuration for imports is updated to include the directives directory by default, reflecting these changes in both the schema and the module's setup.

Changes

File Path Change Summary
docs/2.guide/2.directory-structure/1.directives.md New documentation for the directives/ directory, outlining usage, requirements, and examples.
packages/nuxt/src/imports/directives.ts Introduced DirectivesPlugin for handling Vue directives, including import filtering and lifecycle methods.
packages/nuxt/src/imports/module.ts Expanded setup function to manage directive directories and added logic for dynamic updates.
packages/nuxt/src/imports/presets.ts Updated vueTypesPreset to include new imports for enhanced type safety.
packages/schema/src/config/adhoc.ts Updated comment in imports configuration to include ~/directives as a default directory.
packages/schema/src/types/imports.ts Modified ImportsOptions interface to include ./directives in the default dirs array.

Assessment against linked issues

Objective Addressed Explanation
Allow auto import of Vue directives (#28954)

Possibly related PRs

Suggested labels

4.x

Suggested reviewers

  • manniL

Poem

In the garden of code, where directives bloom,
A new guide has sprouted, dispelling the gloom.
With plugins and presets, our imports align,
Auto-importing magic, oh how it will shine!
Hopping through changes, with joy we will sing,
For directives in Nuxt, a wonderful thing! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (12)
packages/schema/src/types/imports.ts (1)

14-14: LGTM! Consider enhancing the documentation link.

The addition of './directives' to the default directories aligns well with the new auto-import feature for Vue directives. The change is non-breaking and maintains backward compatibility.

Consider updating the documentation link in the JSDoc to also reference the new directives documentation, as it currently only points to composables.

- * @see https://nuxt.com/docs/guide/directory-structure/composables#how-files-are-scanned
+ * @see https://nuxt.com/docs/guide/directory-structure/composables#how-files-are-scanned
+ * @see https://nuxt.com/docs/guide/directory-structure/directives
docs/2.guide/2.directory-structure/1.directives.md (4)

1-8: Consider enhancing the introduction with more context.

The introduction could be more helpful for newcomers by:

  • Explaining what Vue directives are and their purpose
  • Mentioning the naming convention requirements upfront
  • Including a link to Vue's official documentation on directives
 Nuxt automatically imports any directive in this directory (along with directives that are registered by any modules you may be using).
+
+Vue directives are special attributes that allow you to apply custom behavior to DOM elements. They are prefixed with `v-` and can be used to handle various DOM manipulations, event handling, and more.
+
+:read-more{to="https://vuejs.org/guide/reusability/custom-directives.html" title="Vue Custom Directives"}

10-14: Enhance the directory structure example with more details.

Consider adding brief comments to indicate what each directive does, and use consistent file extensions.

 ```bash [Directory Structure]
 | directives/
---| awesome.ts
---| focus.ts
+--| awesome.ts        # Example directive that adds awesome animations
+--| focus.ts         # Directive to auto-focus form inputs

---

`27-27`: **Enhance the SSR warning with more details.**

The SSR warning could be more helpful by including the actual error message and a clear example of the correct implementation.

```diff
-If you have SSR enabled, you must use object notation in your directives. Otherwise, Vue will throw an error about missing `getSSRProps`.
+If you have SSR enabled, you must use object notation in your directives and implement the `getSSRProps` function. Otherwise, Vue will throw an error like:
+
+```bash
+[Vue warn]: Directive is missing getSSRProps in SSR
+```
+
+Here's how to properly implement an SSR-compatible directive:
+
+```ts
+export const MyDirective = {
+  mounted(el: HTMLElement, binding: DirectiveBinding) {
+    // Client-side logic
+  },
+  getSSRProps(binding, vnode) {
+    // Return SSR-specific props
+    return {}
+  }
+}
+```

45-45: Fix the formatting of the example link.

Add a newline before the link component to maintain consistent formatting.

+
 :link-example{to="/docs/examples/features/auto-imports"}
packages/schema/src/config/adhoc.ts (1)

34-34: Documentation update looks good, minor enhancement suggested.

The updated comment correctly reflects the addition of ~/directives to the default directories. However, consider making the documentation more explicit about the behavior of these default directories.

Consider this minor enhancement to improve clarity:

-     * Note that this option will not override the default directories (~/composables, ~/utils, ~/directives).
+     * Note that this option will extend, not override, the default directories (~/composables, ~/utils, ~/directives).
+     * These default directories will always be included regardless of the directories specified here.
packages/nuxt/src/imports/directives.ts (6)

2-3: Consolidate imports from 'unimport' into a single statement

You can improve code clarity by combining the imports from 'unimport'.

Apply this diff to consolidate the imports:

-import type { AddonsOptions, Import } from 'unimport'
-import { createUnimport } from 'unimport'
+import { createUnimport, type AddonsOptions, type Import } from 'unimport'

25-26: Remove unnecessary optional chaining on 'imports'

Since imports is defined and expected to be an array, the optional chaining operator ?. is unnecessary and can be removed for simplicity.

Apply this diff:

-imports?.forEach(visit)
+imports.forEach(visit)

26-31: Avoid variable shadowing of 'imports' inside the loop

The variable imports is re-declared inside the loop, which shadows the outer imports parameter and may cause confusion.

Consider renaming the inner imports variable to prevent shadowing. Apply this diff:

   presets?.forEach((preset) => {
     if (preset && 'imports' in preset) {
-      const imports = preset.imports as Import[]
-      imports.forEach(visit)
+      const presetImports = preset.imports as Import[]
+      presetImports.forEach(visit)
     }
   })

28-29: Ensure type safety without unnecessary casting

Instead of casting preset.imports using as Import[], consider ensuring proper typing to maintain type safety.

Apply this diff:

-      const presetImports = preset.imports as Import[]
+      const presetImports: Import[] = preset.imports || []

43-43: Extend 'transformInclude' to handle additional file types if necessary

Currently, transformInclude only processes Vue files with script or template types. If directives are used in other contexts, consider updating this function to accommodate them.


52-52: Simplify conditional return statement

You can simplify the return statement for better readability.

Apply this diff:

-      if (!s.hasChanged()) { return }
+      if (!s.hasChanged()) return
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between cbafa58 and cf14552.

📒 Files selected for processing (6)
  • docs/2.guide/2.directory-structure/1.directives.md (1 hunks)
  • packages/nuxt/src/imports/directives.ts (1 hunks)
  • packages/nuxt/src/imports/module.ts (5 hunks)
  • packages/nuxt/src/imports/presets.ts (2 hunks)
  • packages/schema/src/config/adhoc.ts (1 hunks)
  • packages/schema/src/types/imports.ts (1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/2.guide/2.directory-structure/1.directives.md

[uncategorized] ~44-~44: Loose punctuation mark.
Context: ... default here export default Focus ``` :link-example{to="/docs/examples/features...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (1)
packages/nuxt/src/imports/presets.ts (1)

245-246: LGTM! The type additions enhance directive support.

The addition of DirectiveBinding and WritableComputedRef types to vueTypesPreset aligns well with the PR's objective of supporting Vue directive auto-imports. The DirectiveBinding type is particularly important as it provides proper typing for directive implementations.

Let's verify the types are being used in the new directives implementation:

Also applies to: 255-255

✅ Verification successful

Let me gather more information about the directive implementation to verify if the type is being used correctly.


Based on the gathered information, I can now generate the final response:

The DirectiveBinding type addition is correctly implemented for directive support

The addition of DirectiveBinding type to vueTypesPreset is appropriate as it's being used in the documentation examples for directive implementations (docs/2.guide/2.directory-structure/1.directives.md). The type is essential for providing proper typing support when developers create custom Vue directives in their Nuxt applications.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for usage of DirectiveBinding type in directive-related files
# Expected: Find references in directive implementation files

# Search for DirectiveBinding usage
rg "DirectiveBinding" -A 5

# Search for files in the directives directory
fd "directives"

Length of output: 916


Script:

#!/bin/bash
# Check the content of directives.ts to see how DirectiveBinding is used
cat packages/nuxt/src/imports/directives.ts

# Search for any directive-related type definitions or implementations
ast-grep --pattern 'interface $_DirectiveBinding {
  $$$
}'

# Look for directive-related imports from Vue
rg "import.*from 'vue'" -A 2

Length of output: 29413

Comment on lines +29 to +43
```ts
function mounted (el: HTMLElement, binding: TouchDirectiveBinding) {
// ...
}

// other lifecycle hooks

export const Focus = {
mounted
// ...
}

// DONT' USE export default here
export default Focus
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve the implementation example for clarity and correctness.

The current example has several issues:

  1. It includes a discouraged export default statement
  2. Uses unexplained type TouchDirectiveBinding
  3. Contains incomplete code with "..." placeholders

Consider replacing it with a complete, working example:

-function mounted (el: HTMLElement, binding: TouchDirectiveBinding) {
+import type { DirectiveBinding } from 'vue'
+
+function mounted(el: HTMLElement, binding: DirectiveBinding) {
   // ...
 }
 
-// other lifecycle hooks
+function beforeMount(el: HTMLElement, binding: DirectiveBinding) {
+  // Initialize the element
+}
+
+function updated(el: HTMLElement, binding: DirectiveBinding) {
+  // Handle updates to directive arguments or value
+}
 
 export const Focus = {
-  mounted
-  // ...
+  mounted,
+  beforeMount,
+  updated
 }
-
-// DONT' USE export default here
-export default Focus
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```ts
function mounted (el: HTMLElement, binding: TouchDirectiveBinding) {
// ...
}
// other lifecycle hooks
export const Focus = {
mounted
// ...
}
// DONT' USE export default here
export default Focus
```
import type { DirectiveBinding } from 'vue'
function mounted(el: HTMLElement, binding: DirectiveBinding) {
// ...
}
function beforeMount(el: HTMLElement, binding: DirectiveBinding) {
// Initialize the element
}
function updated(el: HTMLElement, binding: DirectiveBinding) {
// Handle updates to directive arguments or value
}
export const Focus = {
mounted,
beforeMount,
updated
}

@@ -31,7 +31,7 @@ export default defineUntypedSchema({

/**
* An array of custom directories that will be auto-imported.
* Note that this option will not override the default directories (~/composables, ~/utils).
* Note that this option will not override the default directories (~/composables, ~/utils, ~/directives).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Documentation is inconsistent with the implementation

The implementation in packages/schema/src/types/imports.ts shows the default directories as ['./composables', './utils', './directives'], but the documentation in adhoc.ts refers to them as (~/composables, ~/utils, ~/directives). The paths use different prefixes (./ vs ~/), which could be confusing for users.

  • packages/schema/src/config/adhoc.ts: Update the documentation to use ./ prefix instead of ~/ to match the actual implementation
  • packages/schema/src/types/imports.ts: Line shows correct default value: @default ['./composables', './utils', './directives']
🔗 Analysis chain

Verify consistency with related configuration files.

Let's ensure this documentation change is consistent with the actual implementation and other configuration files.


Let me check the actual implementation details in the found files to verify the consistency.


Let me check one more thing to verify the actual implementation of these defaults.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistency of directives configuration across files

# Check for related configuration in schema types
echo "Checking schema types for directives configuration..."
rg -l "~/directives|./directives" "packages/schema/src/types/"

# Check for imports configuration in other files
echo "Checking other files for imports configuration..."
rg -l "imports.*dirs.*directives" "packages/"

# Check for documentation consistency
echo "Checking documentation consistency..."
rg -l "default.*directories.*(composables|utils|directives)" "packages/"

Length of output: 565


Script:

#!/bin/bash
# Check the implementation in imports.ts
echo "Content of imports.ts:"
cat packages/schema/src/types/imports.ts

# Check for any other references to these default directories
echo -e "\nSearching for default directory configurations:"
rg -A 5 "defaultDirs|default.*dirs" packages/schema/src/

Length of output: 1164


Script:

#!/bin/bash
# Search for the actual implementation of these defaults
echo "Searching for implementation of default directories:"
rg -A 10 "composables.*utils.*directives" packages/

# Also check the documentation
echo -e "\nChecking documentation mentions:"
rg -A 3 "default.*directories" packages/

Length of output: 1941

@@ -66,6 +118,7 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
}
composablesDirs.push(resolve(layer.config.srcDir, 'composables'))
composablesDirs.push(resolve(layer.config.srcDir, 'utils'))
composablesDirs.push(resolve(layer.config.srcDir, 'directives'))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid adding 'directives' directory to composablesDirs

Adding the 'directives' directory to composablesDirs may result in directives being scanned and processed as composables, which could cause unexpected behavior since directives and composables serve different purposes. It's advisable to handle the 'directives' directory separately to maintain clarity and prevent potential conflicts.

Apply this diff to remove the 'directives' directory from composablesDirs:

- composablesDirs.push(resolve(layer.config.srcDir, 'directives'))

Ensure that directives are managed independently within their appropriate context.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
composablesDirs.push(resolve(layer.config.srcDir, 'directives'))

Comment on lines +61 to +63
const path = resolve(nuxt.options.srcDir, relativePath)
if (directivesDir.includes(path)) {
logger.info(`Directory \`${relativePath}/\` ${event === 'addDir' ? 'created' : 'removed'}`)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Normalize paths before comparison to ensure accurate detection

In the builder:watch hook, the path variable may not match entries in the directivesDir array due to inconsistencies in path formats (e.g., different slashes or case sensitivity across platforms). To ensure accurate detection when directories are added or removed, consider normalizing path before the comparison.

Apply this diff to normalize the path variable:

const path = resolve(nuxt.options.srcDir, relativePath)
+ const normalizedPath = normalize(path)
- if (directivesDir.includes(path)) {
+ if (directivesDir.includes(normalizedPath)) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const path = resolve(nuxt.options.srcDir, relativePath)
if (directivesDir.includes(path)) {
logger.info(`Directory \`${relativePath}/\` ${event === 'addDir' ? 'created' : 'removed'}`)
const path = resolve(nuxt.options.srcDir, relativePath)
const normalizedPath = normalize(path)
if (directivesDir.includes(normalizedPath)) {
logger.info(`Directory \`${relativePath}/\` ${event === 'addDir' ? 'created' : 'removed'}`)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

allow auto import vue directives
2 participants