magic-comments v2.1.12
✨ magic-comments
Tooling utility to add configurable webpack magic comments to dynamic import() expressions at build time.
Useful when working with:
- Babel plugins
- Webpack loaders
- Vite plugins
- Anywhere you want to add magic comments to your source code before running through webpack.
Getting Started
First install magic-comments:
npm install magic-commentsNext, for each file processed provide the following required information for every import() found:
- The absolute filename of the file being processed (
modulePath). - The dynamic import's specifier (
importPath).
Then pass those values to getMagicComment() to generate a magic comment that can be inserted into the corresponding import():
src/file.js
const mod = import('./folder/module.js')tooling
import { resolve } from 'node:path'
import { readFileSync } from 'node:fs'
import { parse } from 'acorn' // Or another parser
import { getMagicComment } from 'magic-comments'
import { traverseForImportSpecifier } from './utils.js'
const filename = resolve(cwd, './src/file.js')
const code = readFileSync(filename)
const ast = parse(code)
const dynamicImports = traverseForImportSpecifiers(ast)
dynamicImports.forEach(({ specifier }) => {
const magicComment = getMagicComment({
modulePath: filename,
importPath: specifier,
// The options are names of webpack magic comments
options: {
webpackChunkName: true,
webpackFetchPriority: (modulePath, importPath) => {
if (importPath.endsWith('important.js')) {
return 'high'
}
}
}
})
console.log(magicComment)
// /* webpackChunkName: "module-important", webpackFetchPriority: "high" */
})getMagicComment()
Generates a webpack magic comment.
getMagicComment(ctx: MagicCommentsContext) => stringThe only parameter is an object with the following properties.
interface MagicCommentsContext {
importPath: string
modulePath: string
match?: 'module' | 'import'
open?: boolean
options?: MagicComments
}MagicCommentsContext
The only required properties are modulePath and importPath:
modulePath
required*
The absolute path to the file with the dynamic imports.
importPath
required*
The specifier from the dynamic import. For example, for import('./specifier.js') the importPath would be ./specifier.js.
open
default false
Whether the returned comment should be surrounded by /* and */, for example, /* comment */ vs comment.
match
default 'module'
Sets how globs are matched, either the module file path, or the import() specifier.
options
An object with properties corresponding to magic comments supported by webpack.
All options can be defined with a CommentFunc or a CommentConfig to support overrides of CommentOptions. Options that support globs use micromatch for pattern matching, where type Glob = string | string[].
webpackChunkNamewebpackFetchPrioritywebpackModewebpackPrefetchwebpackPreloadwebpackIncludewebpackExcludewebpackExportswebpackIgnore
CommentFunc
All options can be defined as a function to dynamically determine their value, or turn on and off.
interface CommentFunc<T> {
(modulePath: string, importPath: string): T
}The exact shape of T is determined by the magic comment the option is associated with, similar to CommentOptions.
CommentConfig
To allow overrides based on module or import paths, all options can be defined with an object having the following interface:
interface CommentConfig<T extends CommentOptions> {
options: T
overrides?: Array<{
files: string | string[]
options: T
}>
}CommentOptions
The exact shape defining options is determined by the magic comment it is associated with, but the interface always extends CommentOptions:
interface CommentOptions {
active?: boolean | ((modulePath: string, importPath: string): boolean)
}The active property turns the option on or off. Each particular magic comment extends this interface in their own way, adding additional properties relevant to their functioning.
For example, webpackChunkName adds a couple additional properties for adjusting the chunk name used:
interface WebpackChunkNameOptions extends CommentOptions {
/**
* Use the basename of the import specifier as the chunk name.
*/
basename?: boolean
/**
* Provide a custom chunk name for all dynamic imports or
* those matching a particular override glob.
*/
name?: string
}You can skip to the overrides example to get a better sense of how this all works.
webpackChunkName
type
boolean
| Glob
| CommentFunc<string | false>
| CommentConfig<WebpackChunkNameOptions>default true
Adds webpackChunkName magic comments. The assumption is that this is the most popular webpack magic comment, so it will be enabled by default when options is empty of falsy.
When using a CommentConfig the following comment options are supported:
{
// Use the basename of the import specifier as the chunk name.
basename?: boolean
// If overrides are not used, this will apply to ALL dynamic imports.
name?: string
}Possible values:
true- AddswebpackChunkNamecomments to all dynamic imports using the derived path from the import specifier in kebab-case as the chunk name. This is the default.false- Disables adding thewebpackChunkNamecomment globally.string | string[]- When the glob(s) match a path from amatchpath, awebpackChunkNamecomment is added using the derived path from the import specifier in kebab-case as the chunk name.(modulePath: string, importPath: string) => string | false- Return a string to be used as the chunk name. Returning a falsy value will skip adding the comment.options.basename:true- Use only the basename from the import specifier as the chunk name. Might result in name collisions. Use in areas where you know the basenames are unique.false- Use the full derived path from the import specifier in kebab-case as the chunk name, same as the default behavior.
options.name:string- Set a fixed string to be used for all dynamic imports, or based on overrides.
options.active:true- Disable the comment.false- Enable the comment.
webpackFetchPriority
type
boolean
| FetchPriority
| CommentFunc<false | FetchPriority>
| CommentConfig<WebpackFetchPriorityOptions>default None
Adds webpackFetchPriority magic comments.
FetchPriority is an enum:
enum FetchPriority {
AUTO = 'auto',
HIGH = 'high',
LOW = 'low'
}When using a CommentConfig the following comment options are supported:
{
fetchPriority?: FetchPriority | CommentFunc<false | FetchPriority>
}Possible values:
false- Disables the comment globally. This is the default behavior.true- AddwebpackFetchPrioritymagic comments to all dynamic imports with the default value of'auto'.string- AddwebpackFetchPrioritymagic comments to all dynamic imports with the provided string value as the priority. If the string is not'high','low', or'auto'the comment will not be added.(modulePath: string, importPath: string) => FetchPriority | false- Return a string to be used as the priority. Returning a falsy value or an unsupported string will not add the comment.options.fetchPriority:FetchPriority- Sets the fetch priority to the provided value when adding the comment.(modulePath: string, importPath: string) => FetchPriority | false- Same as using a function for the value.
options.active:true- Disable the comment.false- Enable the comment.
webpackMode
type
boolean
| Mode
| CommentFunc<false | Mode>
| CommentConfig<WebpackModeOptions>default None
Adds webpackMode magic comments.
Mode is an enum:
enum Mode {
LAZY = 'lazy',
LAZY_ONCE = 'lazy-once',
EAGER = 'eager',
WEAK = 'weak'
}When using a CommentConfig the following comment options are supported:
{
mode?: Mode | CommentFunc<false | Mode>
}Possible values:
false- Disables the comment globally. This is the default behavior.true- AddwebpackModemagic comments to all dynamic imports with the default value of'lazy'.string- AddwebpackModemagic comments to all dynamic imports with the provided string value as the mode. If the string is not'lazy','lazy-once','eager', or'weak'the comment will not be added.(modulePath: string, importPath: string) => Mode | false- Return a string to be used as the mode. Returning a falsy value or an unsupported string will not add the comment.options.mode:Mode- Sets the chunk loading mode to the provided value when adding the comment.(modulePath: string, importPath: string) => Mode | false- Same as using a function for the value.
options.active:true- Disable the comment.false- Enable the comment.
webpackPrefetch
type
boolean
| Glob
| CommentFunc<boolean>
| CommentConfig<CommentOptions>default None
Adds webpackPrefetch magic comments.
When using a CommentConfig this option does not add additional properties to CommentOptions.
Possible values:
false- Disables the comment globally. This is the default behavior.true- AddwebpackPrefetchmagic comments with a value oftrueto all dynamic imports.string | string[]- AddwebpackPrefetchcomment with a value oftruewhen the glob(s) match a path from amatchpath.(modulePath: string, importPath: string) => boolean- Returningfalsewill disable adding the comment, otherwise it will be added.options.active:true- Disable the comment.false- Enable the comment.
webpackPreload
type
boolean
| Glob
| CommentFunc<boolean>
| CommentConfig<CommentOptions>default None
Adds webpackPreload magic comments.
When using a CommentConfig this option does not add additional properties to CommentOptions.
Possible values:
false- Disables the comment globally. This is the default behavior.true- AddwebpackPreloadmagic comments with a value oftrueto all dynamic imports.string | string[]- AddwebpackPreloadcomment with a value oftruewhen the glob(s) match a path from amatchpath.(modulePath: string, importPath: string) => boolean- Returningfalsewill disable adding the comment, otherwise it will be added.options.active:true- Disable the comment.false- Enable the comment.
webpackInclude
type
RegExp
| CommentFunc<RegExp>
| CommentConfig<WebpackIncludeOptions>default None
Adds webpackInclude magic comments.
When using a CommentConfig the following comment options are supported:
{
include?: RegExp | CommentFunc<RegExp>
}Possible values:
RegExp- Adds awebpackIncludecomment to all dynamic imports using the provided regular expression.(modulePath: string, importPath: string) => RegExp- Adds awebpackIncludecomment using the provided regular expression. Returning anything other than a regular expression does not add the comment.options.include:RegExp- Adds awebpackIncludecomment to all dynamic imports, or only those matching a path from thematchpath if using overrides.(modulePath: string, importPath: string) => RegExp- Same as using a function for the value.
options.active:true- Disable the comment.false- Enable the comment.
webpackExclude
type
RegExp
| CommentFunc<RegExp>
| CommentConfig<WebpackExcludeOptions>default None
Adds webpackExclude magic comments.
When using a CommentConfig the following comment options are supported:
{
exclude?: RegExp | CommentFunc<RegExp>
}Possible values:
RegExp- Adds awebpackExcludecomment to all dynamic imports using the provided regular expression.(modulePath: string, importPath: string) => RegExp- Adds awebpackExcludecomment using the provided regular expression. Returning anything other than a regular expression does not add the comment.options.exclude:RegExp- Adds awebpackExcludecomment to all dynamic imports, or only those matching a path from thematchpath if using overrides.(modulePath: string, importPath: string) => RegExp- Same as using a function for the value.
options.active:true- Disable the comment.false- Enable the comment.
webpackExports
type
CommentFunc<string[]> | CommentConfig<WebpackExportsOptions>default None
Adds webpackExports magic comments.
When using a CommentConfig the following comment options are supported:
{
exports?: CommentFunc<string[]>
}Possible values:
(modulePath: string, importPath: string) => string[]- Adds awebpackExportscomment using the strings in the returned array as the export names. Returning anything other than an array will not add the comment.options.exports:(modulePath: string, importPath: string) => string[]- Same as using a function for the value.
options.active:true- Disable the comment.false- Enable the comment.
webpackIgnore
type
boolean
| Glob
| CommentFunc<boolean>
| CommentConfig<CommentOptions>default None
Adds webpackIgnore magic comments.
When using a CommentConfig this option does not add additional properties to CommentOptions.
Possible values:
false- Disables the comment globally. This is the default behavior.true- AddwebpackIgnoremagic comments with a value oftrueto all dynamic imports. Effectively, opt-out of webpack code-splitting for dynamic imports.string | string[]- AddwebpackIgnorecomment with a value oftruewhen the glob(s) match a path from amatchpath.(modulePath: string, importPath: string) => boolean- Returningfalsewill not add the comment, otherwise it will be added.options.active:true- Disable the comment.false- Enable the comment.
Examples
Below are some examples. Consult one of the used by packages, or the unit tests in this repo for something more comprehensive. Particularly, the loader specification from magic-comments-loader.
Multiple
Since options is an object, you can define more than one type of a webpack magic comment.
import { getMagicComment, Mode } from 'magic-comments'
const magicComment = getMagicComment({
modulePath: '/path/file.js',
importPath: './import/module.js',
options: {
webpackMode: Mode.EAGER,
webpackPreload: (modulePath, importPath) => {
return importPath.includes('module')
}
}
})
console.log(magicComment) // /* webpackMode: "eager", webpackPreload: true */Overrides
When using a CommentConfig<T> object, you can override the configuration passed in the options property by defining overrides. It is an array of objects that look like:
Array<{
files: string | string[];
options: T;
}>Where the generic T is related to the magic comment the options are associated with. The files and options properties are both required, where the former is a glob string, or an array thereof, and the latter is the associated magic comment's CommentOptions.
Here's a more complete example of how overrides can be applied.
import { getMagicComment } from 'magic-comments'
const comment = getMagicComment({
modulePath: '/file/module.js',
importPath: './dynamic/import.js',
options: {
webpackChunkName: {
options: { active: false },
overrides: [
{
files: '**/file/*.js',
options: {
active: true,
name: 'override'
}
}
]
}
}
})
console.log(comment) // /* webpackChunkName: "override" */Here, match is set to import, so the glob used in the override will not match.
const comment = getMagicComment({
match: 'import',
modulePath: '/file/module.js',
importPath: './dynamic/import.js',
options: {
webpackChunkName: {
options: { active: true },
overrides: [
{
files: '**/file/*.js',
options: {
basename: true
}
}
]
}
}
})
console.log(comment) // /* webpackChunkName: "dynamic-import" */Changing the glob to **/dynamic/*.js will then match, and the override options will be used.
console.log(comment) // /* webpackChunkName: "import" */Used by
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago