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(schema,nuxt): add shared folder to alias and auto-imports #28682

Merged
merged 7 commits into from
Nov 2, 2024

Conversation

Barbapapazes
Copy link
Contributor

@Barbapapazes Barbapapazes commented Aug 23, 2024

🔗 Linked issue

fix #28675

📚 Description

Hello 👋,

This PR adds:

  • #shared alias to both tsconfig.json and tsconfig.server.json
  • Enable auto imports from #shared/types and #shared/utils in app and server directories

The second bullet point is still in progress because I'm still trying to understand the usage of unimport within the project.

So, after talking with @danielroe, this #shared folder is more complicated than expected. Here a list of expected behavior (that needs to be implemented)

  • ./app folder can import (and auto-import) from the ./shared folder
  • ./server folder can import (and auto-import) from the ./shared folder
  • Inside the ./shared folder, you should not be able to import stuff from both ./app and ./server (could be achieved by excluding the file from the tsconfig.json and a single chunk could be created)
  • Inside the./shared folder, you could (we need to decide if we want or not this behavior) import and auto-import stuff from the ./shared folder.

At the end, the shared folder is just for pure JS (or TS) stuff. Nothing have to be related to Vue or Nitro.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced import protection mechanisms with context-aware handling for shared directories.
    • Conditional inclusion of additional directories for Nuxt 4 compatibility.
  • Bug Fixes

    • Improved error handling and reporting for module fetching in the Vite node server.
  • Documentation

    • Updated configuration schema to include a shared directory for better resource organization.
  • Refactor

    • Restructured Nitro configuration process for enhanced flexibility across different Nuxt versions.

Copy link

stackblitz bot commented Aug 23, 2024

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

@DamianGlowala DamianGlowala added 4.x and removed 3.x labels Aug 23, 2024
@Barbapapazes Barbapapazes changed the title feat(schema): add shared alias feat(schema,nuxt): add shared folder to alias and auto-imports Aug 23, 2024
@Barbapapazes Barbapapazes marked this pull request as ready for review August 23, 2024 17:30
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.

This looks great!

The next step is to configure import protection so that we do not use certain protected imports in that folder...

@Barbapapazes
Copy link
Contributor Author

Barbapapazes commented Aug 29, 2024

import protection

What do you mean? I never heard this term before. Do you have an example?


Edit

Oh, you mean, protecting against unwanted imports like vue / nitro related stuff within the shared folder to ensure it will works properly in both app and server.

@danielroe
Copy link
Member

You can add patterns for the shared directory here:

export const nuxtImportProtections = (nuxt: { options: NuxtOptions }, options: { isNitro?: boolean } = {}) => {
const patterns: ImportProtectionOptions['patterns'] = []
patterns.push([
/^(nuxt|nuxt3|nuxt-nightly)$/,
'`nuxt`, `nuxt3` or `nuxt-nightly` cannot be imported directly.' + (options.isNitro ? '' : ' Instead, import runtime Nuxt composables from `#app` or `#imports`.'),
])
patterns.push([
/^((~|~~|@|@@)?\/)?nuxt\.config(\.|$)/,
'Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module.',
])
patterns.push([/(^|node_modules\/)@vue\/composition-api/])
for (const mod of nuxt.options.modules.filter(m => typeof m === 'string')) {
patterns.push([
new RegExp(`^${escapeRE(mod as string)}$`),
'Importing directly from module entry-points is not allowed.',
])
}
for (const i of [/(^|node_modules\/)@nuxt\/(kit|test-utils)/, /(^|node_modules\/)nuxi/, /(^|node_modules\/)nitro(?:pack)?(?:-nightly)?(?:$|\/)(?!(?:dist\/)?runtime|types)/, /(^|node_modules\/)nuxt\/(config|kit|schema)/]) {
patterns.push([i, 'This module cannot be imported' + (options.isNitro ? ' in server runtime.' : ' in the Vue part of your app.')])
}
if (options.isNitro) {
for (const i of ['#app', /^#build(\/|$)/]) {
patterns.push([i, 'Vue app aliases are not allowed in server runtime.'])
}
}
if (!options.isNitro) {
patterns.push([
new RegExp(escapeRE(relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, nuxt.options.serverDir || 'server'))) + '\\/(api|routes|middleware|plugins)\\/'),
'Importing from server is not allowed in the Vue part of your app.',
])
}
return patterns
}

Or let me know, and I'll happily implement. 🙏

@danielroe danielroe marked this pull request as draft August 29, 2024 19:52
@Barbapapazes
Copy link
Contributor Author

Barbapapazes commented Sep 17, 2024

You can add patterns for the shared directory here:

export const nuxtImportProtections = (nuxt: { options: NuxtOptions }, options: { isNitro?: boolean } = {}) => {
const patterns: ImportProtectionOptions['patterns'] = []
patterns.push([
/^(nuxt|nuxt3|nuxt-nightly)$/,
'`nuxt`, `nuxt3` or `nuxt-nightly` cannot be imported directly.' + (options.isNitro ? '' : ' Instead, import runtime Nuxt composables from `#app` or `#imports`.'),
])
patterns.push([
/^((~|~~|@|@@)?\/)?nuxt\.config(\.|$)/,
'Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module.',
])
patterns.push([/(^|node_modules\/)@vue\/composition-api/])
for (const mod of nuxt.options.modules.filter(m => typeof m === 'string')) {
patterns.push([
new RegExp(`^${escapeRE(mod as string)}$`),
'Importing directly from module entry-points is not allowed.',
])
}
for (const i of [/(^|node_modules\/)@nuxt\/(kit|test-utils)/, /(^|node_modules\/)nuxi/, /(^|node_modules\/)nitro(?:pack)?(?:-nightly)?(?:$|\/)(?!(?:dist\/)?runtime|types)/, /(^|node_modules\/)nuxt\/(config|kit|schema)/]) {
patterns.push([i, 'This module cannot be imported' + (options.isNitro ? ' in server runtime.' : ' in the Vue part of your app.')])
}
if (options.isNitro) {
for (const i of ['#app', /^#build(\/|$)/]) {
patterns.push([i, 'Vue app aliases are not allowed in server runtime.'])
}
}
if (!options.isNitro) {
patterns.push([
new RegExp(escapeRE(relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, nuxt.options.serverDir || 'server'))) + '\\/(api|routes|middleware|plugins)\\/'),
'Importing from server is not allowed in the Vue part of your app.',
])
}
return patterns
}

Or let me know, and I'll happily implement. 🙏

I do not know how to know when the folder is the shared folder 🤔

Thanks to @manniL I could push a new plugin with a different cwd

@Barbapapazes
Copy link
Contributor Author

I'm facing another issue. Everything in the shared folder is available from imports folder because of this, people would want to import inside the shared folder, stuff from the shared folder but at the same time, I do not want people to import Vue stuff (from imports) inside the shared folder.

@TheAlexLichter
Copy link
Member

I'm facing another issue. Everything in the shared folder is available from imports folder because of this, people would want to import inside the shared folder, stuff from the shared folder but at the same time, I do not want people to import Vue stuff (from imports) inside the shared folder.

Is there anything in imports which is neither server nor client-dependent?

@TheAlexLichter
Copy link
Member

@Barbapapazes I think adding the import protection for folders is a good first step. (cc @danielroe if you have ideas how to protect from #imports)

Copy link

coderabbitai bot commented Nov 1, 2024

Walkthrough

The changes in this pull request involve significant updates to the Nitro framework configuration within a Nuxt application. Key modifications include the introduction of a new #shared alias, enhancements to import protection mechanisms, and conditional logic for handling shared directories based on the Nuxt version. New methods and properties have been added to facilitate these updates, improving the flexibility and adaptability of the configuration process for different project setups.

Changes

File Path Change Summary
packages/nuxt/src/core/nitro.ts Renamed import for import protection patterns, updated logic for auto-import directories based on Nuxt version, expanded Nitro configuration, refined route rules.
packages/nuxt/src/core/nuxt.ts Replaced import for import protection, established shared folder import protection, updated configuration for Vite and Webpack plugins.
packages/nuxt/src/core/plugins/import-protection.ts Added new interface for import protection context, replaced nuxtImportProtections with createImportProtectionPatterns, updated error handling logic.
packages/nuxt/src/imports/module.ts Added isNuxtV4 variable to manage composables directories, modified setup function for conditional directory inclusion.
packages/nuxt/test/import-protection.test.ts Updated tests to use createImportProtectionPatterns, modified function signature to include context parameter.
packages/schema/src/config/common.ts Added shared property in dir object, updated alias for #shared directory.
packages/vite/src/server.ts Modified buildServer function to include #shared in external dependencies.
packages/vite/src/utils/external.ts Updated createIsExternal function to accept nuxt object, modified external options to include #shared.
packages/vite/src/vite-node.ts Adjusted createViteNodeApp to pass ctx.nuxt directly to createIsExternal, enhanced error handling in eventHandler.
packages/webpack/src/configs/server.ts Updated external array in serverStandalone to include #shared, modified path resolution logic.

Assessment against linked issues

Objective Addressed Explanation
Add #shared alias (#[28675])
Support auto-import for #shared/types and #shared/utils (#[28675])

🐰 In the meadow, where the bunnies play,
New paths have opened, brightening the day.
With #shared to guide us, we hop and we bound,
Importing our treasures, all safe and sound!
So let’s celebrate changes, both big and small,
For in our cozy burrow, there's room for us all! 🌼


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.

@danielroe danielroe marked this pull request as ready for review November 1, 2024 18:02
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: 0

🧹 Outside diff range and nitpick comments (4)
packages/nuxt/test/import-protection.test.ts (1)

41-50: Consider adding runtime validation for context parameter

While the TypeScript type union ensures type safety at compile time, consider adding runtime validation for the context parameter to provide clearer error messages during testing.

Example implementation:

 const transformWithImportProtection = (id: string, importer: string, context: 'nitro-app' | 'nuxt-app' | 'shared') => {
+  const validContexts = ['nitro-app', 'nuxt-app', 'shared'] as const
+  if (!validContexts.includes(context as any)) {
+    throw new Error(`Invalid context "${context}". Must be one of: ${validContexts.join(', ')}`)
+  }
   const plugin = ImpoundPlugin.rollup({
     cwd: '/root',
     patterns: createImportProtectionPatterns({
packages/vite/src/server.ts (1)

84-90: LGTM: Robust external dependencies configuration.

The external dependencies configuration correctly implements the #shared alias requirements:

  1. Explicitly marks #shared as external
  2. Properly handles the shared directory path with safe RegExp escaping and correct path normalization

Consider adding a debug log to help troubleshoot the resolved shared directory path:

 external: [
   'nitro/runtime',
   '#internal/nuxt/paths',
   '#internal/nuxt/app-config',
   '#shared',
+  // Log the resolved path in development
+  process.env.DEBUG && console.log('Shared dir pattern:', escapeStringRegexp(withTrailingSlash(resolve(ctx.nuxt.options.rootDir, ctx.nuxt.options.dir.shared)))),
   new RegExp('^' + escapeStringRegexp(withTrailingSlash(resolve(ctx.nuxt.options.rootDir, ctx.nuxt.options.dir.shared)))),
 ],
packages/schema/src/config/common.ts (1)

358-361: Enhance documentation for the shared directory.

The current documentation should be expanded to include important constraints and usage guidelines.

     /**
-     * The shared directory. This directory is shared between the app and the server.
+     * The shared directory containing pure JavaScript/TypeScript code that can be imported by both
+     * the app and server directories. This directory must not contain Vue components or have
+     * dependencies on Vue or Nitro frameworks to maintain proper separation of concerns.
      */
     shared: 'shared',
packages/nuxt/src/core/plugins/import-protection.ts (1)

59-63: Standardize context descriptions in contextFlags

In the contextFlags object, consider adjusting the description for the 'shared' context from 'the #shared directory' to '`#shared`'. This change ensures that error messages read naturally and consistently across different contexts, for example: "nuxt cannot be imported directly in #shared."

Proposed change:

 const contextFlags = {
   'nitro-app': 'server runtime',
   'nuxt-app': 'the Vue part of your app',
-  'shared': 'the #shared directory',
+  'shared': '`#shared`',
 } as const
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0ecb42b and 2aa8af1.

📒 Files selected for processing (10)
  • packages/nuxt/src/core/nitro.ts (4 hunks)
  • packages/nuxt/src/core/nuxt.ts (2 hunks)
  • packages/nuxt/src/core/plugins/import-protection.ts (2 hunks)
  • packages/nuxt/src/imports/module.ts (2 hunks)
  • packages/nuxt/test/import-protection.test.ts (3 hunks)
  • packages/schema/src/config/common.ts (2 hunks)
  • packages/vite/src/server.ts (2 hunks)
  • packages/vite/src/utils/external.ts (2 hunks)
  • packages/vite/src/vite-node.ts (1 hunks)
  • packages/webpack/src/configs/server.ts (2 hunks)
🔇 Additional comments (26)
packages/vite/src/utils/external.ts (6)

4-6: LGTM: Imports support new shared directory path handling.

The new imports provide necessary utilities for safe path manipulation and type safety.


10-10: LGTM: Improved function signature using object parameter pattern.

The change to accept a nuxt object parameter improves maintainability by centralizing configuration access.


29-29: LGTM: Consistent use of Nuxt configuration.

The update to use nuxt.options.modulesDir aligns with the centralized configuration approach.


35-35: LGTM: Consistent update to use Nuxt configuration.

The return statement correctly uses the rootDir from Nuxt options, maintaining consistency with the refactoring.


Line range hint 10-35: Verify import protection mechanism.

The implementation adds the #shared alias and path-based restrictions, but we should verify that it effectively prevents unwanted imports from Vue/Nitro in the shared folder as per PR objectives.

#!/bin/bash
# Description: Check for potential Vue/Nitro imports in shared folder

# Look for Vue imports in shared directory
rg "from ['|\"]vue['|\"]|from ['|\"]@vue" --type ts

# Look for Nitro imports in shared directory
rg "from ['|\"]nitro['|\"]|from ['|\"]@nitro" --type ts

# Look for import protection patterns in build configuration
rg -A 5 "noExternal|external.*shared" --type ts

23-24: Verify Windows path compatibility.

The path handling looks correct, but we should verify that the regex pattern works correctly on Windows systems, particularly with path separators.

✅ Verification successful

Path handling is correctly implemented for Windows compatibility

The implementation is safe for Windows environments because:

  1. It uses Node's resolve() function which automatically handles path separators for the current OS
  2. The regex pattern is created using escapeStringRegexp() which properly escapes all special characters
  3. The withTrailingSlash() utility is used consistently across the codebase for both Windows and POSIX paths
  4. Similar path handling patterns are used in other parts of the codebase (nitro, webpack) without platform-specific issues
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if there are any Windows-specific path handling tests
# or related issues in the codebase

# Look for Windows path-related tests
rg -i "windows.*path|path.*windows" --type ts

# Look for path separator handling tests
rg -i "path\.sep|path\.delimiter" --type ts

# Look for related issues in test files
fd -e test.ts -e spec.ts -x rg -l "shared.*path|path.*shared" {}

Length of output: 13064


Script:

#!/bin/bash
# Let's check the implementation details around path handling

# Check the implementation of withTrailingSlash and related path utilities
ast-grep --pattern 'function withTrailingSlash'

# Look for path normalization or separator handling
rg -A 5 'normalize(Path|Separator)' --type ts

# Check if there are any Windows-specific path handling tests
fd -e test.ts -e spec.ts -x rg -l "\\\\|path\.sep" {}

# Check the actual implementation around shared directory resolution
rg -A 5 "dir\.shared" --type ts

Length of output: 4189

packages/nuxt/test/import-protection.test.ts (3)

4-4: LGTM! Function rename improves clarity

The rename from nuxtImportProtections to createImportProtectionPatterns better describes the function's purpose.


31-31: 🛠️ Refactor suggestion

Consider adding test cases for different contexts

The test currently only uses the 'nuxt-app' context. Given that the PR introduces import protection for different contexts ('nitro-app', 'nuxt-app', 'shared'), consider adding test cases that verify import protection behavior for each context.

Example test structure:

describe('import protection', () => {
  describe.each(['nuxt-app', 'nitro-app', 'shared'] as const)('with %s context', (context) => {
    it.each(testsToTriggerOn)('should protect %s', async (id, importer, isProtected) => {
      const result = await transformWithImportProtection(id, importer, context)
      // ... existing assertions
    })
  })
})

Line range hint 8-20: Verify coverage of shared folder import constraints

The PR objectives specify that the shared folder should not import from app or server. Let's verify if the test cases cover these scenarios.

Consider adding test cases like:

const sharedImportTests = [
  ['~/app/component', 'shared/utils/helper.ts', true],  // should block
  ['~/server/api', 'shared/types/common.ts', true],     // should block
  ['~/shared/utils', 'app/component.vue', false],       // should allow
] as const
packages/webpack/src/configs/server.ts (2)

1-1: LGTM: Import of resolve from 'pathe'

The addition of resolve from 'pathe' is appropriate for cross-platform path resolution, which is necessary for handling the shared directory path.


56-60: Verify shared directory resolution and externalization

The changes correctly add support for the #shared alias by:

  1. Adding the #shared alias to externals
  2. Including the resolved shared directory path

However, we should verify that this implementation:

  1. Properly resolves the shared directory path across different Nuxt project structures
  2. Correctly handles the externalization of shared modules

Let's verify the implementation:

✅ Verification successful

Shared directory externalization implementation is correctly handled

The implementation is verified to be robust and consistent across the framework:

  • The shared directory is properly configured in the schema and correctly resolved in Nuxt core using both absolute and relative paths
  • The externalization is implemented consistently across both Webpack and Vite:
    • Uses #shared alias
    • Includes the resolved absolute path
    • Handles path normalization with trailing slashes
  • Additional protection is implemented via import protection plugins for both Vite and Webpack
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the shared directory resolution and usage

# Test 1: Check if the shared directory is consistently referenced across the codebase
echo "Checking shared directory references..."
rg -l "dir\.shared" 

# Test 2: Verify the usage of #shared alias in imports
echo "Checking #shared alias usage..."
rg -l "#shared"

# Test 3: Look for potential circular dependencies
echo "Checking for potential circular dependencies..."
rg -l "from ['\"](#app|#server).*" ./shared

Length of output: 861


Script:

#!/bin/bash
# Let's analyze the implementation details of shared directory handling

# Check how shared directory is configured and used
echo "Checking shared directory configuration in schema..."
rg -A 5 "dir\.shared" packages/schema/src/config/common.ts

# Check how the shared directory is handled in Nuxt core
echo "Checking shared directory handling in Nuxt core..."
rg -A 10 "dir\.shared" packages/nuxt/src/core/nuxt.ts

# Check external handling in Vite config
echo "Checking Vite external handling..."
rg -A 5 "#shared" packages/vite/src/utils/external.ts

# Check if there are any imports using the #shared alias
echo "Checking actual #shared alias usage in imports..."
rg "from ['\"]#shared" 

Length of output: 1941

packages/vite/src/vite-node.ts (1)

Line range hint 143-150: Verify external module resolution with the new #shared alias.

The change to pass the entire nuxt context to createIsExternal aligns with the PR objectives for implementing the #shared alias. However, we should verify that external module resolution works correctly with the new shared folder structure.

Let's verify the external module resolution:

This change looks good, but consider adding tests to verify:

  1. The external module resolution with the #shared alias
  2. The prevention of Vue/Nitro imports in the shared folder
✅ Verification successful

External module resolution change looks safe and well-implemented

The verification shows that the change to pass the entire nuxt context to createIsExternal is safe and consistent with the codebase:

  • The createIsExternal implementation in packages/vite/src/utils/external.ts properly handles the Nuxt context
  • The resolveModule usage is consistent across the codebase and properly handles error cases
  • The change maintains compatibility with Vite's SSR external/noExternal configuration

The implementation correctly handles both the module resolution and error cases, making it suitable for the #shared alias implementation.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential issues with external module resolution
# and shared folder imports.

# Test 1: Check if there are any direct imports from app/server to shared
echo "Checking for imports from app/server to shared..."
rg -g '!node_modules' -g 'app/**' -g 'server/**' "from ['\"].*shared.*['\"]"

# Test 2: Check if shared folder contains any Vue/Nitro imports
echo "Checking shared folder for Vue/Nitro imports..."
rg -g '!node_modules' -g 'shared/**' "from ['\"](@vue|@nuxt|vue|nuxt|nitro)['\"]"

# Test 3: Verify #shared alias usage
echo "Checking #shared alias usage..."
rg -g '!node_modules' "from ['\"]#shared.*['\"]"

Length of output: 810


Script:

#!/bin/bash
# Let's try a different approach to verify the external module resolution changes

# Check the implementation of createIsExternal function
echo "Checking createIsExternal implementation..."
rg -A 10 "function createIsExternal" 

# Check for any existing tests related to external resolution
echo "Checking for related tests..."
fd -e test.ts -e spec.ts | xargs rg -l "external|isExternal"

# Look for other usages of resolveModule
echo "Checking resolveModule usage..."
rg "resolveModule\(" -A 2

# Check if there are any existing shared folder references
echo "Looking for shared folder patterns..."
fd shared

Length of output: 2196

packages/vite/src/server.ts (1)

10-10: LGTM: Safe string escaping for RegExp.

The addition of escapeStringRegexp is appropriate for safely handling path strings in RegExp patterns.

packages/nuxt/src/imports/module.ts (2)

57-58: LGTM: Good use of feature flagging.

The isNuxtV4 flag is well-implemented to ensure backward compatibility while enabling new features for Nuxt v4.


70-73: Consider adding import protection for shared directories.

While the directory structure is correctly implemented, consider adding import protection mechanisms to enforce the following requirements from the PR objectives:

  1. Prevent Vue/Nitro dependencies in the shared folder
  2. Prevent imports from app and server directories within the shared folder

Let's verify if import protection patterns exist:

Consider implementing import protection by:

  1. Adding patterns to prevent Vue/Nitro imports in shared files
  2. Adding patterns to prevent app/server imports in shared files
  3. Using static analysis tools to validate these constraints during build time

Would you like assistance in implementing these protection mechanisms?

packages/schema/src/config/common.ts (2)

432-432: LGTM: Alias implementation is correct and consistent.

The #shared alias is properly implemented:

  • Resolves path relative to rootDir for compatibility with monorepo setups
  • Follows the same pattern as other directory aliases

Also applies to: 438-438


Line range hint 358-438: Verify implementation of import protection for shared directory.

While the directory and alias setup looks good, we should verify that import protection is properly implemented to prevent importing from app and server directories within the shared directory.

✅ Verification successful

Let's check the content of the file that matched our search to better understand the implementation.


Let's check the implementation of the import protection patterns and the ImpoundPlugin to ensure they properly prevent imports from app and server directories.


Let's check the specific implementation of the import protection patterns to verify the restrictions on app and server imports.


Import protection for shared directory is properly implemented

The implementation in import-protection.ts confirms that the shared directory has proper import protection mechanisms:

  1. For shared directory context (options.context === 'shared'):
    • Prevents imports from Vue app aliases (#app, #build)
    • Blocks imports from server directory (server/(api|routes|middleware|plugins))
    • Uses ImpoundPlugin to enforce these restrictions at build time for both Vite and Webpack

The implementation meets the PR objectives of preventing imports from app and server within the shared directory.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for import protection implementation in the codebase

# Look for import protection patterns related to shared directory
echo "Searching for import protection implementation..."
rg -l "shared.*import.*protection" --type ts

# Look for specific import validation logic
echo "Searching for import validation logic..."
ast-grep --pattern 'function $name($$$) {
  $$$
  shared $$$
  $$$
  import $$$
  $$$
}'

Length of output: 368


Script:

#!/bin/bash
# Check the content of the matched file
echo "Content of packages/nuxt/src/core/nuxt.ts:"
rg -A 10 -B 10 "shared.*import.*protection" packages/nuxt/src/core/nuxt.ts

# Look for any import validation or path checking logic
echo "\nSearching for import path validation patterns:"
ast-grep --pattern 'if ($condition) {
  $$$
  import $$$
  $$$
}'

# Search for any build-time or runtime checks related to imports
echo "\nSearching for import-related hooks or plugins:"
rg -l "import.*check|validateImport|resolveImport" --type ts

Length of output: 1934


Script:

#!/bin/bash
# Check the implementation of import protection patterns
echo "Searching for createImportProtectionPatterns implementation:"
rg -A 10 "createImportProtectionPatterns" --type ts

# Look for ImpoundPlugin implementation
echo "\nSearching for ImpoundPlugin implementation:"
rg -A 10 "ImpoundPlugin" --type ts

# Check for any app/server import restrictions
echo "\nSearching for app/server import restrictions:"
rg -A 5 "app.*server.*import|import.*app.*server" --type ts

Length of output: 14526


Script:

#!/bin/bash
# Check the content of the import protection implementation
echo "Content of import protection patterns implementation:"
cat packages/nuxt/src/core/plugins/import-protection.ts

# Look for any tests verifying the import restrictions
echo "\nContent of import protection tests:"
cat packages/nuxt/test/import-protection.test.ts

Length of output: 4863

packages/nuxt/src/core/nitro.ts (5)

20-20: Import 'createImportProtectionPatterns' added

The import statement for createImportProtectionPatterns from './plugins/import-protection' is correctly added and used appropriately in the code.


52-52: Version compatibility check introduced

The introduction of isNuxtV4 provides a clear way to check for Nuxt version 4 compatibility using nuxt.options.future?.compatibilityVersion === 4.


71-76: Conditional inclusion of shared directories in imports

The imports.dirs configuration now conditionally includes the shared utils and types directories when isNuxtV4 is true, aligning with the PR objectives to facilitate auto-imports from the #shared directory.


374-382: Implementing import protection for 'shared' context

The code correctly adds an ImpoundPlugin instance to enforce import protection patterns for the 'shared' context, using sharedPatterns in the include property and specifying the appropriate patterns.


383-386: Extending import protection to 'nitro-app' context

The addition of import protection patterns for the 'nitro-app' context, with relevant exclude patterns including sharedPatterns, ensures that import rules are correctly enforced without interfering with the 'shared' context.

packages/nuxt/src/core/nuxt.ts (4)

34-34: Import createImportProtectionPatterns

The import statement correctly brings in createImportProtectionPatterns from ./plugins/import-protection, aligning with its usage later in the code.


251-262: Verify correctness of shared directory paths and import patterns

The code introduces import protection for the shared directory using sharedDir, relativeSharedDir, and sharedPatterns. Please ensure that:

  • nuxt.options.dir.shared is correctly defined and points to the intended shared directory.
  • Path resolutions with resolve and relative functions work correctly across different operating systems.
  • The regular expressions in sharedPatterns accurately match all intended import paths to and from the shared directory.

260-261: Confirm target environments for shared import protection plugins

The addVitePlugin and addWebpackPlugin functions are called with { server: false }, applying the import protection only to the client-side build. If import protection is also required on the server-side, consider adjusting the options to ensure consistent protection across both client and server.


264-272: Ensure exclusion of shared patterns from Nuxt import protection is intentional

In the nuxtProtectionConfig, the exclude array includes ...sharedPatterns, which excludes the shared directory from this import protection configuration. Verify that this exclusion is intentional and aligns with the desired import protection behavior, especially considering the newly added shared directory import protection.

@Rednas83
Copy link

Rednas83 commented Nov 2, 2024

Why not share the utils on default with the server?

Something like

[app]/utils/capitalize.ts // Shared with the server on default
[app]/utils/capitalize.client.ts // Client only
[app]/utils/capitalize.server.ts // Server only

Advantages

  • All utils in a single folder
  • Same ux that is already used for components, pages and plugins
  • No need to introduce a new shared folder

@KevinOustry
Copy link

Hi there,
Nice idea of adding this shared folder.
But how it will works with Vue-Email ?
Indeed we need to import specific Components from app into the server directory in order to render them with the data.
Thanks !

@s1gr1d
Copy link

s1gr1d commented Nov 5, 2024

It seems like this breaks the Nuxt build when using the Sentry Nuxt SDK where Sentry is adding a Rollup plugin that wraps the server entry with a dynamic import(). We at Sentry have to do this, so OpenTelemetry works and users don't have to add the --import flag to the node command.

This issue was filed by a user: getsentry/sentry-javascript#14179
I also checked this by running the default build and got this error:

RollupError: [plugin impound] This module cannot be imported in server runtime. [importing node_modules/nitropack/dist/presets/node/runtime/node-server.mjs from node_modules/nitropack/dist/presets/node/runtime/node-server.mjs?sentry-query-wrapped-entry?sentry-query-reexported-functions=defaultSENTRY-QUERY-END]

What the Sentry Nuxt Rollup plugin does is similar to the polyfill code example in the Rollup docs: https://rollupjs.org/plugin-development/#resolveid

This is the Rollup plugin in the Sentry Nuxt SDK: https://github.com/getsentry/sentry-javascript/blob/develop/packages/nuxt/src/vite/addServerConfig.ts#L116

Tell me if you need a reproduction :)

@ThimoDEV
Copy link
Contributor

ThimoDEV commented Nov 5, 2024

Just tried the new shared folder, but found that auto import breaks for subfolders inside the ./shared/utils folder. I tried to specify a config folder in ./shared/utils/config/myConfig.ts and have mutiple files there but these are not auto imported

@danielroe
Copy link
Member

@ThimoDEV The auto-imports is only top level. For files nested more deeply, you can import from #shared/your-folder

@ThimoDEV
Copy link
Contributor

ThimoDEV commented Nov 6, 2024

@danielroe Makes sense that its not possible like with the components. If we would like to configure more folders for auto import besides the types and utils folder, we would need to add that in nuxt config imports setting, right?

@TheAlexLichter
Copy link
Member

Makes sense that its not possible like with the components. If we would like to configure more folders for auto import besides the types and utils folder, we would need to add that in nuxt config imports setting, right?

Correct 👍

@matt-clegg
Copy link
Contributor

Is there a reason why only the top level files in the shared/utils and shared/types folders are auto-imported, and not nested folders like shared/foo or shared/utils/bar?

@Barbapapazes
Copy link
Contributor Author

Is there a reason why only the top level files in the shared/utils and shared/types folders are auto-imported, and not nested folders like shared/foo or shared/utils/bar?

Because it is the same behavior for composables, utils, modules, plugins

@vasilistotskas
Copy link

Is this recommended practice if i need to auto import all nested folders of the /shared directory? (it seems to work)

export default defineNuxtConfig({
  imports: {
    autoImport: true,
    dirs: [
      'stores/**',
      '../shared/constants/**',
      '../shared/schemas/**',
      '../shared/types/**',
      '../shared/utils/**',
    ],
  },
    nitro: {
    imports: {
      dirs: [
        'shared/constants/**',
        'shared/schemas/**',
        'shared/types/**',
        'shared/utils/**',
      ],
    },
})

@TheAlexLichter
Copy link
Member

you can just do shared/** to get all subfolders 👍🏻
The others are as you pointed out. Not sure if you could even use #shared/** as alias to avoid the difference between nitro and nuxt auto imports.

@BlueskyFR
Copy link

BlueskyFR commented Nov 17, 2024

Hi everyone! It seems no documentation has been added along with this PR, is it planned? - my bad, just saw the other issue refering that!
For example shared, shared/types and shared/utils are not present in the directory structure tab.
Also the rest of the documentation does not yet refer to the shared folder when specifying which directories are provided with autoimport capabilities.

@BlueskyFR
Copy link

Also it seems that this feature requires Nuxt V4 although the blog post pretends it is also available for V3. Maybe either the PR or the blog post should be updated accordingly?

@BlueskyFR
Copy link

Just noticed a bug (after enabling V4 😋): type aliases are not created if the filenames are *.d.ts, they SHOULD be *.ts it seems.
Is this a desired behavior?

@BlueskyFR
Copy link

BlueskyFR commented Nov 17, 2024

Another one... Should I open up separate issues for these?

Having a shared/types/test.ts file with that:

export const hey = 2;
export type hey = string;

In app.vue I have the following problems:
image
image

And it works perfectly fine with a regular TS project, meaning the shared dir autoimports are messing up something here it seems

@TheAlexLichter
Copy link
Member

@BlueskyFR Please raise an extra issue if you encounter problems 👍

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.

Add #shared alias