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

[Feature]: Design of JavaScript Redirect #548

Closed
fi3ework opened this issue Dec 11, 2024 · 9 comments · Fixed by #535
Closed

[Feature]: Design of JavaScript Redirect #548

fi3ework opened this issue Dec 11, 2024 · 9 comments · Fixed by #535

Comments

@fi3ework
Copy link
Member

fi3ework commented Dec 11, 2024

What problem does this feature solve?

subtask of #140

Background: The relationship of alias/externals in bundle and bundleless mode

  • bundle mode: use resolve.alias
  • bundleless mode: use output.external to rewrite the external path, redirect is also implemented via external functionality.

Currently supported features in bundle and bundleless mode:

bundle bundleless
rewrite path N/A
rewrite import extension N/A
resolve.aliasStrategy

Modern.js Module related implementation

  • autoExtension: true: controls output file extensions
  • redirect.alias: controls whether to rewrite alias path
  • redirect.autoExtension: rewrite import extension in output code, it will force add the extension as well (use autoExtension as default value)

Proposal of Rslib

redirect only works for bundleless mode

The main principle to is to align the concept and configuration of alias in bundle mode and externals in bundleless mode to reduce the cognitive cost of users

redirect.externalsStrategy

  • type: 'prefer-tsconfig' | 'prefer-externals'
  • default value: 'prefer-tsconfig'

Just like resolve.externalsStrategy in bundle mode, for bundleless mode, Rslib will automatically load tsconfig.compilerOptions.paths as alias and apply. While in bundleless mode, all path rewrite is performed via externals.

'prefer-tsconfig' and 'prefer-externals' perform the same as resolve.externalsStrategy about the strategy.

redirect.relativeImportExtensions

  • type: boolean
  • default value: true

Rename redirect.autoExtension in Modern.js Module (aligned with typescript), relativeImportExtensions changes the extension in the output code which pointing to other output files that will be determined by autoExtension.

main files

Since only CommonJS only support the main files (https://nodejs.org/api/modules.html#all-together). By default:

  • For ESM output, patching the main file path to index.mjs (if exists).
  • For CJS output, patching the main file path to index.cjs (if exists).
    For CommonJS output, do not patch the main file path, leave the path as it is. (I'm not quite sure, maybe we could add the main files directly 🤔)

Examples overview

.
└── src
    ├── index.ts
    ├── foo.ts
    └── utils
        ├── index.ts
        └── add.ts

ESM output

  • import foo from './foo' -> import foo from './foo.mjs'
  • import foo from './foo.ts' -> import foo from './foo.mjs'
  • import foo from './foo.js' -> import foo from './foo.mjs'
  • import foo from './utils' -> import foo from './utils/index.mjs'
  • import foo from './utils/index' -> import foo from './utils/index.mjs'

CommonJS output

  • import foo from './foo' -> import foo from './foo.js'
  • import foo from './foo.ts' -> import foo from './foo.js'
  • import foo from './foo.js' -> import foo from './foo.js'
  • import foo from './utils' -> import foo from './utils'
  • import foo from './utils/index' -> import foo from './utils/index.js'

Compares with Modern.js Module

Modern.js Module Rslib
redirect.alias Removed. I believe there are few situations that require disabling the alias. If we really need to do so in the future, we can use redirect.externalsStrategy = false as an alternative.
redirect.autoExtension changed to redirect.rewriteRelativeImportExtensions
redirect.externalsStrategy

What does the proposed API look like?

/

@fi3ework
Copy link
Member Author

fi3ework commented Dec 11, 2024

To be determined:

  • Do we need to add the main files for the CommonJS output, or should we leave it as is?

@Timeless0911
Copy link
Contributor

  1. Can we just read resolve.externalsStrategy and apply it to our implementation in externals configurations. Thus, the config can be named redirect.alias as before. I think users do not need to know that bundleless is externals and bundle is alias, these can all be considered as aliases in users side.
  2. redirect.rewriteRelativeImportExtensions seems too long, just redirect.relativeImportExtensions is enough since redirect has the same semantics with rewrite.
  3. I think we do not need to distinguish redirection of main files from CJS and ESM output. They can all be rewrited.
    import foo from './utils' -> import foo from './utils/index.(m)js'

@fi3ework
Copy link
Member Author

  1. I used to consider that implementation, However, in our current documentation and implementation, output.externals and resolve.alias have diverged (https://lib.rsbuild.dev/config/rsbuild/resolve#resolvealias). To avoid confusion, we have maintained this distinction.
  2. LGTM. That name was taken from typescript, verbose but clear.
  3. LGTM. We could omit the main file path in the future if needed, adopt simple and unified implementation method first.

@Timeless0911
Copy link
Contributor

I used to consider that implementation, However, in our current documentation and implementation, output.externals and resolve.alias have diverged (lib.rsbuild.dev/config/rsbuild/resolve#resolvealias). To avoid confusion, we have maintained this distinction.

We can read resolve.externalsStrategy and the redirection configuration can be named redirect.externals instead if we want to let user clarify the nature of this behaviour.

@fi3ework
Copy link
Member Author

  1. read from resolve.aliasStrategy
  2. use redirect.externals

Are these two options you provided?

@Timeless0911
Copy link
Contributor

  1. read from resolve.aliasStrategy
  2. use redirect.externals

Are these two options you provided?

This is a complete process. We only need to compose redirect.externals to users and we can read from resolve.aliasStrategy in implementation.

@Timeless0911
Copy link
Contributor

Timeless0911 commented Dec 11, 2024

type Externals =
  | {
      strategy: 'prefer-tsconfig' | 'prefer-externals';
    }
  | boolean;

export type Redirect = {
  style?: boolean;
  externals?: Externals;
};

The default strategy is 'prefer-tsconfig'.

@fi3ework
Copy link
Member Author

type Externals =
| {
strategy: 'prefer-tsconfig' | 'prefer-externals';
}
| boolean;

export type Redirect = {
style?: boolean;
externals?: Externals;
};

LGTM. Maintain scalability while being compatible with previous configurations.

@fi3ework
Copy link
Member Author

fi3ework commented Dec 16, 2024

The implementation has been updated to:

export type JsRedirect = {
  /**
   * Whether to automatically redirect the import paths of JavaScript output files,
   * compilerOptions.paths in tsconfig.json will be applied by default.
   * @defaultValue `true`
   */
  path?: boolean;
  /**
   * Whether to automatically add the file extension based on the JavaScript output files.
   * @defaultValue `true`
   */
  extension?: boolean;
};

export type Redirect = {
  /** Controls the redirect of the import paths of JavaScript output files. */
  js?: JsRedirect;
  style?: boolean;
};

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

Successfully merging a pull request may close this issue.

2 participants