Tooling utility to add configurable webpack magic comments to dynamic import()
expressions at build time.
Useful when working with:
Getting Started
First install magic-comments
:
npm install magic-comments
Next, 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'
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,
options: {
webpackChunkName: true,
webpackFetchPriority: (modulePath, importPath) => {
if (importPath.endsWith('important.js')) {
return 'high'
}
}
}
})
console.log(magicComment)
})
Generates a webpack magic comment.
getMagicComment(ctx: MagicCommentsContext) => string
The only parameter is an object with the following properties.
interface MagicCommentsContext {
importPath: string
modulePath: string
match?: 'module' | 'import'
open?: boolean
options?: MagicComments
}
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[]
.
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
.
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
}>
}
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 {
basename?: boolean
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:
{
basename?: boolean
name?: string
}
Possible values:
true
- Adds webpackChunkName
comments 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 the webpackChunkName
comment globally.string | string[]
- When the glob(s) match a path from a match
path, a webpackChunkName
comment 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
- Add webpackFetchPriority
magic comments to all dynamic imports with the default value of 'auto'
.string
- Add webpackFetchPriority
magic 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
- Add webpackMode
magic comments to all dynamic imports with the default value of 'lazy'
.string
- Add webpackMode
magic 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.
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
- Add webpackPrefetch
magic comments with a value of true
to all dynamic imports.string | string[]
- Add webpackPrefetch
comment with a value of true
when the glob(s) match a path from a match
path.(modulePath: string, importPath: string) => boolean
- Returning false
will disable adding the comment, otherwise it will be added.options.active
:
true
- Disable the comment.false
- Enable the comment.
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
- Add webpackPreload
magic comments with a value of true
to all dynamic imports.string | string[]
- Add webpackPreload
comment with a value of true
when the glob(s) match a path from a match
path.(modulePath: string, importPath: string) => boolean
- Returning false
will 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 a webpackInclude
comment to all dynamic imports using the provided regular expression.(modulePath: string, importPath: string) => RegExp
- Adds a webpackInclude
comment using the provided regular expression. Returning anything other than a regular expression does not add the comment.options.include
:
RegExp
- Adds a webpackInclude
comment to all dynamic imports, or only those matching a path from the match
path 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 a webpackExclude
comment to all dynamic imports using the provided regular expression.(modulePath: string, importPath: string) => RegExp
- Adds a webpackExclude
comment using the provided regular expression. Returning anything other than a regular expression does not add the comment.options.exclude
:
RegExp
- Adds a webpackExclude
comment to all dynamic imports, or only those matching a path from the match
path 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 a webpackExports
comment 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
- Add webpackIgnore
magic comments with a value of true
to all dynamic imports. Effectively, opt-out of webpack code-splitting for dynamic imports.string | string[]
- Add webpackIgnore
comment with a value of true
when the glob(s) match a path from a match
path.(modulePath: string, importPath: string) => boolean
- Returning false
will 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)
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)
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)
Changing the glob to **/dynamic/*.js
will then match, and the override options will be used.
console.log(comment)
Used by