npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

magic-comments

v2.1.12

Published

Node.js tool for adding Webpack magic comments at build time.

Downloads

8,302

Readme

magic-comments

CI codecov NPM version

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' // 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) => string

The 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[].

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 - 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.

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 - 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.

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 - 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) // /* 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